linux下出现大量僵尸进程,Linux下僵尸进程的产生和解决方法

僵尸进程的产生

由下面一段cs架构的代码说明下,僵尸进程的产生,下面是一个简单的回射服务器,客户端负责从标准输入读入数据,写到服务端,服务端主进程监听连接套接字,fork一个子进程处理连接套接字,读入数据和回写给客户端。大写的函数只对出错的情况进行处理。

客户端代码

#include

#include

#include

#include

#include

#include

#include

#include

#include

#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

#include

#include

#include

#include

#include

#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;

}

第一次连接通信时,我们可以看到,并没有产生僵尸进程。

linux下出现大量僵尸进程,Linux下僵尸进程的产生和解决方法_第1张图片

当客户端结束通信,服务端主进程不退出,服务端子进程输出client close并exit(0)退出时,可以看到僵尸进程产生了。

linux下出现大量僵尸进程,Linux下僵尸进程的产生和解决方法_第2张图片

服务端一直没有退出,当客户端发起第二次连接通信时,我们可以看到,之前产生的进程僵尸进程依然残留。

linux下出现大量僵尸进程,Linux下僵尸进程的产生和解决方法_第3张图片

客户端第二次结束通信,又产生了一个僵尸进程。

linux下出现大量僵尸进程,Linux下僵尸进程的产生和解决方法_第4张图片

僵尸进程产生的原因是,服务器子进程终止时,会给父进程发送一个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

#include

#include

#include

#include

#include

#include

#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下僵尸进程的产生和解决方法_第5张图片

你可能感兴趣的:(linux下出现大量僵尸进程)