Linux – process control (process wait)

Necessity of process waiting

As mentioned before, if the child process exits, if the parent process is ignored, it may cause the problem of a “zombie process” and cause a memory leak.

In addition, once a process becomes a zombie, it is invulnerable, and the “kill without blinking” kill -9 can do nothing, because no one can kill a dead process. Finally, we need to know how the tasks assigned by the parent process to the child process are completed. For example, whether the child process finishes running, whether the result is correct or not, or whether it exits normally. The parent process reclaims the resources of the child process and obtains the exit information of the child process by means of process waiting

The method of process waiting

View through the man manual process

man 2 wait

wait() function

#include<sys/types.h>
#include <sys/wait.h>
pid_t wait(int*status);
return value:
 Successfully returns the pid of the process being waited for, and returns -1 on failure.
parameter:
 Output parameter, get the exit status of the child process, if you don't care, you can set it to NULL

Let’s test the wait() function

Next, observe the process status, first run

 while :; do ps axj | head -1 & amp; & amp; ps ajx | grep mytest | grep -v grep; sleep 1; echo "--------------- -"; done

Next execute the program

View the process status while executing the program

In this way, the phenomenon cannot be observed, and the code should be improved.

waitpid() function

pid_t waitpid(pid_t pid, int *status, int options);
return value:
    When returning normally, waitpid returns the process ID of the collected child process;
    If the option WNOHANG is set, and waitpid finds that there are no exited child processes to collect during the call, it returns 0;
    If there is an error in the call, -1 is returned, and errno will be set to the corresponding value to indicate the error;
parameter:
   pid:
     Pid=-1, wait for any child process. Equivalent to wait.
     Pid > 0. Wait for a child process whose process ID is equal to pid.
   status:
     WIFEXITED(status): True if this is the status returned by a normally terminated child process. (Check to see if the process exited normally)
     WEXITSTATUS(status): If WIFEXITED is non-zero, extract the exit code of the child process. (Check the exit code of the process)
   options:
      WNOHANG: If the child process specified by pid has not ended, the waitpid() function returns 0 and does not wait. If it ends normally, the ID of the child process is returned. 

1. If the child process has exited, when calling wait/waitpid, wait/waitpid will return immediately, release resources, and obtain the exit information of the child process.

2. If wait/waitpid is called at any time and the child process exists and is running normally, the process may be blocked.

3. If the child process does not exist, an error will be returned immediately.

Get subprocess status

Both wait and waitpid have a status parameter, which is an output parameter filled by the operating system.

If NULL is passed, it means that the exit status information of the child process is not concerned. Otherwise, the operating system will feed back the exit information of the child process to the parent process according to this parameter.

The status cannot be simply viewed as an integer, but can be viewed as a bitmap. The details are as follows (only the lower 16 bits of the status are studied):

The following is to get the exit code

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        int cnt = 5;
        while (cnt)
        {
            printf("I am a child process, I am still alive, I still have %dS, pid:%d, ppid:%d\
", cnt--, getpid(), getppid());
            sleep(1);
        }
        exit(107);
    }
    // parent process
    int status = 0;
    pid_t ret_id = waitpid(id, & status, 0);
    printf("I am the parent process, waiting for the success of the child process, pid:%d, ppid:%d, ret_id:%d, child exit code:%d\
", getpid(), getppid(), ret_id, (status >> 8) & 0xFF);
    return 0;
}

Where status>>8 & amp;0xff get the integer value

The following is to get the exception signal code

Get the abnormal signal status & amp; 0x7f to get the integer value

The exit code is 0, we create an exception, and check the exception signal

Let’s continue to change the program to make the program infinite loop

An infinite loop can also kill a process.

While the parent process is waiting for the child process, it can also do other things

If the child process does not exit, let the parent process do other things, write a program below to test it

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define TASK_NUM 10

typedef void (*func_t)(); // function pointer
func_t other_task[TASK_NUM] = {NULL};

// Preset a batch of tasks
void sync_disk()
{
    printf("This is a task to refresh data!\
");
}

void sync_log()
{
    printf("This is a synchronous log task!\
");
}

void net_send()
{
    printf("This is a network sending task!\
");
}

