linux网络多进程编程处理僵尸进程
来源:互联网 发布:spss数据 excel 编辑:程序博客网 时间:2024/05/19 18:46
前面的多进程程序中,当客户端关闭后,一个子进程会关闭,但是其pdb仍然存在,通过ps -ef|grep server可以查看到,对于服务器来说,大量的僵尸进程会消耗进程控制块数目,影响服务器性能,因此僵尸进程必须处理。
什么是僵尸进程?
首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。
而僵尸进程就是指:一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。
任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。
僵尸进程的目的?
设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。
如何避免僵尸进程?
通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。
如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。
第一种方法忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。
#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/wait.h>#include<stdlib.h>#include<stdio.h>#include<errno.h>#include<string.h>#include<signal.h>#define ERR_EXIT(m) do{perror(m);exit(EXIT_FAILURE);}while(1)struct packet{int len;char buf[1024];};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){continue;return -1;}else if (nread == 0)return count - nleft;}bufp = bufp + nread;nleft = nleft - nread;}return count;}ssize_t writen(int fd, void *buf, size_t count){size_t nleft = count;ssize_t nwrite;char *bufp = (char*)buf;while (nleft > 0){if ((nwrite = write(fd, bufp, nleft)) < 0){if (errno == EINTR){continue;return -1;}else if (nwrite == 0)continue;}bufp = bufp + nwrite;nleft = nleft - nwrite;}return count;}ssize_t recv_peek(int sockfd, void *buf, size_t len){while (1){int ret = recv(sockfd, buf, len, MSG_PEEK);if (ret == -1 && errno == EINTR)continue;return ret;}}ssize_t readline(int sockfd, void *buf, size_t maxline){int ret;int nread;char *bufp = (char*)buf;int nleft = maxline;while (1){ret = recv_peek(sockfd, bufp, nleft);if (ret < 0)return ret;else if (ret == 0)return ret;//对方关闭了套接口nread = ret;for (int i = 0; i < nread; i++){if (bufp[i] == '\n'){ret = readn(sockfd, bufp, i + 1);//将缓冲区数据移除if (ret != i + 1)exit(EXIT_FAILURE);return ret;}}if (nread>nleft)exit(EXIT_FAILURE);//读到的数据不能超过缓冲区nleft -= nread;ret = readn(sockfd, bufp, nread);if (ret != nread)exit(EXIT_FAILURE);bufp += nread;}return -1;}void do_service(int connfd){char recvbuf[1024];int n;while (1){memset(&recvbuf, 0, sizeof(recvbuf));int ret = readline(connfd, recvbuf, 1204);if (ret == -1){ERR_EXIT("read");}else if (ret == 0){printf("client cloase\n");break;}fputs(recvbuf, stdout);writen(connfd, recvbuf, strlen(recvbuf));}}void handle_sigchild(int sig){// wait(NULL);while (waitpid(-1, NULL, WNOHANG) > 0);//回收所有子进程}int main(void){//signal(SIGCHLD, SIG_IGN);signal(SIGCHLD,handle_sigchild); int listenfd;if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)ERR_EXIT("socket");struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5188);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);int on = 1;if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)ERR_EXIT("setsockopt");if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)ERR_EXIT("bind");if (listen(listenfd, SOMAXCONN) < 0)ERR_EXIT("listten");while (1){int connfd;struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);if ((connfd = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)ERR_EXIT("accept");printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));pid_t pid = fork();if (pid == -1)ERR_EXIT("fork");if (pid == 0){close(listenfd);do_service(connfd);exit(EXIT_SUCCESS);}if (pid > 0){close(connfd);}}close(listenfd);}
但是使用wait会出现问题,wait是阻塞的,当多个子进程同时退出时,wait并不能等待所有的子进程退出,当它等待第一个子进程退出后,就返回了,这个时候就要用waitpid方法,如果只是waitpid并不能解决同时退出的问题,要使用while(waitpid()),保证回收所有退出的子进程。
- linux网络多进程编程处理僵尸进程
- LINUX僵尸进程处理
- 网络编程之僵尸进程
- linux 下处理僵尸进程
- Linux 僵尸进程的处理
- linux僵尸进程的处理
- linux下处理僵尸进程
- Linux系统编程----僵尸进程
- Linux网络编程(一)——消灭僵尸进程
- 【Linux编程】僵尸进程和孤儿进程
- Linux--进程--僵尸进程
- UNIX网络编程——僵尸进程
- 网络编程中服务器端避免僵尸进程
- socket网络编程避免僵尸进程
- linux编程---进程---孤儿进程与僵尸进程区别
- Linux下僵尸进程的处理
- linux下的僵尸进程处理办法
- linux系统处理僵尸进程的方法
- spring-boot第一次搭建使用
- Android-OpenGL视图碎片化动画解析
- mysqld加入服务的方式
- nginx 初识
- sprintf和snprintf的区别
- linux网络多进程编程处理僵尸进程
- 基本排序算法总结
- ASP.NET 教程
- Android Studio
- 火车票检测与提取矫正
- react native 顶部控件
- List接口常用实现类的特点和底层实现
- CF-(贪心)
- js data日期初始化的5种方法