Advanced Programming in UNIX Environment Episode 30

exit Function

As we described in Section 7.3,a process can terminate normally in five ways:

1.Executing a return from the main function.
2.Calling the exit function.
3.Calling the _exit or _Exit function.
4.Executing a return from the start routine of the last thread in the process.
5.Calling the pthread_exit function from the last thread in the process.

The three forms of abnormal termination are as follows:

1.Calling abort. This is a special case of the next item,as it generates the
SIGABRT signal.
2.When the process receives certain signals.
3.The last thread responds to a cancellation request.

Regardless of how a process terminates,the same code in the kernel is eventually executed. This kernel code closes all the open descriptors for the process,releases the memory that it was using,and so on.

The final condition to consider is this: What happens when a process that has been inherited by init terminates? Does it become a zombie? The answer is ‘‘no,’’ because init is written so that whenever one of its children terminates,init calls one of the wait functions to fetch the termination status. By doing this,init prevents the system from being clogged by zombies.

wait and waitpid Functions

When a process terminates,either normally or abnormally,the kernel notifies the parent by sending the SIGCHLD signal to the parent. Because the termination of a child is an asynchronous event—it can happen at any time while the parent is running — this signal is the asynchronous notification from the kernel to the parent.

For now,we need to be aware that a process that calls wait or waitpid can

  • Block,if all of its children are still running
  • Return immediately with the termination status of a child,if a child has terminated and is waiting for its termination status to be fetched
  • Return immediately with an error,if it doesn’t have any child processes

If the process is calling wait because it received the SIGCHLD signal,we expect wait to return immediately. But if we call it at any random point in time,it can block.

#include <sys/wait.h>

pid_t wait(int *statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);

The differences between these two functions are as follows:

  • The wait function can block the caller until a child process terminates,whereas waitpid has an option that prevents it from blocking.
  • The waitpid function doesn’t wait for the child that terminates first; it has a number of options that control which process it waits for.
#include "apue.h"
#include <sys/wait.h>

void pr_exit(int status)
{
    if(WIFEXITED(status))
        printf("normal termination,exit status = %d\n",WEXITSTATUS(status));
    else if(WIFSIGNAL(status))
        printf("abnormal termination,signal number = %d%s\n",WTERMSIG(status),#ifdef WCOREDUMP
            WCOREDUMP(status)?" (core file generated)" : "");
#else
            "");
#endif
    else if(WIFSTOPPED(status))
        printf("child stopped,signal number=%d\n",WSTOPSIG(status));
}

Print a description of the exit status

FreeBSD 8.0,Linux 3.2.0,Mac OS X 10.6.8,and Solaris 10 all support the WCOREDUMP macro. However,some platforms hide its definition if the _POSIX_C_SOURCE constant is defined

#include "apue.h"
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    int status;

    if((pid=fork())<0)
        err_sys("fork error");
    else if(pid==0)
        exit(7);

    if(wait(&status)!=pid)
        err_sys("wait error");
    pr_exit(status);

    if((pid==fork())<0)
        err_sys("fork error");
    else if(pid==0)
        abort();

    if(wait(&status)!=pid)
        err_sys("wait error");
    pr_exit(status);

    if((pid=fork())<0)
        err_sys("fork error");
    else if(pid==0)
        status/=0;

    if(wait(&status)!=pid)
        err_sys("wait error");
    pr_exit(status);

    return 0;
}

Demonstrate various exit statuses

The interpretation of the pid argument for waitpid depends on its value:

pid == −1 Waits for any child process. In this respect,waitpid is equivalent to wait.
pid >0 Waits for the child whose process ID equals pid.
pid ==0 Waits for any child whose process group ID equals that of the calling process.
pid < −1 Waits for any child whose process group ID equals the absolute value of pid.

The waitpid function provides three features that aren’t provided by the wait function.

1.The waitpid function lets us wait for one particular process,whereas the wait function returns the status of any terminated child. We’ll return to this feature when we discuss the popen function.
2.The waitpid function provides a nonblocking version of wait. There are times when we want to fetch a child’s status,but we don’t want to block.
3.The waitpid function provides support for job control with the WUNTRACED and WCONTINUED options.

