系列文章:
操作系统详解(1)——操作系统的作用
操作系统详解(2)——异常处理(Exception)
操作系统详解(3)——进程、并发和并行
操作系统详解(4)——进程控制(fork, waitpid, sleep, execve)
操作系统详解(5)——信号(Signal)
要解决第一问,我们首先分析一下程序的流程。
第22行到第28行:sigfillset把当前进程的所有信号赋给mask_all, 然后用sigprocmask把所有的信号都Block,并把原来的状态存在prev_one中
然后Signal函数把信号和handler绑定。
第29行到第34行:fork() 创建了一个子进程。若在子进程中,则先调用两次alarm.
32行将信号unblock,也就是在32行以后子进程才会收到信号。
于是30行与31行必定会先执行。由于之前从未设置过alarm, 所以30行alarm返回0.
虽然现实中一般两行间是紧接着执行的,或者操作系统中断执行后在非常短的时间后就会将控制流回到下一行。但是这里题目是理想状态下,即操作系统在任意时刻都可能把当前进程阻塞,并且在任意时长以后再恢复。所以31行的alarm可能返回0,1,2. 即30执行完后可能被阻塞了0, 1, 2秒甚至更长的时间。
33行子进程暂时休眠。等被信号唤起后exit退出。
36行到41行:父进程的内容。在39行前,父进程的信号全部被BLOCKED. 38行向子进程发送了SIGALRM信号。最后是一个while循环,等待子进程返回。
handler里的内容:
可以知道,handler想要执行,必定在子进程执行32行之后。初次执行从第8行开始。第9行再次调用alarm. 但是由于handler会block自身的信号,所以只是设置pending bit, 不会再次执行Handler.
这里的alarm会返回什么呢?由于前一次alarm是3秒,所以这里的返回值可能是0,1,2,3. 之所以会有这些区别,是因为handler并不一定是被alarm(3)触发。前一次的alarm(2)和alarm(3)间可能有两秒以上的间隔使得alarm(2)结束后向子进程发送SIGALRM信号。并且,父进程中的kill也可以触发handler
11行接着向父进程发送SIGUSR1信号,对应的handler在第18行。那么18行一定会执行吗?答案是对的。即使父进程设置了block, 但是第39行unblock信号后还是会check pending bit,这时候就会执行handler_usr1.
回到子进程。第12行子进程休眠5秒,当然随时有可能被Exception打断(比如说其它信号,或者收到了一个网络包什么的)。第13行向子进程自己发送了SIGUSR2. 执行完第13行进程会立刻执行handler_usr2. 一般情况下,信号的生效时间是不确定的。但在此例中,由于kill是一个系统调用,而系统在从内核态返回用户态时会check pendning bit. 由于发现SIGUSR2还没有被接收,所以会执行handler_usr2.
执行完了handler,子进程回到原来的控制流位置。这里很有意思的是,第33行的 pause() 函数有可能不会返回,也就是说子进程可能会永久休眠。这是因为,很可能在39行UNBLOCK信号以后,handler就处理了所有进程的SIGALRM信号,pause 以后没有再接收信号,故不会返回。这种情况下第20行的handler_chld就不执行。所以第20行是可能执行的。
而第5&6行是不可能被执行到的。前面说过,handler不会被同种信号再次打断,所以一旦执行过一次handler, pre_alarm和post_alarm都会置1,所以不存在post_alarm为0而pre_alrm为1的情况。
所以第一问的答案是:
Line | Visibility | Possible return values of alarm() |
---|---|---|
5&6 | F | ---- |
9&10 | T | 0/1/2/3 |
15 | T | ------ |
18 | T | ------ |
19 | T | ------ |
20 | M | ------ |
30 | T | 0 |
31 | T | 0/1/2 |
36 | T | ------ |
程序的详细逻辑已经在上面分析过了,第二问就是把过程以流程图呈现。主要是弄清楚哪些行是某一行的必要条件,比方说只有unblock信号后才可能执行handler
时序图:
由代码可知,第30,31,38,9行都可能会向子进程发送信号, 故最多发送4个信号。
但是最多只会收到3个信号,原因如下:
如果31行的alarm取消了30行的alarm,那么30行的信号就不会发出。
即使30行的alarm没有被取消,第31行和第9行的信号最多只会收到一个:
如果31行的alarm()结束了以后才解除block,那么31发送信号的pending bit就会被原来30发送的信号覆盖(因为pending signal is not queued)
而如果进入handler后31行的alarm尚未结束,那么在运行第9行时,要么取消了31行的alarm,要么第9行发送的信号被31行发送的pending signal覆盖。
所以第31行和第9行的信号最多只会收到一个。