在回射客户/服务器程序中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现。网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端。但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程,最简单的办法就是直接忽略SIGCHLD信号。
上述程序利用了一点,就是父子进程共享打开的文件描述符,因为在子进程已经用不到监听描述符,故将其关闭,而连接描述符对父进程也没价值,将其关闭。当某个客户端关闭,则read 返回0,退出循环,子进程顺便exit,但如果没有设置对SIGCHLD信号的忽略,则因为父进程还没退出,故子进程会变成僵尸进程。
现在先运行server,再打开另外两个终端,运行client(直接用回射客户/服务器程序中的客户端程序),可以看到server输出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_fork
recv connect ip=127.0.0.1 port=46452
recv connect ip=127.0.0.1 port=46453
在另一个终端ps一下:
simba@ubuntu:~$ ps aux | grep echoser
simba 3300 0.0 0.0 280 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3303 0.0 0.0 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3305 0.0 0.0 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3313 0.0 0.0 4392 836 pts/3 S+ 22:12 0:00 grep --color=auto echoser
simba@ubuntu:~$
发现共有3个进程,其中一个是父进程处于监听中,另外两个是子进程处于对客户端服务中,现在ctrl+c 掉其中一个client,由上面的分析可知对应服务的子进程也会退出,而因为我们设置了父进程对SIGCHLD信号进行忽略,故不会产生僵尸进程,输出如下:
simba@ubuntu:~$ ps aux | grep echoser
simba 3300 0.0 0.0 280 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3305 0.0 0.0 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3321 0.0 0.0 4392 836 pts/3 S+ 22:13 0:00 grep --color=auto echoser
如果把29行代码注释掉,上述的情景输出可能为:
simba@ubuntu:~$ ps aux | grep echoser
simba 3125 0.0 0.0 280 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3128 0.0 0.0 0 0 pts/0 Z+ 21:38 0:00 [echoser_fork] <defunct>
simba 3130 0.0 0.0 60 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3141 0.0 0.0 4392 832 pts/3 S+ 21:40 0:00 grep --color=auto echoser
即子进程退出后变成了僵尸进程。
如果不想忽略SIGCHLD信号,则必须在信号处理函数中调用wait处理,但这里需要注意的是wait只能等待第一个退出的子进程,所以这里需要使用waitpid函数,如下所示:
实际上使用 while (wait(NULL) > 0) ; 也可以达到同样的效果。
来源:csdn 作者:Simba888888