#include "apue.h"
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    if((pid==fork())<0)
    {
        err_sys("fork error");
    }
    else if(pid==0)
    {
        if((pid=fork())<0)
            err_sys("fork error");
        else if(pid>0)
            exit(0);

        sleep(2);
        printf("second child,parent pid=%ld\n",(long)getppid());
        exit(0);
    }

    if(waitpid(pid,NULL,0)!=pid)
        err_sys("waitpid error");

    return 0;
}

Avoid zombie processes by calling fork twice

waitid Function

The Single UNIX Specification includes an additional function to retrieve the exit status of a process. The waitid function is similar to waitpid,but provides extra flexibility.

#include <sys/wait.h>
int waitid(idtype_t idtype,id_t id,siginfo_t *infop,int options);

The infop argument is a pointer to a siginfo structure. This structure contains detailed information about the signal generated that caused the state change in the child process. The siginfo structure is discussed further in Section 10.14.

Of the four platforms covered in this book,only Linux 3.2.0,and Solaris 10 provide support for waitid. Note,however,that Mac OS X 10.6.8 doesn’t set all the information we expect in the siginfo structure.

wait3 and wait4 Functions

Historically,these two variants descend from the BSD branch of the UNIX System. The only feature provided by these two functions that isn’t provided by the wait,waitid,and waitpid functions is an additional argument that allows the kernel to return a summary of the resources used by the terminated process and all its child processes.

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
pid_t wait3(int *statloc,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *statloc,struct rusage *rusage);

Race Conditions

For our purposes,a race condition occurs when multiple processes are trying to do something with shared data and the final outcome depends on the order in which the processes run. The fork function is a lively breeding ground for race conditions,if any of the logic after the fork either explicitly or implicitly depends on whether the parent or child runs first after the fork.

If the second child runs before the first child,then its parent process will be the first child. But if the first child runs first and has enough time to exit,then the parent process of the second child is init. Even calling sleep,as we did,guarantees nothing. If the system was heavily loaded,the second child could resume after sleep returns,before the first child has a chance to run.

For a parent and child relationship,we often have the following scenario. After the fork,both the parent and the child have something to do. For example,the parent could update a record in a log file with the child’s process ID,and the child might have to create a file for the parent. In this example,we require that each process tell the other when it has finished its initial set of operations,and that each wait for the other to complete,before heading off on its own.

The five routines TELL_WAIT,TELL_PARENT,TELL_CHILD,WAIT_PARENT,and WAIT_CHILD can be either macros or functions.

#include "apue.h"

static void charatatime(char *);

int main(void)
{
    pid_t pid;

    if((pid=fork())<0)
    {
        err_sys("fork error");
    }
    else if(pid==0)
    {
        charatatime("output from child\n");
    }
    else
    {
        charatatime("output from parent\n");
    }

    return 0;
}

static void charatatime(char *str)
{
    char *ptr;
    int c;

    setbuf(stdout,NULL);
    for(ptr=str;(c=*ptr++)!=0;)
    {
        putc(c,stdout);
    }
}

Program with a race condition

We need to change the program in Figure 8.12 to use the TELL and WAIT functions.

#include "apue.h"

static void charatatime(char *);

int main(void)
{
    pid_t pid;
    TELL_WAIT();

    if((pid=fork())<0)
    {
        err_sys("fork error");
    }
    else if(pid==0)
    {
        WAIT_PARENT();
        charatatime("output from child\n");
    }
    else
    {
        charatatime("output from parent\n");
        TELL_CHILD(pid);
    }

    return 0;
}

static void charatatime(char *str)
{
    char *ptr;
    int c;

    setbuf(stdout,stdout);
    }
}

Modification of Figure 8.12 to avoid race condition

相关文章

用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2...
#!/bin/bashcommand1&command2&wait从Shell脚本并行...
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/ph...
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如...
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexa...
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全...