fork的整体流程
- 当用户空间的代码调用fork的时候,产生0x80号中断。(此时用户空间的程序阻塞,保存用户空间的上下文信息,进入内核程序)
- system_call处理0x80号中断程序,获取用户空间通过寄存器带来的系统调用号
- 内核程序通过系统调用号,调用对应的系统调用函数sys_fork()
- sys_fork()底层通过do_fork()实现,调用内核代码do_fork()
- 首先do_fork()向系统申请一个pid,标识新产生的子进程
(pid 的最大值32768 开始申请时在当前进程的最大的pid的基础上加一,
当超过最大值的时候,从开始设置的起始位置找第一个可以使用的pid)
- 分配进程的pcb块
slab 分配器会为内核中经常用到的一些数据结构构造专用的高速缓存区,使得提高内存的分配效率
alloc_task_struct(); 宏可以申请一个pcb块大小的内存块
- 分配内核栈 thread_info
在task_struct结构体中,有一个指针指向一块内核栈的空间,这块空间中存储着内核进程的调用信息
- 复制父进程的pcb块与内核栈的信息
*ti = *orig->thread_info; //将父进程的进程的描述信息复制到新申请的thread_info结构体中
*tsk = *orig; //父进程的pcb块复制到新申请的task_struct结构体中,该复制属于浅拷贝,所以要有前一句的复制过程
tsk->thread_info = ti; //新申请的pcb块中的thread_info 指向新申请的内核栈
ti->task = tsk; //新申请的内核栈中的task指针指向新申请的pcb块
- 保存新进程的pid
在 task_struct 中的pid的值设置为刚刚申请的pid的值
- 复制父进程的各种信息
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p)))
goto bad_fork_cleanup_sighand;
if