首页 > 代码库 > 第十二篇:并发回射服务器的具体实现及其中僵尸子进程的清理( 上 )
第十二篇:并发回射服务器的具体实现及其中僵尸子进程的清理( 上 )
前言
本文将分为两个部分,第一部分具体实现一对并发回射服务器/客户程序( 看过前面那篇文章的这部分可不看 重复了 );第二部分为服务器添加僵尸子进程自动清理机制。
那么服务器具体怎么实现并发?怎么会有僵尸进程?僵尸进程又是什么?如何处理这些僵尸进程 ... 本文将为你一一解惑。
回射并发服务器
功能:接收用户发送过来的数据后再发送回用户,且能同时处理多个用户请求。
大体思路:每当收到用户请求,服务器就fork一个子进程,让子进程去处理客户请求。
实现代码:
1 #include "unp.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 int listenfd, connfd; 7 pid_t childpid; 8 socklen_t clilen; 9 struct sockaddr_in cliaddr, servaddr;10 11 listenfd = Socket(AF_INET, SOCK_STREAM, 0);12 13 bzero(&servaddr, sizeof(servaddr));14 servaddr.sin_family = AF_INET;15 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);16 servaddr.sin_port = htons(SERV_PORT);17 18 Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));19 20 Listen(listenfd, LISTENQ);21 22 for ( ; ; ) {23 clilen = sizeof(cliaddr);24 connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);25 26 /*27 * 子进程代码28 */29 if ( (childpid = Fork()) == 0) {30 // 子进程启动后首先关闭监听套接字31 Close(listenfd); 32 // 回射处理33 str_echo(connfd);34 // 退出子进程35 exit(0);36 }37 // 关闭连接套接字38 Close(connfd); 39 }40 }
1 #include "unp.h" 2 3 void 4 str_echo(int sockfd) 5 { 6 ssize_t n; 7 char buf[MAXLINE]; 8 9 again:10 while ( (n = read(sockfd, buf, MAXLINE)) > 0)11 Writen(sockfd, buf, n);12 13 if (n < 0 && errno == EINTR)14 goto again;15 else if (n < 0)16 err_sys("str_echo: read error");17 }
回射并发客户端
功能:向服务器发送数据,接收服务器反射回的信息并打印。
大体思路:略。
实现代码:
1 #include "unp.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 int sockfd; 7 struct sockaddr_in servaddr; 8 9 if (argc != 2)10 err_quit("usage: tcpcli <IPaddress>");11 12 sockfd = Socket(AF_INET, SOCK_STREAM, 0);13 14 bzero(&servaddr, sizeof(servaddr));15 servaddr.sin_family = AF_INET;16 servaddr.sin_port = htons(SERV_PORT);17 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);18 19 Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));20 21 str_cli(stdin, sockfd); // 与服务器通信 22 23 exit(0);24 }
1 #include "unp.h" 2 3 void 4 str_echo(int sockfd) 5 { 6 ssize_t n; 7 char buf[MAXLINE]; 8 9 again:10 while ( (n = read(sockfd, buf, MAXLINE)) > 0)11 Writen(sockfd, buf, n);12 13 if (n < 0 && errno == EINTR)14 goto again;15 else if (n < 0)16 err_sys("str_echo: read error");17 }
运行测试
1. 在一个终端以超级用户权限启动服务器
2. 在另几个终端打开客户端并输入IP地址参数127.0.0.1
3. 执行回射测试,运行情况如下( 几个客户终端都运行正常 ):
问题发现
看似这个并发回射程序已经做好了。但,很遗憾,不是的。我们接下来的观察将会带我们进入本文真正的主题:僵尸子进程。
首先退出这几个客户终端。按照我们最初的设想,这几个子进程应该已经结束了。但事实真是这样吗?我们在终端执行以下命令进行查看:
图中,STAT显示表示进程状态,Z+表示进程正处在“ 僵尸 ”状态。通过比对其PID与PPID不难发现,服务器中处理客户端的进程并没有“ 死去 ”,而是变成了“ 僵尸 ”。
什么是僵尸进程
僵尸进程就是已经撤销但依然占着资源的进程。
为什么要有僵尸进程
设置僵死状态的目的是为了维护子进程的信息。父进程有时需要获取到一些已经撤销的子进程的信息。
如何处理僵尸进程
对于本例中的僵尸进程,我们当然要将其清理掉并释放其占用的资源。
第十二篇:并发回射服务器的具体实现及其中僵尸子进程的清理( 上 )