在终端上运行一个程序main
首先,bash作为一个父进程,运行起来的程序是bash的子进程。
即,当我们在终端输入./main
运行一个可执行程序,bash会调用一个名为fork()
系统调用,然后陷入内核,CPU执行内核态的sys_fork()
函数,而sys_fork()
函数中调用了do_fork()
,其中do_fork()
会创建一个task_struct,然后将该task_struct加入到内核维护的进程的双向链表中。
然后,fork()调用结束,子进程诞生了,但是fork()做的是复制父进程,实际上我们当然不是期望父进程和子进程执行的指令和数据一模一样了,我们期望的是子进程执行自己的数据和指令,即main可执行程序的数据和指令。
然后,子进程调用exec()
族函数,继续陷入内核,执行sys_execve(),调用load_elf_binary()将main的存放在磁盘的数据和指令加载到内存中。
最终,真正意义上main进程诞生了,此时的子进程真正执行的是main的数据和指令。
Linux系统调用 sys_fork()过程
-
用户态调用
fork(),
出发系统调用,CPU转向内核,执行内核态的代码; -
通过查询系统调用表,找到内核的
sys_fork()
函数,进行调用; -
而
sys_fork()
实际上调用的是do_fork()
,该函数做的事情较多:从slab分配器中分配一个task_struct实例
分配创建内核栈,并拷贝父进程内核栈,设置thread_info,特别的,父进程在陷入内核前,保存了的上下文也会被子进程进行拷贝,也就