一、前提情况描述
1. linux系统,有父子进程,本案例中,一个父进程,三个子进程。
2.子进程都要使用管道与父进程通信,父进程收到的终端数据,分别通过管道(pipe)发送给3个子进程,子进程(3个)收到数据后,通过(另一个)管道(pipe)将数据(原封)发送给父进程,父进程读出管道的数据,打印到终端。
3.正常情况下,管道应该是6个,父进程与3个子进程之间分别有两个管道。
二、故障现象描述
1. 父进程收到终端的数据,将数据发送到子进程,子进程返回数据给父进程,父进程能正常打印出子进程的数据,该功能(3个子进程都)正常。
2. 有一个子进程收到父进程的数据,除了发送给父进程外,还需要写到指定的文件中。但是这个文件(文件是存在的)没有任何数据,文件字节大小为0.
3. 父进程收到终端的EOF,进程结束,父进程正常退出了。
4. 删除2步骤中指定的文件,再次运行程序时,会产生一个0字节的文件;或者在文件中写入内容,再次运行程序时,文件大小被改为0.
三、问题的解决
1. 前期一直不知道是什么原因,程序运行正常,但是对应的文件内容为0,就感觉很奇怪。
2. 用stat命令查看文件的时间,发现创建时间,修改时间,访问时间均相同,那就是只创建了,没有写入数据。用一个可行的程序发现,创建时间与修改时间差了4秒。看来文件被创建,但是没有被写入。
3.考虑到文件写入的内容不足100个字节(文件缓存一般是4k左右),还不足以自动刷新缓存。但是子进程不是我自己写的,无法加入刷新缓存操作。
4.考虑进程异常结束(缓存没有被刷新)?或者进程没有结束?(缓存不会被刷新)。使用ps命令一看,果然有很多子进程还在跑着。
5.问题定位到,父进程终止,而子进程没有终止(不是说父进程终止,子进程就必须要终止,这里是有条件的。)。什么原因呢?。
6.我猜是pipe的端口没有正常关闭。仔细(反复看了不少于10遍)看了源码,看到父进程和子进程在进入的时候,都关闭掉了自己不用的pipe端口了呀。
7.父进程终止的时候,与子进程的pipe的写段和读端都关闭了,这时候子进程再读的话应该会读到EOF,或者写pipe的时候,应该也是出错,这时,子进程应该就结束了呀。(理论应该是这样)
8.找不到破绽,因为是普通用户,无法查看/proc/pid/fd目录的情况(提示没有权限)。
9.硬着头皮返回代码,发现,在创建子进程(fork)之前,父进程(一次性)已经申请了6个管道。fork之后每个子进程都持有子进程之间的管道读写端。
10.找到父进程终止(再次读管道会读到EOF,写管道会报错)的原因。是因为即使父进程关闭了管道的读写端,但是仍然有(其他)子进程持有读写端,所以读写该管道的子进程不会读到EOF,写管道也不会出错,造成子进程仍然等待。
11.解决:需要在每一次fork之前创建每个子进程需要使用的两个管道。(而不是一次性把管道全部创建后,再去三次fork)。父进程需要关闭不需要的端口,然后再下一次创建两个管道给下一个子进程,然后再fork,依次类推。
12.修改了创建管道的时机后,问题得到解决。(父进程退出后,子进程也跟着结束了)。
13.本案例中如果只有一个子进程,不会出现上述问题。
14.fork使用确实很简单(逻辑功能可能会变得比较复杂),配合着其他功能,可能就会出现莫名的问题。(有需要的同学借鉴一下)
15.本案例中,管道还是6个,但是每个管道就分别被4个进程持有相关的读端和写端(尤其是不相干的两个子进程,是持有另一个子进程与父进程管道的读端和写端的,他们没有做任何关闭操作)。正常情况应该是只有父进程持有6个管道的部分读写端,而每个子进程应该只持有一个管道的写端和另一个管道的读端,每个子进程应该拥有不同于另外两个子进程的(两个)管道。