Process termination, process waiting and fork(), wait(), waitpid()

Article directory

  • 1. Process termination
    • (1) Situation of process termination
    • (2) How to terminate the process
  • 2. Process waiting
    • (1) Concept
    • (2) Necessity
    • (3) wait()–system call
    • (4) waitpid()–system call
    • (5) Get child process status
  • 3. fork() function

1. Process termination

(1) Process termination situation

  1. The code is run and the result is correct
  2. The code runs and the result is incorrect
  3. The code terminates abnormally (that is, the program crashes or crashes)

In C\C++, the main() function generally returns 0, and this “0” is the process exit code we are going to talk about. It represents the process exit information, allowing the parent process to read it, so that the The child process in the zombie state (Z) has the exit information read by the parent process, causing it to enter the death state (X).

Is it possible to use echo $ in Linux command line? Indicates in bash, the exit code of the corresponding process when the latest execution is completed.

[swb@VM-8-12-centos test]$ echo $?
0

The output of the second and third echo is the exit code of the previous echo process. In fact, every command executed is a process and will have an exit code. This exit code is the return value of the main function. Each process has a main function.

Different error codes correspond to different error messages. Of course, we can also design a mapping between error codes and error messages ourselves.

There is a strerror function in the C\C++ library file that can print out exit codes corresponding to different error causes.

 1 #include <stdio.h>
  2 #include <string.h>
  3
  4 int main(){<!-- -->
  5
  6 int i = 0;
  7 for(;i < 10; i + + ){<!-- -->
  8 printf("%d : %s\\
",i,strerror(i));
  9   }
 10
 11 return 0;
 12}
[swb@test temp_test]$ ./test
0: Success
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted system call
5: Input/output error
6: No such device or address
7: Argument list too long
8:Exec format error
9 : Bad file descriptor

(2) How to terminate the process

Normal termination (you can view the process exit code through echo $?):

  1. Returning from main (represents process exit, the same is true for returning from a function, called function return)
  2. Call exit (terminate the process, the buffer will be flushed)
  3. _exit (terminates the process without any refresh)
#include <unistd.h>
void _exit(int status);

Parameters: status defines the termination status of the process. The parent process obtains this value through wait (status has many macro values, such as EXIT_SUCCESS)
Note: Although status is int, only the lower 8 bits can be used by the parent process. So when _exit(-1) is executed on the terminal, $? is found and the return value is 255.

What will the kernel do when the process is terminated?
When the child process goes from zombie state (Z) to death state (X), the code and data of the child process will be released, but the operating system may not release the kernel data structure of the process. (Note: Kernel data buffer pool – the role of slab dispatcher)

Exit abnormally:

ctrl + c, signal terminated

2. Process waiting

(1) Concept

After the parent process executes fork, a child process will be created. The child process generally helps the parent process complete some task. At this time, the parent process needs to wait for the child process to exit through wait/waitpid.

(2) Necessity

  • When the child process exits, the parent process does not read the child process exit information, which may cause zombie process problems and cause process leakage.
  • Once a process becomes a zombie process, kill -9 cannot kill the process.
  • The parent process recycles the child process to obtain the exit information of the child process through process waiting.

(3) wait()–system call

Wait for any child process to exit

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);

return value:
The pid of the process being waited for is returned successfully, and -1 is returned on failure.

parameter:
Output parameter, obtains the exit status of the child process. If you don’t care, it can be set to NULL.

(4) waitpid()–system call

The most commonly used parent process recycles the resources of the child process and allows the child process to move from state Z to state X.

#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);

return value:
>0: Wait for the child process to succeed and return the child process pid.
=0: Waiting failed.
parameter:
pid: >0: Specify which child process to wait for; -1: Wait for any process (same as wait function)
status: output parameter (by calling this function, specific data is taken out from inside the function). The inside of the function refers to taking out the exit code of the child process exit from the task_struct of the child process and giving it to the parent process.
options: 0: blocking waiting; non-0: non-blocking waiting.

(5) Get child process status

  • Both wait and waitpid have a status parameter, which is an output parameter and is filled in 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 feedback the exit information of the child process to the parent process based on this parameter.
  • status cannot simply be treated as an integer, but can be treated as a bitmap (only the lower 16 bits of status are looked at).

There are two situations for the exit of a child process

  • One is normal termination (result is correct or incorrect)
  • One is abnormal termination (the process receives some kind of signal due to abnormal problems)

Determine whether the process terminated normally by judging whether a termination signal (0-6, lower 7 bits) is received. If it terminates normally (lower 7 bits are all 0), then look at the exit status (8-15, 8 bits) Bit).

Bitwise operations obtain the exit status (exit code) and termination signal of the child process
If you want to get the 8-digit exit status (exit code) of 8-15, you can shift the status to the right by 8 bits, and then compare it with 000…000 1111 1111 ((status>>8) & amp;0xFF) (because status is 32 bit, after shifting 8 bits to the right, set the previous 24 bits to 0);
If you want to obtain the 7-bit termination signal from 0 to 6, you can set status & 0x7F (only the lower 7 bits need to be obtained);
If it exits normally, the exit signal will be 0; if it exits abnormally, the exit signal will not be 0, and the previous exit status will be meaningless.

The macro definition in C language obtains the exit status (exit code) of the child process and determines whether a termination signal is received

WIFEXITED(status): True if it is the status returned by the normal termination of the child process. (Check whether the process exited normally)
WEXITSTATUS(status): If WIFEXITED is non-zero, extract the child process exit code. (View the exit code of the process)

3. fork() function

The fork function is a very important function in Linux. It creates a new process from an existing process. The new process is the child process, and the original process is the parent process.

#include <unistd.h>
pid_t fork(void);

Return value: 0 is returned in the child process, the child process id is returned in the parent process, and -1 is returned in error.

The process calls fork(). When control is transferred to the fork code in the kernel, the kernel will do:

  1. Allocate new memory blocks and kernel data structures (pcb + address space mm_struct + page table) to the child process
  2. Copy part of the data structure content of the parent process to the child process
  3. Add child process to system process list
  4. fork returns and starts scheduler scheduling

Before the fork() function is executed, the parent process executes independently. After the fork() function, the parent and child processes execute separately (the father and the child share all code).
The subsequent code executed by the child process is not equal to all the shared code. The child process can only start execution after the fork() function (note: related to the program counter in the CPU, which is the eip or pc pointer).

What does the operating system do after thefork() function?
Because the process is independent, after fork(), the kernel data structure (struct task_struct, struct mm_struct and page table) of the child process is created, as well as the code inherited from the parent process, and the data of the parent process is copied on write. Be shared or standalone.

fork() copy-on-write
Usually, the code of the father and the son is shared. When the father and the son are not writing, the data is also shared. When either party tries to write, they each have a copy in the form of copy-on-write. See the figure below for details:
The parent process and the child process each have their own virtual address space and page table, but the physical address mapped to the corresponding data virtual address is the same.
By default, all data is read-only. If a child process wants to write certain data (page table entry 100), copy-on-write will occur, and a page fault interrupt will occur. First copy a copy of the data to the new physical address. space, and then modify the mapping relationship of the child process’s page table, so that the virtual address of the child process’s corresponding data is mapped to the new physical address, and then the interrupt ends, allowing the child process to modify the data.

General usage of fork

  1. A parent process wants to duplicate itself so that the parent and child processes execute different code segments at the same time. For example, a parent process waits for a client request and spawns a child process to handle the request.
  2. A process wants to execute a different program. For example, after the child process returns from fork, it calls the exec function.