// Load the task function pointer into the task list

int LoadTask(func_t func)
{
    int i = 0;
    for (; i < TASK_NUM; i ++ )
    {
        if (other_task[i] == NULL)
            break;
    }
    if (i == TASK_NUM)
        return -1;
    else
        other_task[i] = func;
    return 0;
}

// Initialize task list
void InitTask()
{
    for (int i = 0; i < TASK_NUM; i ++ )
        other_task[i] = NULL;
    LoadTask(sync_disk);
    LoadTask(sync_log);
    LoadTask(net_send);
}

// Execute all tasks in the task list
void RunTask()
{
    for (int i = 0; i < TASK_NUM; i ++ )
    {
        if (other_task[i] == NULL)
            continue;
        other_task[i]();
    }
}

int main()
{
    // create child process
    pid_t id = fork();
    if (id == 0) // code executed by the child process
    {
        int cnt = 5;
        while (cnt)
        {
            printf("I am a child process, I am still alive, I still have %dS, pid:%d, ppid:%d\
", cnt--, getpid(), getppid());
            sleep(1);
        }
        exit(107); // The child process exits and returns 107
    }

    // code executed by the parent process
    InitTask(); // Initialize task list
    while (1)
    {
        int status = 0;
        pid_t ret_id = waitpid(id, & amp;status, WNOHANG); // Check the status of the child process
        if (ret_id < 0)
        {
            printf("waitpid error!\
");
            exit(1);
        }
        else if (ret_id == 0) // child process is still running
        {
            RunTask(); // Execute all tasks in the task list
            sleep(1);
            continue;
        }
        else // the child process has exited
        {
            printf("I am the parent process, waiting for the success of the child process, pid:%d, ppid:%d, ret_id:%d, child exit code:%d, child exit signal:%d\
", getpid(), getppid (), ret_id, (status >> 8) & amp; 0xFF, status & amp; 0x7F);
        }
    }

    return 0;
} 

This program means that before the end of the child process, when the parent process is still idle, let the parent process do something else while waiting.

WIFEXITED get exit signal

WEXITSTATUS get exit code

Determine whether the child process exits normally, exit normally, and obtain the exit code through WEXITSTATUS(status)

Judging whether to exit normally, according to the signal, if the signal is received, it is an abnormal exit, judged by WIFEXITED(status)

Exit normally, the signal is 0, exit abnormally, we kill the process halfway to see the signal code

Specific code implementation

The blocking and waiting mode of the process

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>


int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        printf("%s fork error\
", __FUNCTION__);
        return 1;
    }
    else if (pid == 0)
    { // child
        printf("child is run, pid is : %d\
", getpid());
        sleep(5);
        exit(257);
    }
    else
    {
        int status = 0;
        pid_t ret = waitpid(-1, & amp;status, 0); // blocking wait, wait 5S
        printf("this is test for wait\
");
        if (WIFEXITED(status) & amp; & amp; ret == pid)
        {
            printf("wait child 5s success, child return code is :%d.\
", WEXITSTATUS(status));
        }
        else
        {
            printf("wait child failed, return.\
");
            return 1;
        }
    }
    return 0;
}

The non-blocking waiting method of the process

WNOHANG is one of the options of the waitpid() function in Linux, which instructs the function not to block while waiting for a child process state change, i.e. return immediately instead of waiting if there is no child process state change available.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        printf("%s fork error\
", __FUNCTION__);
        return 1;
    }
    else if (pid == 0)
    { // child
        printf("child is run, pid is : %d\
", getpid());
        sleep(5);
        exit(1);
    }
    else
    {
        int status = 0;
        pid_t ret = 0;
        do
        {
            ret = waitpid(-1, & amp;status, WNOHANG); // non-blocking wait
            if (ret == 0)
            {
                printf("child is running\
");
            }
            sleep(1);
        } while (ret == 0);

        if (WIFEXITED(status) & amp; & amp; ret == pid)
        {
            printf("wait child 5s success, child return code is :%d.\
", WEXITSTATUS(status));
        }
        else
        {
            printf("wait child failed, return.\
");
            return 1;
        }
    }
    return 0;
}