操作系统详解(5.2)——信号(Signal)的题目进阶

发布时间:2024年01月15日

系列文章:
操作系统详解(1)——操作系统的作用
操作系统详解(2)——异常处理(Exception)
操作系统详解(3)——进程、并发和并行
操作系统详解(4)——进程控制(fork, waitpid, sleep, execve)
操作系统详解(5)——信号(Signal)
操作系统详解(5.1)——信号(Signal)的相关题目

文章目录

题目

(如果有错误,欢迎指正)

const int num = 4;
volatile sig_atomic sum = 0, child = 0;
volatile sig_atomic n = num;
void handler_chld(int sig){
	int status;
	while(waitpid(-1, &status, 0) > 0){
		if(WIFEXITED(status)){
			sum += WEXITSTATUS(status);
			n += 1;
			child += 1;
		}
	}
}
int main()
{
	sigset_t mask_all, prev_one;
	sigemptyset(&mask_all);
	sigaddset(&mask_all, SIGCHLD);
	sigprocmask(SIG_BLOCK, &mask_all, &prev_one);
	signal(SIGCHLD, handler_chld);
	while(1){
		if(n == 0) exit(0);
		if(n == 1) exit(1);
		n = n - 1;
		if(fork() == 0) continue;
		n = n - 1;
		if(fork() == 0) continue;
		break;
	}
	sigprocmask(SET_MASK, &prev_ont, NULL);
	if(child < 2) pause();
	if(n == sum){
		printf("f[%d] = %d\n", n, sum);
	}
	exit(sum);
}
  • Q1: 程序一共fork了几次?
  • Q2: 为什么要把SIGCHLD阻塞?
  • Q3: pause()是否一定会返回?
  • Q4: 程序的输出是什么? 如果改成const int num = 9呢?

Q1

首先画出程序的流程图
(画图是解决此类题型的重要方法)
屏幕截图 2024-01-15 194744.png
可见一共fork了8次

Q2

要把SIGCHLD阻塞,肯定是在fork()期间不能被信号干扰。
观察handler内,发现会修改n, child的值。而fork()会使子进程与父进程拥有相同的变量值。以child为例,如果不屏蔽信号,那么很可能会使进程接收到信号后使child+1=1, 那么当fork以后,子进程的child也为1,就无法保证子进程在fork()以后,能够pause()等待回收子进程。

Q3

在此例中,pause()是一定可以返回的。
handler里是一个while循环,条件是waitpid, 这样可以保证只需要收到一个信号就可以回收所有子进程,不会出现多个信号发送被覆盖的情况。
假如所有信号都在UNBLOCK之前收到,那么UNBLOCK的时候就会执行handler, 并且回收所有子进程,这样就会使child >=2,不会执行pause().
(所有的父进程的子进程个数一定>=2,下一问会分析)

如果在UNBLOCK之前收到了一个signal:

  • 如果在handler中又收到了信号,则会在结束后紧接着再次执行handler,使得child>=2, 不会执行pause()
  • 如果在pause()后收到信号,则会打断pause()

如果在pause()以后才收到信号,则会打断pause, 并且在handler中等待回收所有的子进程。

Q4

上面的流程图虽然表明了n的取值,但是不能反映出父进程与子进程之间的关系。
先举一个简单的例子:假设进程p1经过fork()以后得到p1和p2,二者分别再fork得到p3和p4。
屏幕截图 2024-01-15 203057.png

  • p1是p2和p3的父亲
  • p2是p4的父亲

与流程图相对应的话,以最上方的parent为例,它的子进程包括

  1. child fork 后得到的父进程
  2. 前一次(即第一次)fork 得到的子进程fork()两次之后的父进程

语言描述比较抽象,图中用笔圈出了关系:
x2.png

类似的还有:
x3.png

弄清楚了fork()多次后得到的父子进程的关系,我们就可以着手分析sum的值了. 由题意可知父进程的返回值为所有子进程的返回值之和,且每有一个子进程执行n+1. 用图表示:
x0.png
故输出是:f[4] = 3

如果改成num = 9:由上图标红的n=4, n=3, n=2可以知道,sum分别是3, 2, 1.
有sum = num - 1
故num = 9时, sum = 8
输出f[9] = 8

如果你不相信的话,我们假设num = 3, 再画一下流程图:
xx.png
可以验证sum = 2;

Q3中的问题:由于fork()以后子进程最后一定会直接exit返回,所以跳出while循环的父进程最少拥有两个子进程

文章来源:https://blog.csdn.net/MagicianofLove/article/details/135611531
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。