tcp回射服务器程序处理僵死进程
来源:互联网 发布:暗黑破坏神2 mac 双开 编辑:程序博客网 时间:2024/06/06 11:39
tcp回射服务器程序处理僵死进程
什么是僵死进程
当子进程调用exit指令退出的时候,会留下一个一个成为僵死(zombie)的数据结构,目的是维护子进程的信息,以便父进程在以后的某个时候获取,这些信息包括子进程的进程ID,终止状态以及资源利用信息等。在退出时他会向父进程发送SIGCHLD信号,如果父进程没有对该信号进行处理,该退出的子进程就会一直处于僵死状态,占用内核中的空间,多了以后甚至会导致我们耗尽进程资源。
如果tcp回射服务器不对SIGCHLD信号进行处理
tcp回射服务器程序如下(摘录自《UNIX网络编程 卷一》)
#include "unp.h"intmain(int argc, char **argv){ int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for ( ; ; ) { clilen = sizeof(cliaddr); connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); if ( (childpid = Fork()) == 0) { /* child process */ Close(listenfd); /* close listening socket */ str_echo(connfd); /* process the request */ exit(0); } Close(connfd); /* parent closes connected socket */ }}
以上代码段未对SIGCHLD信号进行处理,所以导致我们键入EOF字符来终止客户时候,对应该套接字的子进程就会一直处于僵死状态。
把两个客户端程序关闭,父进程没有对子进程发来的SIGCHLD信号处理导致子进程一直处于僵死状态。
对SIGCHLD信号进行处理
父进程接收到SIGCHLD信号后调用wait或waitpid函数来为子进程”收尸”。这两个函数的原型如下:
#include<sys/wait.h>#include "unp.h"pid_t wait(int *statloc);pid_t waitpid(pid_t pid,int *statloc,int options);/*这两个函数均返回两个值:已终止的进程ID号和通过statloc指针返回的子进程终止状态。通过设置options,我们可以指定附加选项,常用的选项是WNOHANG,它告知内核在没有已终止子进程时不要阻塞。pid参数允许我们制定想等待的进程ID,如果值为-1则表示等待第一个终止的进程。*//*如果调用wait的进程没有已终止的子进程,不过有一个或多个子进程仍在执行,那么wait将阻塞到现有的子进程第一个终止为止*/void sig_chld(int signo) { pid_t pid; int stat; pid = wait(&stat); printf("chlild %d terminated\n",pid); return;}//如果调用waitpid则可以通过循环等待所有子进程结束void sig_chld(int signo) { pid_t pid; int stat; while(pid = waitpid(-1,&stat,WNOHANG)) > 0) printf("child %d terminated\n",pid); return;}
我们应该使用wait还是waitpid?答案肯定是waitpid。我们假设一个情景:客户端程序通过循环与服务器建立了5个TCP连接,然后退出客户端程序,这5个连接几乎在同一时刻发给父进程SIGCHLD信号。因为Unix信号一般是不排队的,信号处理函数只执行一次。所以,如果使用wait,处理完第一个信号,信号处理函数就返回了,导致其他四个信号没有得到处理,还是导致了僵死。如果用waitpid的时候,我们可以通过一个循环,处理所有SIGCHLD信号,这样就没有僵死信号存留了。
注意:我们应该在listen调用之后增加该信号处理函数
Signal(SIGCHLD,sig_chld);
这必须在fork第一个子进程之前完成,且只做一次)
编写捕获信号的网络程序时,必须认清被中断的系统调用并处理他们
我们的服务器程序阻塞于慢系统调用(accept)捕获该信号,内核就会使accept返回一个EINTR错误(被中断的系统调用)而如果父进程不处理该错误,就会被中止。对于那些可能永远阻塞的函数,我们可以称之为慢系统调用。有些内核会自动重启被中断的系统调用,有些不会。所以为了程序的健壮性。我们要做的事情就是自己重启被中断的系统调用。
//在判断到EINTR错误的时候,执行continue返回循环重启accept//因为本文的程序基本摘抄自《Unix网络编程 卷一》所以读者如过要//编译这些代码的话需要该书提供的一些库for( ; ;) { clilen = sizeof(cliaddr); if( (connfd = accept(listenfd, (SA*) &cliaddr, &clilen) < 0) { if(errno == EINTR) continue; } else err_sys("accept error");}
总结
我们在网络编程的时候要注意这三种情况:
- 当fork子进程时,必须捕获SIGCHLD信号
- 当捕获信号时,必须处理被中断的系统调用
SIGCHLD的信号处理函数必须正确编写,应使用waitpid函数以免下僵死进程。
最后贴出注意以上三点后的服务器程序代码
#include "unp.h"intmain(int argc, char **argv){ int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; void sig_chld(int); listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); Signal(SIGCHLD, sig_chld); /* must call waitpid() */ for ( ; ; ) { clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) { if (errno == EINTR) continue; /* back to for() */ else err_sys("accept error"); } if ( (childpid = Fork()) == 0) { /* child process */ Close(listenfd); /* close listening socket */ str_echo(connfd); /* process the request */ exit(0); } Close(connfd); /* parent closes connected socket */ }}
- tcp回射服务器程序处理僵死进程
- Linux并发回射服务器(二):处理僵死进程
- TCP 回射程序(同步处理僵死进程的方式)
- 如何处理僵死进程
- 怎样处理僵死进程
- 如何处理僵死进程?
- python简单程序使用signal模块处理僵死进程
- linux 僵死进程及处理
- linux 僵死进程及处理
- 捕捉SIGCHLD,处理僵死进程
- linux僵死进程与并发服务器编程
- mysql处理查询僵死的进程
- linux 僵死进程及其处理方法
- linux处理僵死进程 及 信号
- 进程(程序)僵死问题解决方法
- 僵死进程
- 僵死进程
- 僵死进程
- jquery的Deferred--promise的解决方案
- 加班-不错的文章
- [SCOI2016][BZOJ4569]萌萌哒
- 关于两个"集合"相同的判断
- LINUX 中的一些配置文件
- tcp回射服务器程序处理僵死进程
- C++ 声明和定义
- IP聚合
- 动手制作操作系统——启动区制作
- ==与equals方法的区别
- C++第5次作业
- HDU 2571 命运(简单DP)
- Linux内核同步方法
- 剑指offer剖析__空格替换字符串问题