fork()二三事

fork();用于创建进程;返回值是整型:若是在子进程中则是返回0,父进程中返回子进程的PID

【例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);
} 

运行结果:
在这里插入图片描述
fork();创建了一个子进程;子进程,父进程分别打印了各自的pid与pgid
pid=4847和pid=4848属于同一进程组,组长是父进程pid=4847
值得注意的是程序“卡”住了,明显是由于了while(1);这条指令使程序(两个)停止在了这个位置
这时候,ctrl+z使进程挂起,再用ps指令查看当前正在运行的进程:
在这里插入图片描述

发现有两个forks.o进程,一个父进程一个子进程

在这里插入图片描述
杀死pid值为4847的forks.o进程,发现pid为4848的进程也同时消失了,说明pid=4847的进程使4848进程的父进程,4847在被杀死之前回收了其子进程
但是,如果在如果用ctrl+c替换前文提到的ctrl+z,会有不一样的结果:
在这里插入图片描述
原因使ctrl+z意为“挂起”,即将程序只是表面上退出,但是保持其进程继续在后台运行,只是在我们“眼前消失”了
而ctrl+c表示终止,将终止所有进程,后台也不保持运行

【例2】
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);
    }
} 

运行结果:

在这里插入图片描述
按理说1.应该有三个进程 2.而且程序并没有被在while(1);处停住,而是似乎自动被挂起了。可以执行其他命令
进程树如下:

最初的父进程最终终止了,而两个子进程都陷入了while(1);而没有终止,因此父进程在终止的时候不会回收其子进程
于是两个子进程都变成了孤儿进程,由inti进程收养这时要用kill命令杀死进程:

在这里插入图片描述

【例3】
#define N 5
int ccount = 0;
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 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中有一条语句:signal(SIGCHLD, child_handler2); 意思即为在以后的进程中,如果进程一旦捕获了一个类型为SIGCHLD的信号则会调用*child_handler这个信号处理程序来处理信号

SIGCHLD(==17):一个子进程停止或者终止

再来关注child_handler这个函数:里面用wait函数做了一个循环条件,因为在调用signal之前就已经把所以的子进程创建完成,所以signal中的while循环会等待所有的子进程结束才退出循环,wait的默认状态是挂起调用进程,只有当有某一子进程结束时才会返回这个子进程的pid,没有未返回子进程则返回-1

进程树如下:
在这里插入图片描述
让子程序sleep(1)的原因是保证在任一子进程结束时,父进程已经进入pause。

当接收到子进程的终止信号SIGCHLD时会停止pause,继而执行调用child_handler,打印相关信息,从child_handler返回之后ccount已经减到了0,退出循环,退出程序

【例4】
#define N 5
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)
	;
}

运行结果:

在这里插入图片描述
与例3类似,程序会先创建好5个子进程,然后在while(ccount>0);停住,接收SIGCHLD之后调用child_handler。但是在信号处理程序中只wait了一次,所以内核也只会从系统中删除一个子进程。剩下的四个均成为了僵死进程,从child_handler返回之后程序继续停在while(ccount>0);
ctrl+z将进程挂起,ps查看运行进程,发现正是有一个父进程,和四个僵死子进程。
此时只需将父进程杀死,就可以一并回收僵死子进程:

在这里插入图片描述

【例5】
#define N 5
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);
    }
}

运行结果:

在这里插入图片描述
程序中signal(SIGINT, int_handler);指每当进程接受到SIGINT信号就会调用*int_handler信号处理函数。

第一个循环为同一个父进程创建了5个子进程,并且让子进程们在while(1);处停住

第二个循环又在父进程中将这五个子进程杀死。每当一个子进程收到终止信号就会调用信号处理函数,打印“Process xxxx received signal 2”(SIGINT==2)
kill(pid[i], SIGINT);——将信号SIGINT传给 进程ID=pid[i]的子进程,使相应子进程终止

第三个循环检查子进程的退出状态
子进程退出会设置child_status的值
如果正常退出,WIFEXITED(child_status)返回为真
只有正常退出才会定义WEXITSTATUS这个状态
状态码(转载)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值