作者:严哲璟

原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

 

fork属于系统调用,而此系统调用创建一个子进程,由于其为系统调用,所以基本的用户态到内核态的切换,以及中断的处理,都与普通的系统调用一样,分为3个主要过程

 

1.调用fork这个API,然后软中断,进入内核态,堆栈切换到内核堆栈,保存用户态sp,cs:eip;

2.执行system_call,然后SAVE_ALL,根据eax寻找系统调用表中对应的中断服务程序,这里是sys_clone

3.执行sys_clone

与普通的系统调用不一样的是,sys_clone需要执行do_fork,产生一个新的进程

 

 

下面是相关的流程图:

do_fork()

{

copy_process(....){                        //复制进程相关信息

             dup_tusk_struct(current){                      //复制PCB

                                   alloc_task_struct_node()           //相关PCB指针赋值

                                  arch_dup_task_struct()

                                  alloc_thread_info_node()            //创建8KB大小的内存空间放置stack以及thread_info,返回stack起始地址ti

                                  setup_thread_info()                   //初始化thread_info

                                                        }

                对新进程P的其他信息修改,因为所有信息都是复制的所以必须修改

               retval=copy_thread(P)                                   //设置新进程的起始地址,新进程的内核堆栈            

                               }

}

下面详细说进程创建以及堆栈的切换(此时在执行fork的父进程中)

copy_thread()               

   p->thread.ip=ret_from_fork          //新进程的入口地址

   p->thread.sp=childregs                  //将新进程的内核堆栈指向

   childregs=current_pt_regs           //父进程内核堆栈数据被拷贝

  childregs->sp=sp;                            //将拷贝的sp设置为传入的sp,即子进程的sp.

 

此时子进程的内核堆栈如下:

       stack(ti),childregs            :  current_pt_regs   (简单复制)

                                                     ax=0;                   修改 ax

                                                     sp=sp;                  修改sp

执行jmp_syscall_exit跳转至syscall_exit,然后restore_all将子进程的内核堆栈所有信息弹出,由于压栈的sp被修改成do_fork传入参数的sp,因此返回到子进程的用户堆栈,返回时同样需要检测调度信号