[英]Killing zombie processes in Linux
我对僵尸进程有疑问。 当我从客户端关闭连接时,僵尸孩子不会死。 如果我从服务器端关闭连接,一切正常。 没有僵尸孩子。 我正在使用下面的代码
任何帮助?
#define SERV_PORT 1051
#define LISTENQ 1024
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return;
}
void *pThread_TCP(void *ptr)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
unsigned char pData[255];
unsigned short n;
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);
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "usb0");
ioctl(listenfd, SIOCGIFINDEX, &ifr);
setsockopt(listenfd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
signal(SIGCHLD, sig_chld); /* must call waitpid() */
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0)
{
if (errno == EINTR)
continue; /* back to for() */
}
if ( (childpid = fork()) == 0)
{ /* child process */
while(1)
{
n = recvfrom(connfd,pData,100,0,(struct sockaddr *)&cliaddr,&clilen);
if(n>0)
{
if(pData[0] == '^') break;
sendto(connfd,"OK\r\n",4,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
}
}
close(listenfd); /* close listening socket */
exit(0);
}
close(connfd); /* parent closes connected socket */
}
}
僵尸进程仅使用进程表中的条目。 内核对其进行维护,以允许父进程的wait(2)
系统调用(和家族)意识到实际上有一个等待的进程,并且在不wait()
子进程的情况下调用wait()
不会失败。 那些行尸走肉的进程可以确保内核数据的一致性,因此,您不能杀死它们(即使以root用户身份),确保活着的父母没有这堆僵尸的唯一方法是wait(2)
一个wait(2)
调用之前已经完成的每个fork()
(您根本不需要这样做)。 正如在您的代码中,线程将在关闭文件描述符后立即死亡,因此您有机会执行waitpid(pid_of_child, ...);
在那里,所以您将等待合适的孩子。 有关此系统调用的更多信息,请参见waitpid(2)
。 这种方法将有一个不可见的缺点(您的线程将持续到孩子死亡为止)。 这在进程中正常工作的原因wait()
在父进程中不需要做wait()
)是因为您不会死于父进程(在线程死后父进程仍存活),所以fork()/wait()
关系维持。 当父进程死亡时,内核将init( id == 1
进程)作为您进程的父进程,而init(8)
始终为系统中的孤立子进程设置wait(2)
。
通过在之后添加以下代码
...
close(connfd); /* parent closes connected socket */
int retcode; /* return code of child process */
waitpid(childpid, &retcode, 0);
} /* for loop */
或者,因为您不打算检查孩子是如何终止的
...
close(connfd); /* parent closes connected socket */
waitpid(childpid, 0, 0);
} /* for loop */
这还有另一个缺点,就是您要等待孩子终止,并且在孩子终止之前不会进入accept(2)
系统调用,这可能不是您想要的。 如果要避免创建子级僵尸进程,还有另一种选择(但它还有一些其他缺点)是在整个进程中忽略SIGCHLD
信号,这会使内核不创建那些僵尸进程(传统方法,还有其他方法)以避免造成僵尸儿童),或者您可以拥有一个新线程,使之成为所需的wait()
并在儿童死亡后将其返回的值分派到正确的位置。
您真的是说“僵尸进程”( ps
Z
状态)吗? 那些已经死了,但是还没有被父母收割。
SOCK_STREAM
套接字上, recvfrom
/ sendto
的地址参数无效,并且在某些实现中实际上使用NULL
/ 0以外的NULL
可能会失败。 pData[0]
是错误的。 发送"^A\\r\\n^B\\r\\n"
的客户端可以合法接收为"^"
后跟"A\\r\\n^B\\r\\n"
或"^A\\r\\n^"
然后是"B\\r\\n"
或其他任何拆分。 "OK\\r\\n"
可能会得到简短的写入。 recvfrom
返回<0
,就像在尝试从关闭套接字读取的错误状态那样,您将在while(1)
永远循环。 这不是僵尸,它是一个正在运行的进程,尽管有一个正在燃烧的CPU没用。 SIGCHLD
也不调用wait
/ waitpid
/ waitpid
这意味着当孩子退出时,他们不会被收割,这将导致实际的僵尸进程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.