第八章:fork函数的执行机制
程序员文章站
2024-01-31 10:44:16
...
对于fork函数,我们已知以下几点:
- 进程在linux中成树状结构, init为根节点,其它进程均有父进程,某进程的父进程就是启动这个进程的进程,这个进程叫做父进程的子进程。
- fork函数的作用就是创建一个子进程,子进程复制父进程的代码段,数据段,BSS段,堆,栈等所有用户空间的信息,在内核中操作系统为其重新申请了一个PCB,并且使用父进程的PCB来初始化,除了PID等特殊信息外,几乎所有的信息都是一样的 。
- 父子进程在创建了子进程后互相不关联,以独立身份抢占CPU资源,具体谁先执行有调度算法决定,用户空间没有办法干预。
- 子进程开始执行代码的位置是fork函数执行后的代码处,之前的都是继承父进程的。
接下来我们通过几个例子来阐述一下fork的执行:
首先,通过编译生成可执行文件,然后开始运行:
我们先选择这样的一段函数进行运行。
void fork5()
{
printf("L0\n");
if (fork() == 0) {
printf("L1\n");
if (fork() == 0) {
printf("L2\n");
}
}
printf("Bye\n");
}
这是它的执行结果。
在这个函数中,首先输出字符“L0”;
接着,父进程创建了一个子进程,并且子进程输出字符“L1”;
再然后,子进程再次创建了一个子进程,并且在这第二个子进程中输出字符“L2”;
最后,三个进程都输出字符“Bye”。
图示如下:
我们再来看第二段例子,它的代码是这样的:
void fork13()
{
pid_t pid[N];
int i;
int child_status;
signal(SIGINT, int_handler);
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0) {
/* Child: Infinite Loop */
while(1)
;
}
for (i = 0; i < N; i++) {
printf("Killing process %d\n", pid[i]);
kill(pid[i], SIGINT);
}
for (i = 0; i < N; i++) {
pid_t wpid = wait(&child_status);
if (WIFEXITED(child_status))
printf("Child %d terminated with exit status %d\n",
wpid, WEXITSTATUS(child_status));
else
printf("Child %d terminated abnormally\n", wpid);
}
}
再上一个代码中,我们熟悉了fork函数创建子进程以及子进程运行的机制,接下来我们会通过这一段代码和它的运行结果来了解一下程序的信号机制。
它的运行结果是这样的:
让我们来分析一下它产生这种结果的原因:
首先,程序会通过一段信号处理程序(signal)来获取sigint这一信号。当信号处理程序得到这一个信号时,它会打印进程号以及输出获取的信号的编号,此处信号的编号为2,即sigint的编号为2。
获取信号之后,程序会直接结束该子进程。
再然后,程序创建了若干个陷入死循环的子进程用于等待信号。
接着,程序给每一个子进程发出一个sigint信号,同时打印"Killing process"以及子进程的编号。
最后,程序通过wait函数等待子进程结束,并打印出子进程结束时的状态。