关于fork函数相关知识

void fork0()
{
if (fork() == 0) {
printf(“Hello from child\n”);
}
else {
printf(“Hello from parent\n”);
}
}
fork1()
{
int x = 1;
pid_t pid = fork();
if (pid == 0) {
printf(“Child has x = %d\n”, ++x);
}
else {
printf(“Parent has x = %d\n”, --x);
}
printf(“Bye from process %d with x = %d\n”, getpid(), x);
}
fork2()
{
printf(“L0\n”);
fork();
printf(“L1\n”);
fork();
printf(“Bye\n”);
}
fork3()
{
printf(“L0\n”);
fork();
printf(“L1\n”);
fork();
printf(“L2\n”);
fork();
printf(“Bye\n”);

fork4()
{
printf(“L0\n”);
if (fork() != 0) {
printf(“L1\n”);
if (fork() != 0) {
printf(“L2\n”);
}
}
printf(“Bye\n”);
}
fork5()
{
printf(“L0\n”);
if (fork() == 0) {
printf(“L1\n”);
if (fork() == 0) {
printf(“L2\n”);
}
}
printf(“Bye\n”);
}

void cleanup(void) {
printf(“Cleaning up\n”);
fork6()
{
atexit(cleanup);
fork();
exit(0);
}
fork7()
{
if (fork() == 0) {
/* Child /
printf(“Terminating Child, PID = %d\n”, getpid());
exit(0);
} else {
printf(“Running Parent, PID = %d\n”, getpid());
while (1)
; /
Infinite loop /
}
}
void fork8()
{
if (fork() == 0) {
/
Child /
printf(“Running Child, PID = %d\n”,
getpid());
while (1)
; /
Infinite loop */
} else {
printf(“Terminating Parent, PID = %d\n”,
getpid());
exit(0);
}
}
fork9()
{
int child_status;

if (fork() == 0) {
printf(“HC: hello from child\n”);
exit(0);
} else {
printf(“HP: hello from parent\n”);
wait(&child_status);
printf(“CT: child has terminated\n”);
}
printf(“Bye\n”);
}
void fork10()
{
pid_t pid[N];
int i, child_status;

for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0) {
exit(100+i); /* Child /
}
for (i = 0; i < N; i++) { /
Parent */
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 terminate abnormally\n”, wpid);
}
}
void fork11()
{
pid_t pid[N];
int i;
int child_status;

for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0)
exit(100+i); /* Child */
for (i = N-1; i >= 0; i–) {
pid_t wpid = waitpid(pid[i], &child_status, 0);
if (WIFEXITED(child_status))
printf(“Child %d terminated with exit status %d\n”,
wpid, WEXITSTATUS(child_status));
else
printf(“Child %d terminate abnormally\n”, wpid);
}
}
void fork12()
{
pid_t pid[N];
int i;
int child_status;

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);
}
}
void int_handler(int sig)
{
printf(“Process %d received signal %d\n”, getpid(), sig); /* Unsafe */
exit(0);
}

/*

  • fork13 - Simple signal handler example
    */
    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);
    }
    }

int ccount = 0;
void child_handler(int sig)
{
int child_status;
pid_t pid = wait(&child_status);
ccount–;
printf(“Received SIGCHLD signal %d for process %d\n”, sig, pid); /* Unsafe /
fflush(stdout); /
Unsafe */
}

