结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程

一 实验目的

1.以fork和execve系统调用为例分析中断上下文的切换

2.分析execve系统调用中断上下文的特殊之处

3.分析fork子进程启动执行时进程上下文的特殊之处

4.以系统调用作为特殊的中断,结合中断上下文切换和进程上下文切换分析Linux系统的一般执行过程

 

二 实验过程

1.fork函数调用

fork系统调用用于创建一个新进程,称为子进程,它与父进程并发运行。创建新的子进程后,两个进程都将执行fork()系统调用之后的下一条指令。子进程使用相同的PC,相同的CPU寄存器,相同的打开文件,这些文件在父进程中使用。fork()有着不同的返回值,负值:创建子进程失败,零:返回到新创建的子进程,正值:返回给父亲或调用者。该值包含新创建子进程的进程ID。
Linux下用于创建进程的API有三个fork,vfork和clone,这三个函数分别是通过系统调用sys_fork,sys_vfork以及sys_clone实现的。而且这三个系统调用,都是通过do_fork来实现的,只是传入了不同的参数。
以下是do_fork函数的原型
long do_fork(unsigned long clone_flags,
    unsigned long stack_start,
    unsigned long stack_size,
    int __user *parent_tidptr,
    int __user *child_tidptr)
{
    struct task_struct *p;
    int trace = 0;
    long nr;

    /*
    * 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)
            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)))
            trace = 0;
    }
    
    p = copy_process(clone_flags, stack_start, stack_size,
        child_tidptr, NULL, trace);
    /*
    * 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;
        struct pid *pid;
    
        trace_sched_process_fork(current, p);
    
        pid = get_task_pid(p, PIDTYPE_PID);
        nr = pid_vnr(pid);
    
        if (clone_flags & CLONE_PARENT_SETTID)
            put_user(nr, parent_tidptr);
    
        if (clone_flags & CLONE_VFORK) {
            p->vfork_done = &vfork;
            init_completion(&vfork);
            get_task_struct(p);
        }
    
        wake_up_new_task(p);
    
        /* forking complete and child started to run, tell ptracer */
        if (unlikely(trace))
            ptrace_event_pid(trace, pid);
    
        if (clone_flags & CLONE_VFORK) {
            if (!wait_for_vfork_done(p, &vfork))
                ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
        }
    
        put_pid(pid);
    } else {
        nr = PTR_ERR(p);
    }
    return nr;

}

  父进程通过函数中的copy_process函数来实现对子进程的创建。

 

2.execve函数调用

execve和其他系统调?不同之处是加载完新的可执?程序之后已经覆盖了原来?进程的上下?环境。execve系统调用的作用是运行另外一个指定的程序。它会把新程序加载到当前进程的内存空间内,当前的进程会被丢弃,它的堆、栈和所有的段数据都会被新进程相应的部分代替,然后会从新程序的初始化代码和 main 函数开始运行。同时,进程的 ID 将保持不变。

execve的调用逻辑是

sys_execve() -> do_execve() -> do_execveat_common() -> do_execve_file -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()

 

3.linux系统的一般执行过程

3.1一般情况

正在运行的用户态进程X切换到运行用户态进程Y的过程

    (1)正在运行的用户态进程X

    (2)发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).

    (3)SAVE_ALL //保存现场

    (4)中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换

    (5)标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)

    (6)restore_all //恢复现场

    (7)iret - pop cs:eip/ss:esp/eflags from kernel stack

    (8)继续运行用户态进程Y

3.2特殊情况

    (1)通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;

    (2)内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;

    (3)创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;

    (4)加载一个新的可执行程序后返回到用户态的情况,如execve;

 

三 总结

1、Linux进程调度是基于分时和优先级的。

2、Linux中,内核线程是只有内核态没有用户态的特殊进程。

3、内核可以看作各种中断处理过程和内核线程的集合。

4、Linux系统的一般执行过程 可以抽象成正在运行的用户态进程X切换到运行用户态进程Y的过程。

5、Linux中,内核线程可以主动调度,主动调度时不需要中断上下文的切换。

6、Linux内核调用schedule()函数进行调度,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换。

 





结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程

上一篇:蓝天采集器v2.3.1 getshell


下一篇:windows php安裝xdebug