Linux process control

1. fork function

1.1 copy-on-write

Why does fork return two values?

Answer: This is because copy-on-write occurs.

After calling fork, the address space and page table of the parent and child processes are exactly the same. Only when the child process wants to change variables, a new space will be opened up for the child process. This technique is called copy-on-write.

1. 2 Reasons for fork failure

​ If the number of calling processes is too large, the fork call will fail.

2. Process terminated

2.1 What does the os do when the process terminates?

Answer: Release the relevant kernel data structures and corresponding data and codes requested by the process.

2.2 Common ways of process termination

a. The code runs and the result is correct

b. The code runs to completion and the result is incorrect

Why is the return value of the main function 0? Can it be other value?

Answer: Yes, the return value of the main function is returned to the upper-level process to judge the result of the process execution. Returning 0 means the result of the program running is correct, and non-zero means the result is wrong. Returning different non-zero values ​​represents different error reasons. At the same time, the returned number is too abstract, and it is necessary to design a solution to convert the returned error code into a string.

In fact, each process has a process exit code, and the exit code can be converted into a string by functions such as strerror.

c. The code did not finish running and the program crashed

If the program crashes, the exit code is meaningless.

At this time os will send a process signal to kill the process

Summary: If the program runs normally, the exit status of the process is indicated by the exit code, and whether the result is correct is indicated by zero and non-zero. If the process terminates abnormally, kill the process with different numbers of kill commands according to the type of signal sent.

2.3 How to terminate a process with code?

  1. return statement + exit code, only the exit statement inside the main function terminates the process.
  2. exit + exit code : exit( exit code).
  3. _exit + exit code: _exit( exit code).
  4. The difference between _exit and exit: _exit will directly call the system interface to close the program directly, regardless of buffer issues, but exit will help deal with the funeral.
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  5 int main()
  6 {
  7   printf("I'm out\n");
  8   sleep(3);
  9   exit(1);
 10   _exit(1);
 11   return 0;
 12 }           

// \n Refresh the buffer, if you remove \n and use _exit, this sentence will not be printed

3. Process waiting

3.1 Why there is a process waiting

The parent process reclaims the resources of the child process and obtains the exit information of the child process by means of process waiting.

3.2 How to perform process waiting

Call the system interface wait, waitpid waits for the child process to die before recycling. The wait/waitpid method returns to the parent process by obtaining the relevant process exit code or process exit signal in the process structure PCB.

3.2.1 wait method

wait + status.

The status here can be null.

3.2.3 waitpid method

waitpid has three parameters, pid, status, options

pid: If it is non-zero, it means the pid of the child process to be waited for, and if -1 is passed, it means to wait for any child process.

status: This is an output parameter, and wait stores the status reason of the child process exit into this variable.

It should be noted that status is designed based on binary bits,

The lower 16 digits of the status number, the upper eight digits represent the exit code, and the lower seven digits represent the exit signal. You can use the following figure to view the exit code.

At the same time, the system provides us with two macro replacements, WIFEXITED and WEXITSTATUS, and the exit code can also be checked by macro replacement.

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)

Specific usage:

       if (WIFEXITED(status))
         printf("exit code: %d\n", WEXITSTATUS(status));       
         printf("Process exited abnormally\n");                           

options: options defaults to 0, which means blocking waiting, but if you want to do something else during the waiting period, you need to change the options parameter to WNOHANG (wait no hang), which is also a macro. If you use a non-blocking calling method , then multiple calls are required to access whether the child process exits.


int num = 0;
while (!num)
	int status;
	pid_t res = waitpid(-1, &status, WNOHANG);
	if (res > 0)
		//Indicates waiting for success && child process exits
		printf("child process has exited, exit code: %d\n", WEXITSTATUS(status));
        num = 1; //Parent process exit loop detection
    else if (res == 0)
        //Wait for success && child process has not exited yet
        printf("The child process has not died yet\n");
        //other things can be done here
        //wait for failure
        printf("wait for failure\n");
        num = 1; //Waiting for failure also exits the detection

