本文为我学习linux内核的总结。
唐建 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000。
1、概述
前面分析了系统调用的原理和过程。本文分析fork这个系统调用,重点分析进程的创建主要动作和流程。
2、fork 系统调用的主要动作
如上图,fork、vfork、clone,最终都是调用do_fork。不过他们之间的差异也可以在do_fork及后续代码中看到,不过这里不讲述。
do_fork 的主要逻辑为调用copy_process,又函数名就可以看到进程的生成逻辑就在copy_process.
copy_process主要调用了:
dup_task_struct()——主要是创建和复制进程
copy_fs ——拷贝文件句柄之类的
copy_files ——拷贝文件句柄之类的
copy_mm ——拷贝虚拟内存
copy_io ——拷贝io
copy_thread ——拷贝thead_info信息
下面我们着重分析 dup_task_struct 和 copy_thread 。
2.1 、dup_task_struct:申请内存,拷贝task_struct和thread_info
如上图所示,前面两个调用为申请task_struct、thread_info 的内存,arch_dup_task_struct就是将父进程的
task_struct 拷贝给子进程。
setup_thread_stack:这个函数是将父进程的堆栈thread_info拷贝给子进程。
2.2、copy_thread :准备栈信息
如上图信息childregs保存父进程的堆栈信息,然后赋值给子进程堆栈。右图pt_regs为系统调用开始通过save_all保存的信息。
同时可以看到如果是内核创建线程走的不同的分支,不同的处理。
上面可以看到childregs->ax=0,就是返回值,也就是子进程返回的pid为0的原因。
p->thread.ip = (unsigned long) ret_from_fork,前面设置了sp栈顶,这里再设置ip。
ret_from_fork->syscall_exit。这里就很熟悉了,就是我们上一篇博客中的系统调用完成后的执行过程,ip为子进程执行的起点,所以可以知道
子进程开始调度时是从ret_from_fork开始执行的。
2.3、父进程
如上图所示,子进程完全准备完毕,获取子进程id作用父进程的返回值,同时唤醒子进程。这样两个进程都可以运行了 。
3、debug
下面我们来跟踪fork内核执行过程,我们断住主要函数do_fork、copy_process、dup_task_struct、copy_thread 、
ret_from_fork
4、总结
fork创建一个进程,实际上就将父进程的进程信息拷贝给子进程,子进程的起点就是父进程系统调用结束的位置。只有子进程信息完全准备好后,父进程要返回前才唤醒子进程。
’