Thursday, February 26, 2009

Handling SIGCHLD Signals

The purpose of the zombie state is to maintain information about the child for the parent to fetch at some later time. This information includes the process ID of the child, its termination status, and information on the resource utilization of the child (CPU time, memory, etc.). If a process terminates, and that process has children in the zombie state, the parent process ID of all the zombie children is set to 1 (the init process), which will inherit the children and clean them up (i.e., init will wait for them, which removes the zombie). Some Unix systems show the COMMAND column for a zombie process as <defunct>.

Handling Zombies

Obviously we do not want to leave zombies around. They take up space in the kernel and eventually we can run out of processes. Whenever we fork children, we must wait for them to prevent them from becoming zombies. To do this, we establish a signal handler to catch SIGCHLD, and within the handler, we call wait.

We establish the signal handler by adding the function call

Signal (SIGCHLD, sig_chld);

1 #include     "unp.h"
2 void
3 sig_chld(int signo)
4 {
5     pid_t   pid;
6     int     stat;

7     pid = wait(&stat);
8     printf("child %d terminated\", pid);
9     return;
10 }

Warning: Calling standard I/O functions such as printf in a signal handler is not recommended. We call printf here as a diagnostic tool to see when the child terminates.

Under System V and Unix 98, the child of a process does not become a zombie if the process sets the disposition of SIGCHLD to SIG_IGN. Unfortunately, this works only under System V and Unix 98. POSIX explicitly states that this behavior is unspecified. The portable way to handle zombies is to catch SIGCHLD and call wait or waitpid.

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

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

    Listen(listenfd, LISTENQ);

    Signal(SIGCHLD, sig_chld);

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

solaris % tcpserv02 & // start server in background

[2] 16939

solaris % tcpcli01 127.0.0.1 // then start client in foreground

hi there // we type this

hi there // and this is echoed

^D       // we type our EOF character

child 16942 terminated // output by printf in signal handler

accept error: Interrupted system call // main function aborts

The sequence of steps is as follows:




  1. We terminate the client by typing our EOF character. The client TCP sends a FIN to the server and the server responds with an ACK.



  2. The receipt of the FIN delivers an EOF to the child's pending readline. The child terminates.



  3. The parent is blocked in its call to accept when the SIGCHLD signal is delivered. The sig_chld function executes (our signal handler), wait fetches the child's PID and termination status, and printf is called from the signal handler. The signal handler returns.



  4. Since the signal was caught by the parent while the parent was blocked in a slow system call (accept), the kernel causes the accept to return an error of EINTR (interrupted system call). The parent does not handle this error, so it aborts.


The purpose of this example is to show that when writing network programs that catch signals, we must be cognizant of interrupted system calls, and we must handle them. In this specific example, running under Solaris 9, the signal function provided in the standard C library does not cause an interrupted system call to be automatically restarted by the kernel. That is, the SA_RESTART flag that we set is not set by the signal function in the system library. Some other systems automatically restart the interrupted system call. If we run the same example under 4.4BSD, using its library version of the signal function, the kernel restarts the interrupted system call and accept does not return an error. To handle this potential problem between different operating systems is one reason we define our own version of the signal function that we use throughout the text.

As part of the coding conventions used in this text, we always code an explicit return in our signal handlers, even though falling off the end of the function does the same thing for a function returning void. When reading the code, the unnecessary return statement acts as a reminder that the return may interrupt a system call.

Handling Interrupted System Calls


We used the term "slow system call" to describe accept, and we use this term for any system call that can block forever. That is, the system call need never return. Most networking functions fall into this category. For example, there is no guarantee that a server's call to accept will ever return, if there are no clients that will connect to the server. Similarly, our server's call to read will never return if the client never sends a line for the server to echo. Other examples of slow system calls are reads and writes of pipes and terminal devices. A notable exception is disk I/O, which usually returns to the caller (assuming no catastrophic hardware failure).

The basic rule that applies here is that when a process is blocked in a slow system call and the process catches a signal and the signal handler returns, the system call can return an error of EINTR. Some kernels automatically restart some interrupted system calls. For portability, when we write a program that catches signals (most concurrent servers catch SIGCHLD), we must be prepared for slow system calls to return EINTR. Portability problems are caused by the qualifiers "can" and "some," which were used earlier, and the fact that support for the POSIX SA_RESTART flag is optional. Even if an implementation supports the SA_RESTART flag, not all interrupted system calls may automatically be restarted. Most Berkeley-derived implementations, for example, never automatically restart select, and some of these implementations never restart accept or recvfrom.

To handle an interrupted accept, we change the call to accept, the beginning of the for loop, to the following:

     for ( ; ; ) {
clilen = sizeof (cliaddr);
if ( (connfd = accept (listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for () */
else
err_sys ("accept error");
}

Notice that we call accept and not our wrapper function Accept, since we must handle the failure of the function ourselves.

What we are doing in this piece of code is restarting the interrupted system call. This is fine for accept, along with functions such as read, write, select, and open. But there is one function that we cannot restart: connect. If this function returns EINTR, we cannot call it again, as doing so will return an immediate error. When connect is interrupted by a caught signal and is not automatically restarted, we must call select to wait for the connection to complete.


    POSIX Signal Handling

    A signal is a notification to a process that an event has occurred. Signals are sometimes called software interrupts. Signals usually occur asynchronously. By this we mean that a process doesn't know ahead of time exactly when a signal will occur.

    Signals can be sent

    • By one process to another process (or to itself)

    • By the kernel to a process

    The SIGCHLD signal that we described at the end of the previous section is one that is sent by the kernel whenever a process terminates, to the parent of the terminating process.

    Every signal has a disposition, which is also called the action associated with the signal. We set the disposition of a signal by calling the sigaction function (described shortly) and we have three choices for the disposition:

    1. We can provide a function that is called whenever a specific signal occurs. This function is called a signal handler and this action is called catching a signal. The two signals SIGKILL and SIGSTOP cannot be caught. Our function is called with a single integer argument that is the signal number and the function returns nothing. Its function prototype is therefore

      void handler (int signo);

      For most signals, calling sigaction and specifying a function to be called when the signal occurs is all that is required to catch a signal. But we will see later that a few signals, SIGIO, SIGPOLL, and SIGURG, all require additional actions on the part of the process to catch the signal.



    2. We can ignore a signal by setting its disposition to SIG_IGN. The two signals SIGKILL and SIGSTOP cannot be ignored.



    3. We can set the default disposition for a signal by setting its disposition to SIG_DFL. The default is normally to terminate a process on receipt of a signal, with certain signals also generating a core image of the process in its current working directory. There are a few signals whose default disposition is to be ignored: SIGCHLD and SIGURG (sent on the arrival of out-of-band data) are two that we will encounter in this text.


    signal Function


    The POSIX way to establish the disposition of a signal is to call the sigaction function. This gets complicated, however, as one argument to the function is a structure that we must allocate and fill in. An easier way to set the disposition of a signal is to call the signal function. The first argument is the signal name and the second argument is either a pointer to a function or one of the constants SIG_IGN or SIG_DFL. But, signal is an historical function that predates POSIX. Different implementations provide different signal semantics when it is called, providing backward compatibility, whereas POSIX explicitly spells out the semantics when sigaction is called. The solution is to define our own function named signal that just calls the POSIX sigaction function. This provides a simple interface with the desired POSIX semantics. We include this function in our own library, along with our err_XXX functions and our wrapper functions, for example, that we specify when building any of our programs in this text.

    1 #include    "unp.h"

    2 Sigfunc *
    3 signal (int signo, Sigfunc *func)
    4 {
    5     struct sigaction act, oact;

    6     act.sa_handler = func;
    7     sigemptyset (&act.sa_mask);
    8     act.sa_flags = 0;
    9     if (signo == SIGALRM) {
    10 #ifdef  SA_INTERRUPT
    11         act.sa_flags |= SA_INTERRUPT;     /* SunOS 4.x */
    12 #endif
    13     } else {
    14 #ifdef  SA_RESTART
    15         act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */
    16 #endif
    17     }
    18     if (sigaction (signo, &act, &oact) < 0)
    19         return (SIG_ERR);
    20     return (oact.sa_handler);
    21 }

    Sigfunc *
    Signal(int signo, Sigfunc *func)    /* for our signal() function */
    {
        Sigfunc    *sigfunc;

        if ( (sigfunc = signal(signo, func)) == SIG_ERR)
            err_sys("signal error");
        return(sigfunc);
    }

    #define SIG_DFL (void (*)(int))0
    #define SIG_IGN (void (*)(int))1
    #define SIG_ERR (void (*)(int))-1

    Simplify function prototype using typedef


    2–3 The normal function prototype for signal is complicated by the level of nested parentheses.

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

    To simplify this, we define the Sigfunc type in our unp.h header as

    typedef    void    Sigfunc(int);

    stating that signal handlers are functions with an integer argument and the function returns nothing (void). The function prototype then becomes

    Sigfunc *signal (int signo, Sigfunc *func);

    A pointer to a signal handling function is the second argument to the function, as well as the return value from the function.


    Set handler


    6 The sa_handler member of the sigaction structure is set to the func argument.


    Set signal mask for handler


    7 POSIX allows us to specify a set of signals that will be blocked when our signal handler is called. Any signal that is blocked cannot be delivered to a process. We set the sa_mask member to the empty set, which means that no additional signals will be blocked while our signal handler is running. POSIX guarantees that the signal being caught is always blocked while its handler is executing.


    Set SA_RESTART flag


    8–17 SA_RESTART is an optional flag. When the flag is set, a system call interrupted by this signal will be automatically restarted by the kernel. (We will talk more about interrupted system calls in the next section when we continue our example.) If the signal being caught is not SIGALRM, we specify the SA_RESTART flag, if defined. (The reason for making a special case for SIGALRM is that the purpose of generating this signal is normally to place a timeout on an I/O operation, in which case, we want the blocked system call to be interrupted by the signal.) Some older systems, notably SunOS 4.x, automatically restart an interrupted system call by default and then define the complement of this flag as SA_INTERRUPT. If this flag is defined, we set it if the signal being caught is SIGALRM.


    Call sigaction


    18–20 We call sigaction and then return the old action for the signal as the return value of the signal function.


    POSIX Signal Semantics


    We summarize the following points about signal handling on a POSIX-compliant system:



    • Once a signal handler is installed, it remains installed. (Older systems removed the signal handler each time it was executed.)



    • While a signal handler is executing, the signal being delivered is blocked. Furthermore, any additional signals that were specified in the sa_mask signal set passed to sigaction when the handler was installed are also blocked. We set sa_mask to the empty set, meaning no additional signals are blocked other than the signal being caught.



    • If a signal is generated one or more times while it is blocked, it is normally delivered only one time after the signal is unblocked. That is, by default, Unix signals are not queued. We will see an example of this in the next section. The POSIX real-time standard, 1003.1b, defines some reliable signals that are queued, but we do not use them in this text.



    • It is possible to selectively block and unblock a set of signals using the sigprocmask function. This lets us protect a critical region of code by preventing certain signals from being caught while that region of code is executing.

    a·pos·tate ~ ap·ti·tude

    a·pos·tate, n. 배교자, 배신자;변절자, 탈당자
    a. 배교의;배신[변절]
    ap·o·stat·ic, -i·cal[] a.

    Because he switched from one party to another, his former friends shunned him as an apostate.

    a·pos·ta·syn. (pl. -sies) [U.C] 배교(背敎), 배신(背信);변절, 탈당

    a charge of apostasy

    a·poth·e·car·yn. (pl.-car·ies) 《·고어약종상, 약제사(druggist);《약국

    In the apothecaries' weight, twelve ounces equal one pound.

    ap·o·thegmn. 경구, 격언

    Proverbs are apthegms that have become familiar sayings.

    a·poth·e·o·sis, n. (pl. -ses[])
    1 [U.C] (
    사람을) 으로 심, 신격화;신성시, 숭배;신격화
    2 이상(理想)의 상(像), 극치;권화(權化) 《of
    3 (
    사람·물건의) 승천(昇天)

    The Oriental in Bangkok is the apotheosis of the grand hotel.

    The apotheosis of a Roman emperor was designed to insure his eternal greatness.

    ap·pa·ri·tionn.
    1 유령, 망령, 환영
    2 경이적인[불가사의한]
    3 (모습 의) 출현
    4천문】 (혜성 의, 특히 정기적인) 출현
    appar v

    It could be some sort of a hallucination in the mind of the fantasy-prone person, or there could be some apparition that's explained by physics here.
    I've never seen the apparition of the little girl, but she does pull a little prank of sleeping on this bed behind us, and she leaves the impression of her body on the bed. 

    Hamlet was uncertain about the identity of the apparition that had appeared and spoken to him.

    ap·pease〕〔OF평화(peace)롭게 하다 에서〕 vt.
    1 <사람을> 달래다;<싸움을> 진정시키다;<노염·슬픔을> 가라앉히다
    The sight appeased his anger. 광경보고 그는 화가 가라앉았다.
    appease+++appease a person by kindness[with a present] 친절[선물] 달래다
    2 <갈증을> 풀어 주다;<식욕·호기심 을> 충족시키다
    appease one’s hunger[curiosity] 허기[호기심] 채우다
    3 (절개굽혀) 양보하다
    appease·ment n. 위무, 진정, 완화, 양보; 유화 정책 ap·pas·er n.

    Gandhi was accused by some of trying to appease both factions of the electorate.

    We have discovered that, when we try to appease our enemies, they make additional demands. 

    ap·pel·la·tionn. 명칭, 통칭, 호칭, 명명, epithet(나쁜의미)

    He earned the appellation `rebel priest.'

    He was amazed when the witches hailed him with his correct appellation.

    ap·pendvt.
    1 덧붙이다, 부가[추가]하다, 부록달다to
    I append Mr. As letter herewith. 여기A씨의 편지첨부합니다.
    append+++append a label to a trunk 트렁크꼬리표붙이다
    2 <펜던트 을> 달다, 매달다to
    3 <서명·도장을> (문서에) 쓰다, 찍다(affix) 《to

    Violet appended a note at the end of the letter.

    It was a relief that his real name hadn't been appended to the manuscript. 

    I shall append this chart to my report.

    ap·praisevt. <물건·재산을> 매기다, 견적[감정]하다;<사람·능력을> 평가하다(⇒ estimate [유의어]);… (치수·중량·품질 을) 평가[계산]하다
    I had an expert appraise the house beforehand. 사전전문가에게 가옥평가시켰다.
    appraise+++appraise property[land] at fifty thousand dollars 재산[토지] 5만 달러평가하다
    appraise property for taxation 과세하기 위해 재산감정하다
    ap·pris·ee n. appraise·ment n. ap·pris·ive a.
    apprisal n.

    It is difficult to appraise the value of old paintings; it is easier to call them priceless.

    ap·por·tionvt. 배분하다, 할당하다 《to
    apportion+++apportion something between[among] persons 물건사람들에게 배분하다
    apportion·a·ble a. apportion·er n.

    I'll apportion each of you different task.

    ap·pre·hendprihnd〕〔L파악하다」 에서〕 vt.
    1문어》 <범인을> 체포하다 《★ catch, seize, arrest일반적》
    The thief was apprehended. 도둑체포되었다.
    2 <의미를> 파악하다, 이해하다, 깨닫다
    apprehend+that I apprehended that the situation was serious. 사태각함을 깨달았다.
    3 우려하다, 염려하다(fear)
    It is apprehended that ... … 우려있다
    There is nothing to apprehend. 아무것염려 없다.
    vi.
    1 깨닫다, 이해되다
    2 걱정되다, 염려되다
    apprehend·er n.
    apprehnsion n.;apprehnsive a.

    The police will apprehend the culprit before long.

    ap·pre·hen·sivea.
    1 우려하는, 염려하는 《of, for, about
    2 이해빠른, 총명한(intelligent)
    3 알아채고[깨닫고] (있는) 《of
    be apprehensive for (a persons safety) (… 안부) 걱정하다

    His apprehensive glances at the people who were walking in the street revealed his nervousness.

    People are still terribly apprehensive about the future.

    ap·prise1, ap·prize1vt. 《문어》 <사람에게 …> 통지하다(inform), 알리다of

    When he was apprised of the dangerous weather conditions, he decided to postpone his trip. 

    Have customers been fully apprised of the advantages?

    We must apprise them of the dangers that may be involved.

    ap·pur·te·nancen.
    1 [
    보통 pl.] 부속물;[pl.] 기계 장치
    2종물(從物)

    a house and all its appurtenances

    He bought the estate and all its appurtenances.

    ap·ti·tuden.
    1 (…에의) 경향, 습성to》;(…하는) 기질, 성질for doing, to do
    2 (…) 소질, 재능, 능력, 수완;(학문·공부의) 총명함, 지력for, in
    3 적성, 울림(fitness) 《for
    have an aptitude for 소질[재능] 있다
    have an aptitude to vices () 물들기 쉽다
    ap·ti·tu·di·nal[] a. p·ti·tdi·nal·ly[] ad.
    pt a.

    SAT [Scholastic Aptitude Test]

    The counselor gave him an aptitude test before advising him about the career he should follow.

    An aptitude for computing is beneficial for students taking this degree.

    e·pit·o·me〕〔Gk요약하다」 에서〕 n.
    1 발췌(拔萃), 개요;대요, 개략
    2비유》 (…) 축도(縮圖)
    man, the worlds epitome 세계축도인간
    in epitome 요약형태로;축도화되어

    Maureen was the epitome of sophistication.

    prank1

    1 농담, 희롱, (짓궂은) 장난(mischief);간계(奸計)
    2 (기계 등의) 부정확한 작동;(동물의) 익살스런 동작

    prank2

    prank2 vt. , vi. 화려하게 차려 입다 《with》, 모양내다 《out, up》

    play pranks on[upon]
    …을 조롱하다

    Stop playing pranks on me.

    a harmless prank
    The orchard is now pranked with blossoms.
    Don´t make such a fuss all the time about kids´ pranks.

    hal·lu·ci·na·tionn. [U.C] 환각(cf. ILLUSION); 환상;잘못된 생각[신념, 신앙, 인상];곡두, 망상(delusion), 착각
    suffer from hallucinations 환각시달리다
    be under hallucination 착각하고 있다
    hallucination·al a.hal·lu·ci·na·tive[, ] a.

    The drug induces hallucinations at high doses.

    shun

    1 피하다, 멀리하다
    2 《고어》 면하다
    3 《고어》 막다

    'shun

    'shun〔〕〔attention에서〕 int. 차렷

    acquaintances politely shunned by one another.

    shun society.

    He tried to shun contact with us.

    He is kept at a respectful distance. or He is politely shunned by everybody.

    The electronics-parts sector is being shunned by investors, who fear computer.

    e·lec·tor·aten.
    1 [the electorate;집합적] 선거민 (전체), 유권자 (전원)
    2역사】 (신성 로마 제국의) 선거후(侯) 지위[관할, 영토]

    the power of the unwashed electorate

    He has the backing of almost a quarter of the electorate.

    reb·el〕〔L전쟁다시 하다 에서〕 n.
    1 반역자, 반도, 반항자 《against, to
    2 [종종 Rebel] 《반란군 병사남북 전쟁 병사》;[종종 Rebel] 《·구어남부 백인
    a. 반역
    the rebel army 반란군
    [] vi.  (rebelled;rebel·ling)
    1 모반[반역]하다, 반란일으키다against》;(권위·관습 등에) 반대하다, 반항하다 《against
    rebel against all authority 모든 권위대항하다
    2 화합하지 않다with》;반감가지다, 반발하다, 몸서리치다 《against, at
    rebel++My mind rebels at the thought. 그것생각하면 싹해진다.
    rebel·lke a.
    rebllion n.;rebllious a.

    Sudan's government says it is ready to accept a proposed peace deal with Darfur rebels, but rebel negotiators are holding out, saying the draft agreement still does not meet their key demands. 

    Sri Lanka has stopped bombing Tamil rebel targets and re-opened borders with rebel territory.