在Linux中杀死僵尸进程

时间:2017-03-19 20:46:16

标签: linux process zombie-process

我遇到僵尸进程问题。当我从客户端关闭连接时,僵尸的孩子不会死。如果我从服务器端关闭连接,一切正常。没有僵尸的孩子。我使用下面的代码

任何帮助??

#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 */
    }
}

2 个答案:

答案 0 :(得分:1)

僵尸进程仅使用进程表中的条目。内核维护它以允许父进程&#39; wait(2)系统调用(和家人)要知道实际上有一个等待的进程,并且在没有子进程走动的情况下调用wait()并不会失败。那些行尸走肉的过程是为了确保内核数据的一致性,因此,你不能杀死它们(即使是root用户)确保一个活着的父母没有这一群僵尸的唯一方法就是做一个wait(2)要求之前已完成的每个fork()(您根本不做)。在你的代码中,线程在关闭文件描述符之后就会死掉,你有机会在那里做waitpid(pid_of_child, ...);,所以你要等到合适的孩子。有关此系统调用的详细信息,请参阅waitpid(2)。这种方法将有一个不可见的缺点(你的线程将持续到孩子死亡)。这对于进程正常工作(在父进程中不需要进行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() s并将孩子们返回的值发送到适当的位置,一旦它们死掉。

答案 1 :(得分:0)

你真的是说&#34;僵尸进程&#34; Z中的ps州?那些已经死了,但他们还没有被他们的父母收获。

  • recvfrom / sendto的地址参数在SOCK_STREAM套接字上无用,实际上使用NULL / 0以外的值可能会在某些实现上失败。
  • 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 /等。这意味着当孩子退出时,他们没有收获,这将导致实际的僵尸进程。