9.僵尸进程的处理

来源:互联网 发布:软件微电子 综合信息 编辑:程序博客网 时间:2024/05/17 09:34

TCP是一个流协议

       TCP是基于字节流传输的,只维护发送出去多少,确认了多少,没有维护消息与消息之间的边界,因而导致粘包问题.

        粘包的解决方法是在应用层维护消息边界.

僵尸进程与SIGCHLD信号

          signal(SIGCHLD,SIG_IGN);///  忽略SIGCHLD信号

         signal(SIGCHLD,handle_sigchld);


   5个客户端一起向服务器发送连接请求

这是客户端程序,服务器程序,没有发生变换


/// echolic.c#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>#include <arpa/inet.h>#include <netinet/in.h>#include <string.h>#define ERR_EXIT(m) \do{ \perror(m); \exit(EXIT_FAILURE); \}while(0)ssize_t readn(int fd,void *buf,size_t count){size_t nleft = count ; // 未读取的数据ssize_t nread;// 已读取的数据char *bufp= (char*)buf;while(nleft > 0){if( (nread = read(fd,bufp,nleft)) < 0){if( errno == EINTR) nread = 0;//  继续读取数据elsereturn -1;}else if( nread == 0) // 对方关闭或已经读到eofbreak;bufp +=nread;nleft -= nread;}return count-nleft;}ssize_t writen(int fd,const void *buf,size_t count){size_t nleft=count;  // 未读取的ssize_t nwritten;    // 已读取的 char *bufp = (char*)buf;  while(nleft > 0) { if((nwritten = write(fd,bufp,nleft)) < 0) { if( errno == EINTR) continue; else return -1; } else if( nwritten == 0) continue; bufp  += nwritten; nleft -= nwritten; } return count;}/// recv()只能读取套接字,而不能读取一般文件描述符ssize_t recv_peek(int sockfd, void *buf, size_t len){while(1){int ret = recv(sockfd,buf,len,MSG_PEEK);//MSG_PEEK接收缓冲区的数据,但是并没有清除if( ret == -1 && errno == EINTR)continue;return ret;}}// 读到'\n' 就返回,加上'\n'一行最多为maxline个字符ssize_t readline(int sockfd, void *buf, size_t maxline){int ret;int nread;char *bufp =(char *) buf;int nleft = maxline;//int count=0;while(1){// recv_peek读取缓冲区的字符个数,并放入到bufp缓存里面ret = recv_peek(sockfd,bufp,nleft);if(ret < 0)return ret;// 表示失败else if(ret == 0)return ret; // 表示对方关闭连接了nread = ret;// 判断接收到字符是否有'\n'int i;for(i=0; i<nread; ++i){if(bufp[i] == '\n'){    // readn读取数据,这部分缓冲会被清空的ret = readn(sockfd,bufp,i+1);if(ret != (i+1))exit(EXIT_FAILURE);return ret; //+ count;}}if( nread > nleft)exit(EXIT_FAILURE);nleft -= nread;ret = readn(sockfd,bufp,nread);if(ret != nread)exit(EXIT_FAILURE);bufp += nread;// 下一次指针偏移//count += nread;}return -1;}void echo_cli(int sock){char sendbuf[1024] = {0};char recvbuf[1024] = {0};while( fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)  {writen(sock,sendbuf,strlen(sendbuf));// 需要的注意的的时sizeof(sendbuf)和strlen(recvbuf)不一样的,容易出现混淆int ret = readline(sock,recvbuf,1024);///最后一个参数为缓冲区的最大值if( ret == -1 )ERR_EXIT("readline");else if(ret == 0){printf("client close \n");break;}fputs(recvbuf,stdout);memset(recvbuf,0,sizeof(recvbuf));memset(sendbuf,0,sizeof(sendbuf));}        close(sock); }int main(){int sock[5];int i;for( i=0;i<5;++i){if( (sock[i] = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)ERR_EXIT("sock err");struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5188);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if( connect(sock[i],(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)ERR_EXIT("connect error");struct sockaddr_in localaddr;socklen_t addrlen = sizeof(localaddr);if(getsockname(sock[i],(struct sockaddr*)&localaddr,&addrlen) < 0)ERR_EXIT("getsockname err");printf("ip=%s ,port = %d \n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));}// 为了方便,只使用一个套接字通信echo_cli(sock[0]);      return 0;}




   没有处理子进程,结果出现了好多5个僵尸进程.

最简单的办法就是父进程直接忽略SIGCHLD信号,即:

              signal(SIGCHLD,SIG_IGN);  // 忽略SIGCHLD信号

                   忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源


                           如果我们想要捕获SIGCHLD信号的话,在信号处理函数中不能只调用一次wait/waitpid 函数,因为客户端退出发出FIN段的时机是不一定的,如果都能按一定时间顺序发送给5个服务器子进程,即子进程发生SIGCHLD信号给父进程的时间有前后之分,那handler函数会被调用多次,则是允许的,也不会产生僵尸进程;但当多个SIGCHLD信号同时到达,因为不可靠信号不能排队导致信号只保存一个,即其余信号会丢失,则产生的僵尸进程个数是不确定的,因为按前面所说取决于5个SIGCHLD信号到达的次序。解决的办法很简单,只要在handler函数中while 循环一下就ok 了,即使5个信号同时到达,只要接收到一个SIGCHLD信号,则5个子进程都会被清理掉:

void handler(int sig){    /*  wait(NULL); //只能等待第一个退出的子进程 */       while (waitpid(-1, NULL, WNOHANG) > 0)        ;}

signal(SIGCHLD, handler);

           前几个程序,都没有注意到僵尸进程,平时的记得处理僵尸进程.













0 0