Linux C Programming: Process Control

(1) Process Concept

A process is a program that has been executed. It does not occupy disk space but consumes system memory, CPU resources, and each running process corresponds to its own virtual address space.

(2) Concepts of Parallelism and Concurrency

The CPU allocates a time slice to each process, and a process can run only when it receives this time slice, making it appear that various programs are running simultaneously, but in reality, they are alternating.

If a process is still running when the time slice ends, the CPU’s usage rights will be reclaimed, and the process will be interrupted and suspended until the next time slice. If the process blocks or ends before the time slice is over, the CPU will switch immediately, thus avoiding waste of CPU resources. The more CPU cores, the higher the processing efficiency of the computer.

(parallel: parallel; concurrent: concurrency)
Linux C Programming: Process Control

Parallel multi-process execution truly exists, allowing multiple processes to run at the same time.

Concurrency appears to run simultaneously on a macro level, achieved through rapid time slice switching by the CPU.

(3) Process Control Block (PCB)

The Linux kernel’s process control block essentially consists of a structure called task_struct, which records various information related to process execution.

① Process ID: Each process has a unique process ID, of type pid_t, which is essentially an integer.

② Process State: Processes have different states, which are constantly changing, including ready, running, suspended, and stopped.

③ File Descriptor Table: Each allocated file descriptor corresponds to an already opened disk file.

④ Resource Limits for Processes

…….

(4) Process States

Linux C Programming: Process Control

① Ready State: The process has been created and is eligible to run but has not yet started; it needs to compete for CPU time slices. Once it obtains a CPU time slice, the process transitions from the ready state to the running state.

② The process’s CPU time slice is exhausted, and it loses the CPU again, transitioning from the running state back to the ready state.

③ Running State: The process that has obtained CPU resources can only run in this state.

(As long as the process has not exited, it will continuously switch between the ready state and the running state)

④ Blocked State: The process is forcibly relinquished from the CPU and is not eligible to compete for CPU time slices.

⑤ Exit State: The process is destroyed, and the system resources it occupied are released.

(5) Process Creation

1. Obtain the current process’s Process ID (PID)

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

2. Obtain the current process’s Parent Process ID (PPID)

#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);

3. Create a new process

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

Return Value: The return value for the parent process is a number greater than 0; the return value for the child process is 0.

Calling the fork() function creates a new process, which becomes the child process. The address space of the child process is copied from the address space of the parent process.

After copying, the user area data in both address spaces is the same, user area includes:

code segment, global data segment, heap segment, dynamically loaded libraries segment (memory-mapped area), stack segment, environment variables, file descriptor table

Execution Position:

The parent process runs from the main() function, while the child process is created after the fork() function is called in the parent process, and the child process begins executing code from after the fork().

(6) Process Control

Process control mainly refers to process exit, process recovery, and special process states: orphan processes and zombie processes.

① Ending a Process:

If you want to directly exit a process, you can call exit() or _exit() at any point in the program.

#include <stdlib.h>
void exit(int status);

② Orphan Processes:

When both parent and child processes are running, but the parent process exits for some reason while the child process is still running, the child process is referred to as an orphan process. When a process is detected to have become an orphan, a fixed process in the system will adopt this orphan process.

When the child process exits, the user area in the process can be released by itself, but the kernel area of the process control block resources cannot be released by itself; it must be released by the parent process to free the child process’s process control block resources. Once adopted, the orphan process allows its parent process to release the kernel area of the process control block resources, thereby avoiding waste of system resources.

③ Zombie Processes:

The parent process runs normally, and the child process ends before the parent process. The child process cannot release its own process control block resources and needs the parent process to execute. However, if the parent process does not execute, the child process becomes a zombie process.

(A zombie process should not be considered a normal process; it has already died, and the user area resources have been released, but it still occupies some kernel resources)

Causes: The parent process does not release the child process’s process control block resources.

(7) Process Recovery

To avoid the creation of zombie processes, we generally recover the resources of child processes in the parent process. There are two methods for recovery: one is blocking mode wait(), and the other is non-blocking mode waitpid().

① wait():

This function is a blocking function; if there are no child processes exiting, the function will block indefinitely. When it detects that a child process has exited, the blocking is lifted, and the child process’s resources are recovered. (Calling it once can only recover one child process)

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

waitpid():

This function allows you to choose whether to recover child process resources in blocking or non-blocking mode. Additionally, it can specify the recovery of a certain child process, a class of child processes, or all child processes’ resources.

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

Parameters:

pid: There are four cases

(1) -1: Recover all child process resources, which is the same as wait(), with no difference, and cannot recover multiple at once; it requires looping recovery.

(2) Greater than 0: Specifies the recovery of resources for a particular process, where pid is the process ID of the child process to be recovered.

(3) 0: Recover all child process IDs of the current process group.

(4) Less than -1: The absolute value of pid represents the process group ID, indicating that all child process resources of this process group should be recovered.

status: NULL

options: Choose whether the function is blocking or non-blocking.

(1) 0: Set to blocking (equivalent to wait()).

(2) WNOHANG: Set to non-blocking.

Leave a Comment