4. Process substitution

The child process and the parent process created by fork share a certain piece of code, but what if the child process wants to have a brand new piece of code of its own? This requires process substitution.

4.1 What is process substitution?

Process replacement is to replace a child process by calling a system interface and loading a brand new program from disk.

4.2 How does process replacement work?

By establishing a new page table mapping, the current process points to new code and data.

4.3 How to use code for process replacement

To call the execl series system interface to load a new program into memory

Take the execl function as an example to explain:

The function signature is as follows:

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

There are "2" parameters here, path represents the path of the target program, arg and ... are regarded as one parameter, representing a variable parameter list: multiple parameters with an indefinite number can be passed in, but must end with NULL at the end to indicate the end of input.

Take the ls command as an example, this function can be filled like this

execl("/usr/bin/ls", "ls", "-a", "-l", NULL);

Take a test program written by myself as an example

The test program only has one sentence to print hello world, and the printed result after the proc is replaced in the afternoon is as shown

If the replacement fails, it will return -1, and if it succeeds, it will not return a value, because execl replaces everything from its own beginning to the last statement, and it has been replaced, so there is nothing else to return.


execv: Different from the l type interface (cl series, you can use list to remember, and pass parameters one by one.), v type can use vector to remember, and pass parameters need to use an array to store instructions.


There are two main types of differences: class v and class l (class v is regarded as a vector, and parameters are passed through an array; class l is regarded as a list, and parameters are passed one by one), class p and class e (p is PATH, which can be Do not write the path, and search through the environment variable; e is env, and the environment variable can be passed to the replaced process through an array).

execlp: execlp("ls", argv);

execvp: execvp("ls", argv); //argv stores the parameters to be used

execle: execle("/usr/bin/ls", "ls", "-a", NULL, env)

execvpe:("ls", "ls", "-a", NULL, env)//env stores the environment variables to be passed

If it is the call interface that the system really provides to us, it has to be execve. All the bottom-level calls of the above functions are execve interfaces, which are the basic encapsulation made by the system.


In the bottom line mode of vim, %s/replacement target/replacement source/g can be replaced in batches.

5. Write a minishell

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define NUM 1024
#define SIZE 32
char login[NUM];

char* work[SIZE];

int main()
  //Get command -- Parse command -- Execute special command -- Execute
  //parent process waits for child process
  while (1)
    //1. Get command
    printf("[zy@VM-12-14-centos----Myshell]$ ");
    memset(login, '\0', sizeof login); //set all to '\0'
    if (fgets(login, sizeof login, stdin) == NULL)
    //remove the last '\n'
    login[strlen(login) - 1] = '\0';
    //printf("Command: %s\n", login);
    //2. Parsing commands
    work[0] = strtok(login, " ");
    int cnt = 1;
    if (strcmp(work[0], "ls") == 0)
      work[cnt++] = "--color=auto";
    while (work[cnt++] = strtok(NULL, " "));//Split and pass
    //for (cnt = 0; work[cnt]; ++cnt)
    //  printf("Command %d: %s\n", cnt + 1, work[cnt]);
    //3. Execute the cd command
    if (strcmp(work[0], "cd") == 0)
      if (work[1] == NULL)  continue; 

    //4. Execution
    //Create a child process for execution, while the parent process waits
    pid_t id = fork();
    if (id == 0)
      //replace process
      execvp(work[0], work);
      printf("guess what? can you see me?\n");

    else if (id < 0)
      printf("Creation failed\n");
    //parent process
    int status;
    pid_t res = waitpid(-1, &status, 0);
    if (res > 0)  printf("Child process exited with exit code: %d\n", WEXITSTATUS(status));

  return 0;

After testing, the basic instructions can be realized with colors.

Tags: Linux Operation & Maintenance C server

Posted by Perry Mason on Tue, 29 Nov 2022 07:17:12 +1030