作者:沈鑫 原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000首先我们创建一个fork程序,并且观察他的运行情况。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
/* fork another process */
pid = fork();
if (pid < 0)
{
/* error occurred */
fprintf(stderr,"Fork Failed!");
exit(-1);
}
else if (pid == 0)
{
/* child process */
printf("This is Child Process!\n");
}
else
{
/* parent process */
printf("This is Parent Process!\n");
/* parent will wait for the child to complete*/
wait(NULL);
printf("Child Complete!\n");
}
}
他的运行结果如下图:
上面的程序大家应该都能看懂。需要注意的是pid = fork();
执行过后返回值,如果pid为0,则代表子进程,如果pid不为0且不是负数,则代表父进程。运行结果图中返回的结果是子进程和父进程都运行结束之后的结果,并不是一个进程同时走到了 if 语句中的两个条件。
子进程在复制父进程的内核堆栈的时候只是复制了其中的一部分内容,并不是完全的copy。
ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
这样就保证了子进程在返回时执行自己应该执行的位置,而不是从main()重新开始执行。如果从main()重新执行的话,fork()就永无休止了。如下代码能够帮助我们进一步了解子进程返回后的执行位置:
*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
可见,sp和ip在子进程中都换成了自己的值,而不是父进程的值。