系列文章:
操作系统详解(1)——操作系统的作用
操作系统详解(2)——异常处理(Exception)
操作系统详解(3)——进程、并发和并行
操作系统详解(4)——进程控制(fork, waitpid, sleep, execve)
操作系统详解(5)——信号(Signal)
操作系统详解(5.1)——信号(Signal)的相关题目
文章目录
- 题目
- Q1
- Q2
- Q3
- Q4
题目
(如果有错误,欢迎指正)
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
首先画出程序的流程图
(画图是解决此类题型的重要方法)
可见一共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。
- p1是p2和p3的父亲
- p2是p4的父亲
与流程图相对应的话,以最上方的parent为例,它的子进程包括
- child fork 后得到的父进程
- 前一次(即第一次)fork 得到的子进程fork()两次之后的父进程
语言描述比较抽象,图中用笔圈出了关系:
类似的还有:
弄清楚了fork()多次后得到的父子进程的关系,我们就可以着手分析sum的值了. 由题意可知父进程的返回值为所有子进程的返回值之和,且每有一个子进程执行n+1. 用图表示:
故输出是: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, 再画一下流程图:
可以验证sum = 2;
Q3中的问题:由于fork()以后子进程最后一定会直接exit返回,所以跳出while循环的父进程最少拥有两个子进程