process waiting, process replacement

Table of contents

process waiting

waitpid function

wait function

process replacement


process waiting

Meaning of process waiting

If the child process exits, if the parent process ignores it, it may cause a 'zombie process' problem, which in turn causes 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 needs to reclaim the resources of the child process and obtain the exit information of the child process by means of process waiting.

waitpid function

#include <sys/types.h>

#include <sys/wait.h>

pid_ t waitpid(pid_t pid, int *status, int options);

return value:

  • When returning normally, waitpid returns the process PID 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:

1. pid (to determine the members of the waiting set) :

If pid=-1, then the waiting set is composed of the child processes owned by the parent process. Equivalent to wait.

If pid > 0, then the wait set is a single child process whose process ID is equal to pid.

2. status (modify the default behavior) :

If the status parameter is non-null, then waitpid will put in status information about the status of the child process that caused the return. The wait.h header file defines several macros that interpret the status parameter:

  • WIFEXITED(status): Returns true if the child process terminated normally by calling exit or a return (return).
  • WEXITSTATUS(status): Returns the exit status of a normally terminated child process. This status is only defined when WIFEXITED() returns true.
  • WIFSIGNALED(status) : Returns true if the child process terminated due to an uncaught signal.
  • WTERMSIG( status): Returns the number of the signal that caused the child process to terminate. This state is only defined if WIFSIGNALED() returns true.
  • WIFSTOPPED(status): Returns true if the child process that caused the return is currently stopped.
  • WSTOPSIG(status): Returns the number of the signal that caused the child process to stop. This state is only defined if WIFSTOPPED() returns true.
  • WIFCONTINUED(status): Returns true if the child process receives a SIGCONT signal to restart.

If the calling process has no children, waitpid returns -1 and sets errno to ECHILD. If the waitpid function is interrupted by a signal, it returns -1 and sets errno to EINTR.

3. options (check the exit status of the recycled child process) :

The default behavior can be modified by setting options to various combinations of the constants WNOHANG, WUNTRACED and WCONTINUED:

  • WNOHANG: If any child process in the waiting set has not terminated, then return immediately (return value is 0). The default behavior is to suspend the calling process until a child process terminates. This option is useful if you want to do some useful work while waiting for the child process to terminate.
  • WUNJTRACED: Suspends the execution of the calling process until one of the processes in the wait set becomes terminated or is stopped. The returned PID is the PID of the terminated or stopped child process that caused the return. The default behavior is to return only terminated child processes. This option is useful when you want to inspect terminated and stopped child processes.
  • WCONTINUED: Suspend the execution of the calling process until a running process in the waiting set terminates or a stopped process in the waiting set receives a SIGCONT signal to restart execution.

    These options can be combined using the OR operation. For example:

  • WNOHANG | WUNTRACED: Return immediately, if none of the child processes in the waiting set are stopped or terminated, the return value is 0; if one is stopped or terminated, the return value is the PID of the child process.

wait function

The wait function is a simple version of the waitpid function.

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int*status);  

Return value: Returns the pid of the process being waited for successfully, returns -1 if failed.

Parameters: Output parameters, get the exit status of the child process, if you don’t care, you can set it to NULL

Calling wait(&status) is equivalent to calling waitpid(-1,&status,0).

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

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

If the subprocess does not exist, an error is returned immediately.

The parent process stores the PIDs of its children in order, and then waits for each child in the same order by calling waitpid with the appropriate PID as the first argument. 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 #define N 2
int main()
{                                                                                                                                                                                           
  int status,i;
   pid_t pid[N],retpid;
    //Parent creates N children
   for(i=0;i<N;i++)
      if((pid[i] = fork()) == 0)//child
       exit(100+i);

     //Parent reaps N children in order
     i=0;
    while((retpid = waitpid(pid[i++],&status,0)) > 0)
    {
         if(WIFEXITED(status))
           printf("child %d terminated normally with exit status=%d\n",retpid,WEXITSTATUS(status));
         else 
           printf("child %d terminated abnormally\n",retpid);
     }
 
     the only normal termination is if there are no more children
    if(errno != ECHILD)
      unix_error("waitpid error");
  
     exit(0);                                                       
 } 

When the child process does not exit, the parent process is always waiting for the child process to exit. During the waiting period, the parent process cannot do anything. This kind of waiting is called blocking waiting.

In fact, we can let the parent process perform some of its own things while waiting for the child process to exit, and then read the exit information of the child process when the child process exits, that is, non-blocking waiting.

At this time we can use the options parameter :

WNOHANG : If any child process in the waiting set has not terminated, then return immediately (return value is 0). The default behavior is to suspend the calling process until a child process terminates. This option is useful if you want to do some useful work while waiting for the child process to terminate.

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
	pid_t pid;
	pid = fork();
	if (pid < 0) {
		printf("%s fork error\n", __FUNCTION__);
		return 1;
	}
	else if (pid == 0) { //child
		printf("child is run, pid is : %d\n", getpid());
		sleep(5);
		exit(1);
	}
	else {
		int status = 0;
		pid_t ret = 0;
		do
		{
			ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
			if (ret == 0) {
				printf("child is running\n");
			}
			sleep(1);
		} while (ret == 0);
		if (WIFEXITED(status) && ret == pid) {
			printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));
		}
		else {
			printf("wait child failed, return.\n");
			return 1;
		}
	}
	return 0;
}

process replacement

exec is a program replacement function and does not create a process itself. The process generated by exec is an identical copy of the current process, and the pid has not changed.

replace function

int execve(const char *path, char *const argv[], char *const envp[]);

Returns nothing on success, -1 on error. 

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ...,char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

Only execve is a real system call, and other functions are encapsulation of the execve function, and finally call execve.

These function prototypes seem to be easy to confuse, but it is easy to remember as long as you master the rules.

l(list) : Indicates that the parameter adopts a list;

v(vector) : array for parameters;

p(path): There is p to automatically search the environment variable PATH;

e(env): Indicates that it maintains environment variables by itself 

execl: The parameter format is a list; without a path; use the current environment variable.

execlp: The parameter format is a list; with a path; you need to assemble environment variables yourself.

execle: The parameter format is a list; without a path; use the current environment variable.

execv: The parameter format is an array; without a path; use the current environment variable.

execvp: The parameter format is an array; with a path; you need to assemble environment variables yourself.

We can see that the end... printed later is not output. When a process calls an exec function, the user space code and data of the process are completely replaced by the new program, and the execution starts from the startup routine of the new program. The following code belongs to the old code and is directly replaced, and will not be executed again. 

 

#include <unistd.h>

int main()

{

        char *const argv[] = {"ls", "-l", NULL};

        char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

        execl("/bin/ls", "ls", "-l", NULL); // with p, you can use the environment variable PATH, no need to write the full path execlp("ls", "ls", "-l ", NULL); // For those with e, you need to assemble the environment variables yourself

        execle("ls", "ls", "-l", NULL, envp);

        execv("/bin/ls", argv); // with p, you can use the environment variable PATH, no need to write the full path

        execvp("ls", argv); // For those with e, you need to assemble environment variables by yourself

        execve("/bin/ls", argv, envp);

        exit(0);

}  

 

 The last element of the array is preferably empty.

 

Guess you like

Origin blog.csdn.net/m0_55752775/article/details/130296320