《深入理解计算机系统》(csapp)有关信号处理程序的两段代码的理解。

来源:互联网 发布:如何评价马自达 知乎 编辑:程序博客网 时间:2024/05/29 13:46

两段程序分别是http://csapp.cs.cmu.edu/public/ics2/code/ecf/signal1.c和http://csapp.cs.cmu.edu/public/ics2/code/ecf/signal2.c。

是第八章信号那一节中的两段程序,第一遍看的时候感觉懂了差不多,后来又反回来看的时候,发现其实很多问题只是似懂非懂,于是重新花了3个多小时彻底把这两段程序研究了一下。

Signal1.c说明:

1,首先是3个子进程先后被创建,虽然有时间先后顺序,但是创建子进程前后差的时间也就是一句i++和i<3的时间,微乎其微,也就是说3个子进程几乎是在同时睡眠,也几乎是同时醒来,执行分别的exit(0)的时间差也就是执行i++和i<3的时间。

2,虽然3个子进程先后时间差的很少,但毕竟也还是有差别的,第一个子进程最先终止,然后内核发送了一个SIGCHLD信号给父进程,此时由于父进程正在执行read语句,然后这个慢速系统调用被中断(根据慢速系统调用在中断返回时在某些系统中不会重启这一个理由,所以我们在编写程序时要显示重启read,使得代码可移植),父进程收到SIGCHLD信号后,调用我们设置的信号处理程序handler,handler程序以极快的速度执行完waitpid语句后(执行这个语句意味着处理了一个死子进程),然后进入睡眠(sleep(2))。

3,在由于第一个子进程终止调用的信号处理程序睡眠还未醒来之前,第二个子进程肯定也已经终止了,因为他和第一个子进程终止差的时间远远小于睡眠的2秒种,父进程又收到一个SIGCHLD信号,但是现在处理SIGCHLD的信号处理程序正在执行,所以第二个SIGCHLD信号阻塞,成为待处理信号。

4,在由于第一个子进程终止调用的信号处理程序睡眠还未醒来之前,不但第二个子进程终止了,第三个子进程也终止了,在进程收到第二个SIGCHLD信号后,马上进程就会收到第三个子进程终止发来的SIGCHLD信号,由于现在已经有一个同类型的待处理信号了,而对于unix系统中信号来说,待处理信号不会排队等待,第三个SIGCHLD信号会被抛弃,因此第三个信号不会致使父进程调用相应信号处理程序,也因此第三个子进程不会被回收。

5,由于第一个子进程终止而调用的信号处理程序,睡眠醒来后,信号处理程序退出。内核会检查待处理信号集合中还有一个SIGCHLD信号,会迫使父进程第二次调用信号处理程序,回收终止的第二个子进程。

6,第二次调用的信号处理程序返回后,内核检查待处理信号集合应该是空的,恢复中断的read函数,等待用户输入。而第三个子进程终止后并没有被回收,仍然在消耗系统的存储器资源。

 

因此第一个程序编写的有问题。

 

再来看第二个程序signal2为什么就解决了子进程回收的问题。

1,开始同第一个程序运行的一样,创建3个子进程,第一个子进程最先终止,发出一个SIGCHLD信号,父进程第一次调用信号处理程序。

2,然而在信号处理程序执行到whlie语句条件中的waitpid时,信号处理程序会一直停止在这个while循环中直到父进程没有子进程为止。

3,waitpid处理完第一个终止的子进程后,此时很有可能第二个子进程也早已经终止了(即使此时第二个子进程没有终止也没有关系,waitpid会挂起调用进程,阻塞于此,一直等待有子进程终止),导致内核发送了第二个SIGCHLD信号给父进程,然后这个信号会成为待处理信号。但是,虽然这个信号成为待处理信号,没有立即马上调用相应的信号处理程序来处理,但是第一个SIGCHLD调用的信号处理程序中的waitpid语句仍然会回收终止的第二个子进程。

4,第三个子进程发出的SIGCHLD信号虽然会被抛弃,但是同样会被第一个SIGCHLD信号调用的信号处理程序中的waitpid回收。

5,至此,三个子进程都被回收完了,于是当第一个SIGCHLD信号调用的信号处理程序中的waitpid语句检查到没有父进程后,返回-1,while循环终止,睡眠2秒钟后,第一个信号调用的信号处理程序结束。

6,当控制从第一个信号调用的信号处理程序返回后,内核检测到待处理信号集合中还有一个SIGCHLD信号(就是第二个子进程终止发出的信号),虽然第二个子进程早已经被回收了,但是父进程还是会调用信号处理程序,while语句中的条件测试显然会失败,于是第二个SIGCHLD调用的信号处理程序什么都没有做,只是睡眠了2秒种就返回了。

7,而第三个SIGCHLD信号虽然连信号处理程序都没有调用,但是第三个子进程同样按照我们所希望的也被回收了。

 

 

因此可以看到,子进程虽然都被正确回收了,但是都是通过第一个子进程调用的信号处理程序中的waitpid语句回收的。

那么如果父进程创建了10个子进程,会发生什么呢?

那就是10个SIGCHLD信号,第一个信号调用了信号处理程序,在这个程序中不但回收了第一个子进程把其余9个子进程顺便也回收了。第二个信号成为待处理信号,第一个第一个信号调用了信号处理程序返回后,也会调用一次信号处理程序,但除了睡眠2秒外什么也不做。剩余的8个信号都被抛弃了。

 

要记住一点:虽然signal2能够正确回收所有进程,但这不是通过调用10次信号处理程序来完成的,signal1程序中,信号处理程序中的阻塞问题仍然存在,除了第一、二个信号外的信号同样会抛弃,解决问题的关键是让第一调用的信号处理程序中的while循环回收了所有的子进程。

 

原创粉丝点击