Linux下僵尸进程的产生和解决方法
2018-03-15 23:00
931 查看
僵尸进程的产生
由下面一段cs架构的代码说明下,僵尸进程的产生,下面是一个简单的回射服务器,客户端负责从标准输入读入数据,写到服务端,服务端主进程监听连接套接字,fork一个子进程处理连接套接字,读入数据和回写给客户端。大写的函数只对出错的情况进行处理。客户端代码
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include "socketio.h" int main() { int sockfd; struct sockaddr_in servaddr; sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); servaddr.sin_port = htons(5566); Connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); char sendline[1024], recvline[1024]; memset(sendline, 0, sizeof(sendline)); memset(recvline, 0, sizeof(recvline)); while (fgets(sendline, sizeof(sendline), stdin) != NULL) { int nread = 0; int nwrite = 0; nwrite = write(sockfd, sendline, strlen(sendline)); if(nwrite < 0) { if (errno == EINTR) continue; perror("write"); } nread = read(sockfd, recvline, sizeof(recvline)); if(nread < 0) { if (errno == EINTR) continue; perror("read"); } if (nread == 0) { printf("server close\n"); // break; } fputs(recvline, stdout); memset(sendline, 0, sizeof(sendline)); memset(recvline, 0, sizeof(recvline)); } close(sockfd); return 0; }
服务端代码
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include "socketio.h" int main() { struct sockaddr_in servaddr, cliaddr; socklen_t clilen; pid_t pid; int listenfd, connfd; listenfd = Socket(AF_INET, SOCK_STREAM, 0); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(5566); int on = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); Bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr)); Listen(listenfd, 5); while(1) { clilen = sizeof(cliaddr); connfd = Accept(listenfd, (struct sockaddr*) &cliaddr, &clilen); if ((pid = fork()) == 0) { while(1) { int nread = 0; int nwrite = 0; char readbuf[1024]; memset(readbuf, 0, sizeof(readbuf)); close(listenfd); // nread = read(connfd, readbuf, sizeof(readbuf)); while((nread = read(connfd, readbuf, sizeof(readbuf))) < 0) { if (errno == EINTR) continue; perror("read"); } if (nread == 0) { printf("client close\n"); exit(0); } printf("nread: %d\n", nread); fputs(readbuf, stdout); nwrite = write(connfd, readbuf, strlen(readbuf)); while(nwrite < 0) { if (errno == EINTR) continue; perror("write"); } } } else if (pid < 0) { perror("fork"); exit(-1); } close(connfd); } return 0; }
第一次连接通信时,我们可以看到,并没有产生僵尸进程。
当客户端结束通信,服务端主进程不退出,服务端子进程输出client close并exit(0)退出时,可以看到僵尸进程产生了。
服务端一直没有退出,当客户端发起第二次连接通信时,我们可以看到,之前产生的进程僵尸进程依然残留。
客户端第二次结束通信,又产生了一个僵尸进程。
僵尸进程产生的原因是,服务器子进程终止时,会给父进程发送一个SIGCHLD信号,此时父进程的默认的处理是忽略此信号,所以产生了僵尸进程,只有等到服务端完全关闭时,才会自动的回收僵尸进程,但是服务端通常是长时间的工作,如果每处理一个连接就会留下一个僵尸进程的话将会耗费大量资源,所以我们需要捕获子进程终止时的信号,让系统杀死僵尸进程。
此时我们引入一个waitpid函数
pid_t waitpid(pid_t pid, int* statloc, int options); 成功时返回进程pid(大于零)
此函数用于处理僵尸进程。
还需要一个捕获信号的函数,表明当捕获到SIGCHLD类型的信号时调用sig_chld函数。
signal(SIGCHLD, sig_chld);
信号处理函数我们需要在循环地调用waitpid,原因是防止在进入信号处理函数的时候收到另外多个SIGCHLD信号,然而只被捕获一次或两次信号,从而只能处理1-2次信号。WNOHANG参数可以避免有尚未终止子进程运行时阻塞,这也是这里不用wait的原因。
void sig_chld() { pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); }
最终服务端的代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include "socketio.h"
void sig_chld() { pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); }
int main()
{
struct sockaddr_in servaddr, cliaddr;
socklen_t clilen;
pid_t pid;
int listenfd, connfd;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(5566);
int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
Bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr));
Listen(listenfd, 5);
signal(SIGCHLD, sig_chld);
while(1)
{
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr*) &cliaddr, &clilen);
if ((pid = fork()) == 0)
{
while(1)
{
int nread = 0;
int nwrite = 0;
char readbuf[1024];
memset(readbuf, 0, sizeof(readbuf));
close(listenfd);
// nread = read(connfd, readbuf, sizeof(readbuf));
while((nread = read(connfd, readbuf, sizeof(readbuf))) < 0)
{
if (errno == EINTR)
continue;
perror("read");
}
if (nread == 0)
{
printf("client close\n");
exit(0);
}
fputs(readbuf, stdout);
nwrite = write(connfd, readbuf, strlen(readbuf));
while(nwrite < 0)
{
if (errno == EINTR)
continue;
perror("write");
}
}
}
else if (pid < 0)
{
perror("fork");
exit(-1);
}
close(connfd);
}
}
最终结果,客户端结束通信,服务端子进程结束,主进程仍然在运行时,不产生僵尸进程。
相关文章推荐
- Linux 僵尸进程产生原因及解决方法
- Linux的僵尸进程产生原因及解决方法
- Linux的僵尸进程产生原因及解决方法
- linux下的僵尸进程产生原因和解决方法(含具体代码)
- (转)Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法(转)
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- linux的僵尸进程和孤儿进程及解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- Linux的僵尸进程及其解决方法
- 【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—基础知识篇
- linux 中 signal 用法回收子进程结束产生的僵尸进程
- 【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—实践篇
- Linux的僵尸进程解决攻略