繁体   English   中英

在Linux中杀死僵尸进程

[英]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可能会失败。
  • TCP不是基于消息的,因此检查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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM