• strace跟踪多进程与内核的交互


    1、ptrace的说明

    ptrace原型:

     #include <sys/ptrace.h>
     long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

    ptrace跟踪多进程时,需要通过ptrace(PTRACE_SETOPTIONS, tcp->pid, NULL, ptrace_setoptions),即PTRACE_SETOPTIONS设置ptrace的相关属性,也就是将data指向的值设定为ptrace的选项,可选的有:

    (1)PTRACE_O_TRACEFORK:被跟踪进程在下次调用fork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始运行就收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到(即:ptrace(PTRACE_GETEVENTMSG, child_waited, 0, &new_pid)。

    (2) PTRACE_O_TRACEVFORK:被跟踪进程在下次调用vfork()时停止执行,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到。

    (3) PTRACE_O_TRACECLONE:被跟踪进程在下一次调用clone()时将其停止,并自动跟踪新产生的进程,新产生的进程刚开始收到SIGSTOP信号。其新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到。

    (4)PTRACE_O_TRACEEXEC:被跟踪进程在下一次调用exec()函数时使其停止,PTRACE_GETEVENTMSG可用于获取发生execve的old_pid(即:ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid))。

    (5)PTRACE_O_EXITKILL:当跟踪进程退出时,向所有被跟踪进程发送SIGKILL信号将其退出,这个参数可以防止被跟踪进程脱离跟踪进程的控制。

    (6)PTRACE_O_TRACEEXIT:被跟踪进程在退出是停止其执行,被跟踪进程的退出状态可通过PTRACE_GETEVENTMSG获得。

    2、strace与linux内核的交互

    strace-4.8,linux-3.10.1

    以下就针对上面加粗部分,分析strace对新进程的处理以及linux内核对这部分功能的支持

    fork、vfork、clone系统调用最后都会调用do_fork,只是传递的参数不同。

    SYSCALL_DEFINE0(fork)
    {
    #ifdef CONFIG_MMU
        return do_fork(SIGCHLD, 0, 0, NULL, NULL);
    #else
        /* can not support in nommu mode */
        return(-EINVAL);
    #endif
    }
    SYSCALL_DEFINE0(vfork)
    {
        return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, 
                0, NULL, NULL);
    }
    SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
             int __user *, parent_tidptr,
             int __user *, child_tidptr,
             int, tls_val)
    {
        return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
    }

    在do_fork中,此处截取和ptrace相关的部分:

    long do_fork(unsigned long clone_flags,
              unsigned long stack_start,
              unsigned long stack_size,
              int __user *parent_tidptr,
              int __user *child_tidptr)
    {
        ...
    
        /*
         * Determine whether and which event to report to ptracer.  When
         * called from kernel_thread or CLONE_UNTRACED is explicitly
         * requested, no event is reported; otherwise, report if the event
         * for the type of forking is enabled.
         */
        if (!(clone_flags & CLONE_UNTRACED)) {
            if (clone_flags & CLONE_VFORK)//根据cloneflags判断陷入strace的event
                trace = PTRACE_EVENT_VFORK;
            else if ((clone_flags & CSIGNAL) != SIGCHLD)
                trace = PTRACE_EVENT_CLONE;
            else
                trace = PTRACE_EVENT_FORK;
    
            if (likely(!ptrace_event_enabled(current, trace)))//if(value),判断task->ptrace没设置该ptrace event则将trace置0
                trace = 0;
        }
    
        p = copy_process(clone_flags, stack_start, stack_size,
                 child_tidptr, NULL, trace);//trace是ptrace中相应的setoptions(fork等陷入
        /*
         * Do this prior waking up the new thread - the thread pointer
         * might get invalid after that point, if the thread exits quickly.
         */
        if (!IS_ERR(p)) {
            struct completion vfork;
    
            trace_sched_process_fork(current, p);
    
            nr = task_pid_vnr(p);
    
            ...
    
            wake_up_new_task(p);//将进程唤醒并添加到就绪队列中等待调度
    
            /* forking complete and child started to run, tell ptracer*/
            if (unlikely(trace))//不进入执行可能性大,但是如果strace设置了该trace事件陷入,则进入执行
                ptrace_event(trace, nr);//此处nr是子进程pid,为了让strace可以通过PTRACE_GETEVENTMSG获得该子进程pid
    
            ...
        } else {
            nr = PTR_ERR(p);
        }
        return nr;
    }  

    在copy_process中,有

    if (likely(p->pid)) {
            ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);//设置子进程被ptrace
    ...
        }

    最重要的部分,对应“并自动跟踪新产生的进程,新产生的进程刚开始运行就收到SIGSTOP信号”:

    /**
     * ptrace_init_task - initialize ptrace state for a new child
     * @child:        new child task
     * @ptrace:        true if child should be ptrace'd by parent's tracer
     *
     * This is called immediately after adding @child to its parent's children
     * list.  @ptrace is false in the normal case, and true to ptrace @child.
     *
     * Called with current's siglock and write_lock_irq(&tasklist_lock) held.
     */
    static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
    {//设置子进程ptrace,如果子进程应该被追踪父进程的strace追踪的话,此ptrace值为1
        INIT_LIST_HEAD(&child->ptrace_entry);
        INIT_LIST_HEAD(&child->ptraced);
    #ifdef CONFIG_HAVE_HW_BREAKPOINT
        atomic_set(&child->ptrace_bp_refcnt, 1);
    #endif
        child->jobctl = 0;
        child->ptrace = 0;
        child->parent = child->real_parent;
    
        if (unlikely(ptrace) && current->ptrace) {
            //父进程被trace的标志给子进程,因为有了相应的ptrace标志(PT_TRACED),导致内核在投递信号时,先检查task_struct的ptrace字段的该标志,若有,则设置进程状态TASK_STOPPED,以中断执行子进程,而后notify_parent和CHLD信号用于通知tracer,tracer可设置PTRACE_SYSCALL使该进程之后发生和完成syscall也会陷入到strace,即没通过attach做这个跟踪,而是内核直接设置了标志,更加直接,不用用户层的系统调用ptrace
            child->ptrace = current->ptrace;
            __ptrace_link(child, current->parent);
    
            if (child->ptrace & PT_SEIZED)
                task_set_jobctl_pending(child, JOBCTL_TRAP_STOP);
            else
                sigaddset(&child->pending.signal, SIGSTOP);//给子进程child设置SIGSTOP信号
    
            set_tsk_thread_flag(child, TIF_SIGPENDING);//给子进程child设置TIF_SIGPENDING标志,说明该进程有延迟的信号(SIGSTOP)要等待处理。该子进程被调度时,在进程返回用户空间前,会调用do_notify_resume()处理该进程的信号
        }
    }

    此处就设置了子进程有未处理的SIGSTOP信号,导致该子进程被调度时,收到SIGSTOP信号,又因为该子进程被strace跟踪,所以该子进程因SIGSTOP陷入到strace中,strace就在第一时刻捕获到该子进程,strace会发现已被追踪链表中没有该子进程pid,则为该子进程新建file和tcb,并设置相应option。

    进程的do_fork中在copy_process后会wake_up_new_process把子进程放到运行队列中,然后执行到ptrace_event,会导致父进程SIGTRAP陷入到strace中。此处对应“被跟踪进程在下次调用fork()时停止执行,新产生的进程的pid可以通过PTRACE_GETEVENTMSG得到”:

    /**
     * ptrace_event - possibly stop for a ptrace event notification
     * @event:    %PTRACE_EVENT_* value to report
     * @message:    value for %PTRACE_GETEVENTMSG to return
     *
     * Check whether @event is enabled and, if so, report @event and @message
     * to the ptrace parent.
     *
     * Called without locks.
     */
    static inline void ptrace_event(int event, unsigned long message)
    {
        if (unlikely(ptrace_event_enabled(current, event))) {
            current->ptrace_message = message;//让PTRACE_GETEVENTMSG可获得fork等的子进程pid(strace没用这个,strace是通过wait到新pid就是新进程,但获取发生execve的old_pid用
            ptrace_notify((event << 8) | SIGTRAP);//如果ptrace设置了该event事件选项,则SIGTRAP陷入strace程序(可以通过geteventmsg得到old_pid
        } else if (event == PTRACE_EVENT_EXEC) {//发生execve则SIGTRAP陷入strace
            /* legacy EXEC report via SIGTRAP */
            if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) == PT_PTRACED)
                send_sig(SIGTRAP, current, 0);
        }
    }

    补充:

    用户态:ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid)

    走到内核是:SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,unsigned long, data)->arch_ptrace->ptrace_request->ret = put_user(child->ptrace_message, datalp);也就是这样把message传给用户态来获取。

  • 相关阅读:
    You are not late! You are not early!
    在同一个服务器(同一个IP)为不同域名绑定的免费SSL证书
    Vue.js Is Good, but Is It Better Than Angular or React?
    It was not possible to find any compatible framework version
    VS增加插件 Supercharger破解教程
    Git使用ssh key
    Disconnected: No supported authentication methods available (server sent: publickey)
    VS 2013打开.edmx文件时报类型转换异常
    asp.net MVC4 框架揭秘 读书笔记系列3
    asp.net MVC4 框架揭秘 读书笔记系列2
  • 原文地址:https://www.cnblogs.com/beixiaobei/p/9559875.html
Copyright © 2020-2023  润新知