zoukankan      html  css  js  c++  java
  • 父子进程信号处理问题

    As we know , we can use :

    void (*signal(int sig, void (*func)(int)))(int);

    to set handler when our process have receive a signal. 

    signal table : we can get them by  man 7 signal

    First the signals described in the original POSIX.1-1990 standard.

    Signal Value Action Comment
    ──────────────────────────────────────────────────────────────────────
    SIGHUP 1 Term Hangup detected on controlling terminal
    or death of controlling process
    SIGINT 2 Term Interrupt from keyboard
    SIGQUIT 3 Core Quit from keyboard
    SIGILL 4 Core Illegal Instruction
    SIGABRT 6 Core Abort signal from abort(3)
    SIGFPE 8 Core Floating point exception
    SIGKILL 9 Term Kill signal
    SIGSEGV 11 Core Invalid memory reference
    SIGPIPE 13 Term Broken pipe: write to pipe with no
    readers
    SIGALRM 14 Term Timer signal from alarm(2)
    SIGTERM 15 Term Termination signal
    SIGUSR1 30,10,16 Term User-defined signal 1
    SIGUSR2 31,12,17 Term User-defined signal 2
    SIGCHLD 20,17,18 Ign Child stopped or terminated
    SIGCONT 19,18,25 Cont Continue if stopped
    SIGSTOP 17,19,23 Stop Stop process
    SIGTSTP 18,20,24 Stop Stop typed at tty
    SIGTTIN 21,21,26 Stop tty input for background process
    SIGTTOU 22,22,27 Stop tty output for background process

    There are some notes we should pay attention to when programming:

    • avoid zomie process
    • avoid signal was ignore when concurrent server run

    so let's take a look at some local program:

    #include <signal.h>
    #include <errno.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    void unix_error(const char *msg)
    {
        fprintf(stderr, "%s: %s\n", msg, strerror(errno));
        exit(0);
    }
    //this will generate a zombie process 
    void handler2(int sig)
    {
        pid_t pid;
        if((pid=waitpid(-1, NULL, 0))>0)
            printf("Handler reped child %d\n", (int)pid);
        else
            unix_error("waitpid error");
        sleep(2);
        return;
    }
    //this will process all signal when process the same signal
    void handler2(int sig)
    {
        pid_t pid;
        while((pid=waitpid(-1, NULL, 0))>0)
            printf("Handler reped child %d\n", (int)pid);
        if(errno!=ECHILD)
            unix_error("waitpid error");
        sleep(2);
        return;
    }
    #define MAXBUF 256
    int main(void)
    {
        int i, n;
        char buf[MAXBUF];
        if(signal(SIGCHLD, handler2)==SIG_ERR)
            unix_error("signal error");
        
        // parent creates children
        for(i = 0; i<3; i++)
        {
            if(fork()==0)
            {
                printf("Hello from child %d\n", (int)getpid());
                sleep(1);
                exit(0);
            }
        }
        while((n=read(STDIN_FILENO, buf, sizeof(buf)))<0)
            if(errno!=EINTR)
                unix_error("read error");
        
        printf("parent processing input\n");
        //when running in this loop , check ps to see zombie process <defunct>
        while(1)
            ;
        
        printf("bye\n");//will never run
        exit(0);
    
    }

    there are two handler2 function , one  will generate a zombie process ,while the other will not. 

    At the first case , we can see zombie process when we run in while(1);

    [dengwei@localhost signal]$ ps -a
    PID TTY TIME CMD
    5913 pts/5 00:00:39 python
    11774 pts/8 00:00:08 sc
    11777 pts/8 00:00:00 sc <defunct>

    That's because 

    when we handling SIGCHLD signal in handler2 function for the first time, we sleep for 2 sec. At the same time , another SIGCHLD was sent , but as it was the same SIGCHLD signal , then the second signal was discarded.

     

    this is ref from <Computer System: A Programmer's Perspective>: 

    • Pending signals can be blocked. Unix signal handlers typically block pending signals of the type currently being processed by the handler. For example, suppose a process has caught a SIGINT signal and is currently running its SIGINT handler. If another SIGINT signal is sent to the process, then the SIGINT will become pending, but will not be received until after the handler returns.
    • Pending signals are not queued. There can be at most one pending signal of any particular type. Thus, if two signals of type are sent to a destination process while signal is blocked because the destination process is currently executing a handler for signal , then the second signal is simply discarded; it is not queued. The key idea is that the existence of a pending signal merely indicates that at least one signal has arrived.
    • System calls can be interrupted. System calls such as read, wait, and accept that can potentially block the process for a long period of time are called slow system calls. On some systems, slow system calls that are interrupted when a handler catches a signal do not resume when the signal handler returns, but instead return immediately to the user with an error condition and errno set to EINTR.

    so , the same theory:

    Let's see our concurrent server program with feature that accept each connection and fork a child to handler the client's requiration:

    #include    "unp.h"
    
    int
    main(int argc, char **argv)
    {
        int                    listenfd, connfd;
        pid_t                childpid;
        socklen_t            clilen;
        struct sockaddr_in    cliaddr, servaddr;
        void                sig_chld(int);
    
        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);
        int on = 1;
        Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
        Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
        Listen(listenfd, LISTENQ);
    
        Signal(SIGCHLD, sig_chld);    /* must call waitpid() */
    
        for ( ; ; ) {
            clilen = sizeof(cliaddr);
            if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
                if (errno == EINTR)
                    continue;        /* back to for() */
                else
                    err_sys("accept error");
            }
    
            if ( (childpid = Fork()) == 0) {    /* child process */
                Close(listenfd);    /* close listening socket */
                str_echo(connfd);    /* process the request */
                exit(0);
            }
            Close(connfd);            /* parent closes connected socket */
        }
    }
    #include    "unp.h"
    
    void
    sig_chld(int signo)
    {
        pid_t    pid;
        int        stat;
    
        while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
            printf("child %d terminated\n", pid);
        return;
    }

    Pay attention to the following:

    if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
                if (errno == EINTR)
    //we handler slow system call

     Signal(SIGCHLD, sig_chld);    /* must call waitpid() */

    // got all children end signal
    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
            printf("child %d terminated\n", pid);

    The waitpid() system call suspends execution of the calling process
    until a child specified by pid argument has changed state. By default,
    waitpid() waits only for terminated children, but this behavior is mod‐
    ifiable via the options argument, as described below.

    The value of pid can be:

    < -1 meaning wait for any child process whose process group ID is
    equal to the absolute value of pid.

    -1 meaning wait for any child process.

    0 meaning wait for any child process whose process group ID is
    equal to that of the calling process.

    > 0 meaning wait for the child whose process ID is equal to the
    value of pid.

    The value of options is an OR of zero or more of the following con‐
    stants:

    WNOHANG return immediately if no child has exited.

  • 相关阅读:
    linux 安装 Chrome
    J2EE版本
    Java 源码解析之局部变量检查
    /etc/xinetd.conf 和 /etc/xinetd.d/*【新网络服务配置】
    Linux 内核编译
    linux 汇编
    /etc/ethers【地址映射】
    Linux LAMP 搭建
    Linux ftp 使用
    linux apache
  • 原文地址:https://www.cnblogs.com/no7dw/p/2881212.html
Copyright © 2011-2022 走看看