作者:严哲璟
原创作品转载请注明出处 + 《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,因此返回到子进程的用户堆栈,返回时同样需要检测调度信号