/*

  • fork14 - Signal funkiness: Pending signals are not queued
    */
    void fork14()
    {
    pid_t pid[N];
    int i;
    ccount = N;
    signal(SIGCHLD, child_handler);

    for (i = 0; i < N; i++) {
    if ((pid[i] = fork()) == 0) {
    sleep(1);
    exit(0); /* Child: Exit /
    }
    }
    while (ccount > 0)
    ;
    }
    void child_handler2(int sig)
    {
    int child_status;
    pid_t pid;
    while ((pid = wait(&child_status)) > 0) {
    ccount–;
    printf(“Received signal %d from process %d\n”, sig, pid); /
    Unsafe /
    fflush(stdout); /
    Unsafe */
    }
    }

/*

  • fork15 - Using a handler that reaps multiple children
    */
    void fork15()
    {
    pid_t pid[N];
    int i;
    ccount = N;

    signal(SIGCHLD, child_handler2);

    for (i = 0; i < N; i++)
    if ((pid[i] = fork()) == 0) {
    sleep(1);
    exit(0); /* Child: Exit */

    }
    while (ccount > 0) {
    pause();
    }
    }
    void fork16()
    {
    if (fork() == 0) {
    printf(“Child1: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    if (fork() == 0)
    printf(“Child2: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    while(1);
    }
    }
    void fork17()
    {
    if (fork() == 0) {
    printf(“Child: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    }
    else {
    printf(“Parent: pid=%d pgrp=%d\n”,
    getpid(), getpgrp());
    }
    while(1);
    }

分析:

fork0()
在这里插入图片描述
体现了fork函数调用一次,返回两次的特点,可以看到输出语句printf(“Hello from child\n”);和printf(“Hello from parent\n”);都执行了,这是因为fork()函数返回了一个子进程和一个父进程

fork1()在这里插入图片描述
该代码用到了getpid()函数,该函数返回进程的PID。
父进程与子进程都有变量x且值为1,接下来子进程输出x+1的值,父进程输出x-1的值,并且两进程最后都输出其进程号及x的值。我这台机子先选择执行了父进程的输出语句(并发)

fork2()在这里插入图片描述
两次调用fork函数,父进程创建了两次子进程,而第二次调用时,子进程也创建了相应的子进程。该程序将会输出一个L0,两个L1,四个Bye,而L0必定是第一个输出。

fork3()在这里插入图片描述
三次调用fork函数,父进程创建了三次子进程,而前两次调用的子进程也创建了相应的子进程。程序将会输出一个L0,两个L1,四个L2,八个Bye,其中L0必定是第一个输出,L1为第二个输出!](https://img-blog.csdnimg.cn/20201227210458166.png#pic_center)

fork4()在这里插入图片描述
嵌套调用fork函数,因此父进程第一次创建的子进程由于不满足if条件所以直接执行输出Bye,而父进程则进入条件语句并且再一次创建子进程,并且只有父进程输出L2,因此程序将会输出一个L0,一个L1,一个L2,三个Bye,其中L0必定是第一个输出,L2之前有L1输出,Bye之前可以只有L0,或者只有L0和L1,或者L0,L1,L2都在它前面输出

fork5()在这里插入图片描述

与fork4相似,只是父进程不满足if条件所以直接执行输出Bye,而父进程第一次创建的子进程则进入条件语句并且再一次创建子进程,并且只有第一次创建的子进程的子进程输出L2,因此程序将会输出一个L0,一个L1,一个L2,三个Bye

fork6()
在这里插入图片描述
调用atexit函数,父进程和子进程各执行一次退出,并且两者是独立执行的。exit()时调用函数cleanup,输出Cleaning up。

fork7()在这里插入图片描述

在我的机器上优先执行父进程,因此先执行语句printf(“Running Parent, PID = %d\n”, getpid());,然后由于死循环while(1),程序就会优先选择执行子进程的语句printf(“Terminating Child, PID = %d\n”, getpid());,随后子进程正常退出,程序回到父进程继续执行死循环,这就导致程序一直在运行无法进行下一步。

fork8()在这里插入图片描述

fork8父进程正常终止,并且由于子进程进入死循环,父进程没有去回收子进程,子进程变成了孤儿进程。

fork9()在这里插入图片描述

语句wait(&status); 等价于语句waitpid(-1,&status,0);
默认情况下(当options=0时),waitpid挂起调用进程的执行,直到它的等待集合中的一个子进程终止。
等待集合的成员是由参数pid来确定的:
如果pid>0,那么等待集合就是一个单独的子进程,它的进程ID等于pid。
如果pid=-1,那么等待集合就是由父进程所有的子进程组成的。
如果statusp参数时非空的,那么waitpid就会在status中放上关于导致返回子进程的状态信息,status是statusp指向的值。
因此wait(&child_status);或者waitpid(-1,&status,0); 代表挂起调用进程的执行,直到等待集合(父进程的所有子进程)中的一个子进程终止,就将状态信息放在变量child_status里。

因此我们可以看到父进程先执行printf(“HP: hello from parent\n”);语句
后遇到wait(&child_status);则等待子进程运行printf(“HC: hello from child\n”);并退出
父进程再接下来执行printf(“CT: child has terminated\n”);
最后执行printf(“Bye\n”);

fork10()在这里插入图片描述

这里出现了WEXITSTATUS(child_status),它代表返回一个正常终止的子进程的退出状态。只有WIFEXITED()返回为真时,才会定义这个状态。 如结果所示,这里返回了exit的返回状态值。

首先父进程调用wait(&child_status) 即waitpid(-1,&child_status,0)来等待它的子进程终止并获得其信息,因为pid参数默认为-1,所以对wait的调用会阻塞,直到任意一个子进程终止。在每个子进程终止时,对wait的调用会返回,返回值为该子进程的非零的PID给变量wpid。
接下来检查子进程的退出状态。WIFEXITED(child_status) 代表如果子进程通过调用exit或者一个返回(return)正常终止,就返回真。

fork11()在这里插入图片描述

与上一个类似,但它消除了不确定性。调用wait(&child_status)换成了waitpid(pid[i], &child_status, 0),表示父进程的等待集合是一个单独的子进程,它的进程ID等于pid[i]。
这次是等到一个子进程就输出其信息,因此顺序是确定的,按照父进程创建子进程的倒序回收这些子进程。父进程按照顺序存储了它的子进程的PID,然后通过用适当的PID作为第一个参数来调用waitpid,然后等待子进程。

fork12()在这里插入图片描述

kill函数发送SIGINT信号,将本会死循环的子进程通过信号终止,相当于用户自己键入Ctrl+C。
首先创建N个子进程(N在前面是5的宏定义),子进程的PID保存在pid[i]数组里,并且每个子进程都是死循环。父进程调用wait函数等待子进程,如果没有kill函数,则子进程迟迟不终止,父进程就要一直等待子进程而停滞。
创建完子进程后先父进程输出语句,后通过kill函数根据pid数组里的子进程PID对每个子进程发送SIGINT信号(相当于键入Ctrl+C终止进程),这样每个子进程就终止了。
接下来对剩下的父进程调用wait函数,回收到子进程的PID给wpid变量,但是由于子进程非正常终止,WIFEXITED返回假,最终执行else的输出。

fork13()在这里插入图片描述

调用了signal函数,将SIGINT信号的原有行为终止改为去实现自己定义的int_handler函数。
创建N个不会正常退出而会死循环的子进程。
然后仍是给子进程发SIGINT信号,但这次子进程不会去执行SIGINT信号的默认行为终止,而转去运行int_handler函数输出语句打印子进程PID及信号号码(SIGINT信号的序号为2)并且正常退出,这里子进程最后根据exit(0)正常退出
由于子进程正常退出,WIFEXITED返回真,因此执行if语句里的输出,输出子进程PID和退出状态(exit(0)里的0决定了退出状态为0)

fork14()在这里插入图片描述

子进程因为sleep被拖延了时间,父进程在等待子进程退出前的时间里一直在死循环。接下来的子进程未被回收,成为僵死进程。
该代码需要每个子进程退出时各返回一个SIGCHLD信号给父进程,然后父进程在child_handler函数里等待到一个子进程输出完ccount-1若ccount仍大于0就继续回去死循环了。

fork15()在这里插入图片描述
pause函数让调用函数休眠,直到该进程收到一个信号。
由于加入了这个函数,即使父进程在死循环也要等待信号到来执行相应调用函数,这样所有的子进程都执行了

fork16()在这里插入图片描述

由于几个子进程陷入死循环而成为僵死进程,可用kill -9来杀死相应进程。
getpid函数返回调用进程的PID。
getpgrp函数返回它的父进程的PID。

fork17()在这里插入图片描述

我们可以用Ctrl+Z挂起程序。如果通过ps查看运行状态时发现死循环的进程仍未清除。我们可以通过kill -9来杀死父进程,并且它相应的子进程就会被回收。
我们也可以用Ctrl+C直接终止程序,再通过ps查看运行状态,可以发现死循环的进程均被清除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值