Linux内核中进程的初始化

分析start_kernel时应该会注意到Linux内核0号进程的初始化,见init/main.c。

Linux内核中进程的初始化_第1张图片

set_task_stack_end_magic(&init_task); 其中,init_task为第一个进程(0号进程)的进程描述符结构体变量,它的初始化是通过硬编码方式固定下来的。除此之外,所有其他进程的初始化都是通过do_fork复制父进程的方式初始化的。

1号和2号进程的创建是start_kernel初始化到最后由rest_ init(如下图所示)通过kernel_thread创建了两个内核线程:一个是kernel_init,最终把用户态的进程init给启动起来,是所有用户进程的祖先;另一个是kthreadd内核线程,kthreadd内核线程是所有内核线程的祖先,负责管理所有内核线程。

Linux内核中进程的初始化_第2张图片

在start_kernel()函数最后有arch_call_rest_init()函数,进入该函数我们就可以发现rest_init()函数了 

Linux内核中进程的初始化_第3张图片

再进入rest_init()函数,我们可以看到通过kernel_thread创建了两个内核线程:

一个是kernel_init,最终把用户态的进程init给启动起来,是所有用户进程的祖先

另一个是kthreadd内核线程,kthreadd内核线程是所有内核线程的祖先,负责管理所有内核线程。

 Linux内核中进程的初始化_第4张图片

kernel_thread创建进程的过程和shell命令行下启动一个进程时fork创建进程的过程在本质上是一样的,都要通过复制父进程来创建一个子进程。

在系统启动时,除了前述0号进程的初始化过程是我们手工编码创建的之外,1号init进程的创建实际上是复制0号进程,根据1号进程的需要修改了进程pid等,然后再加载一个init可执行程序。同样地,2号kthreadd内核线程也是通过复制0号进程来创建的。通过如下kernel/fork.c中kernel_thread代码可以看到1号进程和2号进程最终都是通过_do_fork创建的,用户态通过系统调用fork创建一个进程最终也是通过_do_fork来完成的。

Linux内核中进程的初始化_第5张图片

我们进入kernel_thread函数中去一探究竟,follow me!!!

Linux内核中进程的初始化_第6张图片

 果然,在kernel_thread函数中发现了_do_fork(),我们再去_do_fork()里面去看看,走!

Linux内核中进程的初始化_第7张图片

Linux内核中进程的初始化_第8张图片

_do_fork具体进程的创建大概就是调用copy_process()把当前进程的描述符等相关进程资源复制一份,从而产生一个子进程,并根据子进程的需要对复制的进程描述符做一些修改,然后调用wake_up_new_task()把创建好的子进程放入运行队列(操作系统原理中的就绪队列)。在进程调度时,新创建的子进程处于就绪状态有机会被调度执行。


以上内容为中科大软件学院《Linux操作系统分析》课后总结,感谢孟宁老师的倾心教授,老师讲的太好啦(^_^)

参考资料:《庖丁解牛Linux内核分析》    孟宁  编著

你可能感兴趣的:(Linux,linux,运维,服务器)