Catalog
2. State and Transition of the Process
5. Process Creation--fork Function
1. Definition of process
Differences between procedures and processes:
Program: is the executable file on disk, and only takes up space on disk, is a static concept.
Processes: Processes are called processes after they are executed. They do not occupy disk space, they need to consume system memory, CPU resources, and each running process corresponds to its own virtual address space. This is a dynamic concept.
2. State and Transition of the Process
The entire life cycle of a process can be simply divided into three states:
Ready: The process has all the conditions to execute and is waiting to allocate processing time for the CPU.
Execution state: The process is running on CPU.
Waiting state: The state in which a process is temporarily unable to continue execution because it does not have certain execution conditions.
Scheduling mechanisms for processes:
Time slice rotation, context switching, multi-process is not to say that one process executes before another process, but to execute alternately. One process executes for a period of time, then the next process executes for a period of time, and so on. After all processes execute, they go back to the beginning of the first year and so on.
3. Process Control Block
A process control block is a structure used to store process information, also known as a PCB.
OS controls and manages concurrent processes based on PCB. When a process is created, the system opens up a memory space to store the PCB data structures associated with the process.
PCB is the most important recorded data structure in the operating system. The PCB records all the information needed to describe the progress of the process and control its operation.
PCB is the only sign that a process exists, and PCB is stored in task_on Linux Struct structure.
4. Process number
Each process is identified by a process number of type pid_t, process number range: 0-32767
The process number is randomly assigned to the current process by the operating system and cannot be controlled by itself
The process number is always unique, but it can be reused. When a process terminates, its process number can be used again
View all open processes in the current system in ubuntu
ps ajx
PPID: Process number of the parent of the current process
PID: Process number of current process
PGID: Process group ID of the group in which the current process is located
COMMAND: The name of the current process
Special process number:
The process number starts with 0 in the linux system.
Processes with process numbers 0 and 1 are created by the kernel.
A process with a process number of 0 is usually a dispatch process and is often referred to as a swapper.
A process with process number 1 is usually an init process, which is the ancestor of all processes.
Except for scheduling processes, all processes under linux are created directly or indirectly by the process init process
The Linux operating system provides three functions getpid(), getppid(), and getpgid() to get the process number.
#include <sys/types.h> #include <unistd.h> pid_t getpid(void); Function: Get the process number of the current process pid_t getppid(void); Function: Get the process number of the parent of the current process pid_t getpgid(pid_t pid); Function: Get the current process in the process group id
5. Process Creation--fork Function
#include <unistd.h> pid_t fork(void); Function: Create a sub-process based on the existing process Parameter: None Return value: Success: >0 The process number of the child process, identifying the code area of the parent process =0 Code area of child process Failure: ‐1 Return to parent process, child process will not create
A child process using the fork function is a copy of the parent process, which inherits the address space of the entire process from the parent process
Address space:
Includes process context, process stack, open file descriptor, signal control settings, process priority, process group number
Wait.
A subprocess is unique in that it only has a process number, a timer, and so on, so the cost of using the fork function is enormous.
Spatial diagram of parent-child process after fork function execution:
6. Process Creation
- The return value of the fork function distinguishes the separate code area of the parent-child process, where greater than 0 is the parent process and 0 is the child process.
- The parent-child process is executed alternately, who runs first and who runs later is uncertain. Do not assume that the parent process executes before the child process executes.
int main() { // Create a child process in the parent process pid_t pid = fork(); printf("Current Process fork()Return value of: %d\n", pid); if(pid > 0) { // Logic of parent process execution printf("I am the parent process, pid = %d\n", getpid()); } else if(pid == 0) { // Logic for subprocess execution printf("I am a child process, pid = %d, My father is: %d\n", getpid(), getppid()); } else // pid == -1 { // Failed to create subprocess } // Without judgment, both parent and child processes will execute this loop for(int i=0; i<5; ++i) { printf("%d\n", i); } return 0; }
Child process inherits parent process space
- The parent process runs from the main () function, the child process is created after the fork () function is called in the parent process, and the child process starts executing code downward from the fork ().
int main(int argc, char *argv[]) { int fd; if((fd = open("file.txt", O_RDONLY)) == -1) { perror("fail to open"); return -1; } //Child processes inherit some common areas of the parent process, such as disk space, kernel space //The offset of the file descriptor is stored in kernel space, so if the parent process changes the offset, the offset obtained by the child process is after the change pid_t pid; pid = fork(); if(pid < 0) { perror("fail to fork"); return -1; } if(pid > 0) { printf("This is a parent process\n"); char buf[32] = ""; if(read(fd, buf, 30) == -1) { perror("fail to read"); return -1; } printf("buf = [%s]\n", buf); } else { sleep(1); printf("This is a son process\n"); char buf[32] = ""; if(read(fd, buf, 30) == -1) { perror("fail to read"); return -1; } printf("buf = [%s]\n", buf); } while(1) { } return 0; }
7. exit and _ exit function
If you want to exit a process directly, you can call exit() or _anywhere in the program Exit() function. Function parameters are equivalent to exit codes, if the parameter value is 0 program exit status code is 0, if 100 exit status code is 100.
exit function
#include <unistd.h> void _exit(int status); Function: Exit the current process Parameters: status: Exit state, passed by parent process wait Functions receive this state General Failure Exit Set to Non-0 General Successful Exit Set to 0 Return value: nothing
_ exit function
include <stdlib.h> void exit(int status); Function: Exit the current process Parameters: status: Exit state, passed by parent process wait Functions receive this state General failure exit set to non General Successful Exit Set to 0 Return value: nothing
Exit and _ The difference between exit functions:
- exit is a library function, and _ exit is a system call
- exit refreshes the buffer, but _ exit will not refresh the buffer
- exit is generally used
8. Recycling of processes
To avoid zombie processes, we normally recycle the resources of the child processes in the parent process. There are two ways of recycling, one is blocking wait(), the other is non-blocking waitpid().
1. wait function
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); Function: Wait for the child process to terminate, if the child process terminates, this function reclaims the resources of the child process. call wait The process of a function is suspended until one of its subprocesses exits or receives a signal that cannot be ignored. If the calling process has no children or if its children have ended, the function returns immediately. Parameters: status: When the function returns, the parameter status Includes status information when the child process exits. The exit information for the child process is in one int It contains multiple fields. Each of these fields can be removed by using a macro definition Subprocesses can pass through exit perhaps_exit Function Send Exit Status Return value: Success: The process number of the child process. Failure:‐1
Remove exit information for child process
WIFEXITED(status)
If the child process terminates normally, the field value is not zero.
WEXITSTATUS(status)
Returns the exit status of the child process, which is saved between 8 and 16 bits in the status variable.
Before using this macro, you should use the macro WIFEXITED to determine if the child process exited normally before using this macro.
Be careful:
This status is an integer variable pointed to by a wait parameter.
int main(int argc, char *argv[]) { pid_t pid; pid=fork(); if(pid<0) { perror("fail to fork"); return -1; } if(pid == 0) { int i = 0; for(i=0;i<5;i++) { printf("this is son process\n"); sleep(1); } //Use exit to exit the current process and set exit status exit(2); } else { //Use wait to block child process exit in parent process //Do not receive child process exit status //wait(NULL); //Receives the exit status of a child process, which must either use exit or _ Exit function exits a process by sending an exit state int status = 0; wait(&status); if(WIFEXITED(status) != 0) { printf("The son process return status: %d\n", WEXITSTATUS(status)); } printf("this is father process\n"); } return 0; }
2. waitpid function
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *status,int options) Function: Wait for the child process to terminate, if the child process terminates, this function reclaims the resources of the child process. Parameters: pid: The specified process or process group pid>0: Waiting for process ID Be equal to pid Subprocess. pid=0: Wait for any child process in the same process group, if the child process has joined another process group, waitpid Will not wait for it. pid=‐1: Wait for any subprocess, at this time waitpid and wait It works the same way. pid<‐1: Waiting for any child process in the specified process group ID Be equal to pid Absolute value status: Save status information on child process exit options: option 0: with wait,Block the parent process and wait for the child process to exit. WNOHANG: If there are no child processes that have ended, they will be returned immediately. WUNTRACED: If the child process pauses, this function returns immediately, ignoring the end state of the child process. (Tracking debugging, rarely used) Return value: Success: Return the process number of the child process whose status has changed; If options are set WNOHANG also pid The specified process returns 0 if it exists. Failure: Return‐1. When pid The indicated subprocess does not exist or exists, but is not a subprocess of the calling process. waitpid Will return in error, at this time errno Set to ECHILD. wait(status) <==> waitpid(‐1, status, 0)
int main(int argc, char *argv[]) { pid_t pid; pid=fork(); if(pid < 0) { perror("fail to fork"); return -1; } if(pid == 0) { int i = 0; for(i=0;i<5;i++) { printf("this is son process\n"); sleep(1); } exit(0); } else { waitpid(pid, NULL, 0); printf("this is father process\n"); } return 0; }
9. Orphan Process
Create a child process in a started process, when the parent and child processes run simultaneously, but the parent process exits first for some reason, and the child process is still running. At this time, the child process can be called an orphan process (as in reality).
The operating system cares about every process running. When an orphan process is detected, there will be a fixed process in the system to adopt the orphan process (with a dad). If there is no desktop terminal on Linux, the process of adopting an orphan is the init process (PID=1), and if there is a desktop terminal, the process of adopting an orphan is the desktop process.
So the question is, why should the system adopt this orphan process?
When the child process exits, the user zone in the process can release itself, but the pcb resources in the process kernel can not be released. The parent process must release the pcb resources of the child process. After the orphan process is adopted, the father can do this on his behalf, which can avoid wasting system resources.
The following code gives you an orphan process:
int main() { // Create Subprocess pid_t pid = fork(); // Parent Process if(pid > 0) { printf("I am the parent process, pid=%d\n", getpid()); } else if(pid == 0) { sleep(1); // Forces a child process to sleep for 1 s, during which time the parent process exits and the current process becomes an orphan process // Subprocess printf("I am a child process, pid=%d, Parent Process ID: %d\n", getpid(), getppid()); } return 0;
10. Zombie Process
Create a child process in a starting process. There are two processes, parent and child. The parent process runs normally. The child process ends with the parent process first. The child process cannot release its own PCB resources. The parent process is needed to do this. However, if the parent process does not care, the child process becomes a zombie process.
The zombie process can't think of it as a normal process. It's dead, the user area resources have been released, and only a few core resources (PCB s) are being used.
A zombie process occurs as a result of an inaction by the parent process of the deceased process.
Run the following code to get a zombie process:
int main() { pid_t pid; // Create Subprocess for(int i=0; i<5; ++i) { pid = fork(); if(pid == 0) { break; } } // Parent Process if(pid > 0) { // Need to keep the parent process running // A zombie process occurs when you run without exiting and recycle while(1) { printf("I am the parent process, pid=%d\n", getpid()); sleep(1); } } else if(pid == 0) { // Subprocess, after executing this code, the subprocess exits printf("I am a child process, pid=%d, Parent Process ID: %d\n", getpid(), getppid()); } return 0; }
The way to kill a zombie process is to kill the parent process of the zombie process so that its resources are recycled by the system.
The way to kill the zombie process PID is not to kill the zombie process. This command only works for the living process, which is already dead.