分析Linux内核创建一个新进程的过程
周恺祺+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、实验要求
- 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235;
分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构; - 使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证您对Linux系统创建一个新进程的理解,推荐在实验楼Linux虚拟机环境下完成实验。
- 特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
- 根据本周所学知识分析fork函数对应的系统调用处理过程,撰写一篇署名博客,并在博客文章中注明“真实姓名(与最后申请证书的姓名务必一致) + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”
二、实验步骤及现象
- 打开实验楼虚拟机实验环境,进入LinuxKernel文件夹,打开menu,修改其中test.c文件。
需要修改的代码如下所示(即加入fork的系统调用函数,并修改主函数):
启动内核,检测fork()系统调用成果
现象:
输入以下代码用gdb进行跟踪调试系统调用过程
Qemu –kernel linux-3.18.6/arch/x86/boot/bzImage –initrd rootfs.img –s –S
gdb file ../linux-3.18.6/vmlinux
Target remote:1234
接下来,可以使用
b sys_clone
b do_fork
b dup_task_struct
b copy_process
等语句进行程序运行的断点设置,并使用 “n”逐条运行,使用“c”持续运行直至下一个断点。以下是几个断点及逐条执行跟踪的举例:
由此可见,fork()函数在这三个地方均已系统调用的方式访问了内核,总体上来说,fork()函数系统调用过程还是一个相对比较复杂的过程,通过单步调试和设置断点的方式很难一一追踪到,了解其奥妙。而且在设置断点过程中,追踪到 jmp_system_exit之后就追踪不到了
三、实验分析
进程的创建相关(有关内容参考网络):
- Linux中创建进程一共三个函数:
(1)fork:创建子进程
(2)vfork:与fork类似,但是父子进程共享地址空间,而且子进程先于父进程运行
(3)clone:主要用于创建线程
注:Linux线程是通过模拟进程实现的, - 通过跟踪实验发现,以上fork、vfork、clone均是通过do_fork函数实现的:
(1)调用copy_process,当前进程复制一份出来为子进程,并且为子进程设置相应的上下文信息
(2)初始化vfork 完成处理信息
(3)调用wake_up_new_task,将子进程放入调度器的队列中,此时的子进程就可以被调度进程选中,得以运行
(4)如果是vfork调用,需要阻塞父进程,直到子进程执行exec
注:如果没有vfork,进程的创建过程基本都在copy_process中 - 通过跟踪以及查阅相关资料可知,Linux中所有的进程都是基于复制的方式,然后对子进程做一些特殊的处理。线程又是一种特殊的进程。
- fork()函数创建一个新锦成是通过以下一系列函数实现的
fork() ->system_clone(0 -> do_fork(0 -> dup_task_struct() -> copy_process() -> copy_thread() -> ret_from_fork
关于init进程创建(通过课程所得)
道生一: start_kernel -> cpu_idle
一生二: kernel_init kthread
二生三:0、1、2 号进程
三生万物:1号进程是所有用户态进程的祖先;2号进程是所有内核线程的祖先;而0号进程是所有进程的祖先