Detailed explanation of Linux system process concept

insert image description here

Von Neumann architecture

Von Neumann Architecture (Von Neumann Architecture), also known as Princeton Architecture, is a basic design paradigm of a computer system, which was proposed by mathematician Von Neumann in 1945. This architecture has become the basis of modern computer architecture, and it is of great significance in the history of computer development. Its main feature is to store the program and data of the computer in the same memory, and use the concept of stored program, so that the computer can automatically execute the program according to the sequence of instructions.

The von Neumann architecture consists of the following key components:

  1. Central Processing Unit (Central Processing Unit, CPU): The CPU is the core of the computer and is responsible for executing instructions and performing arithmetic and logic operations. It includes arithmetic logic unit (Arithmetic Logic Unit, ALU) and control unit (Control Unit) . The ALU performs various arithmetic and logic operations, while the control unit is responsible for fetching instructions from memory, interpreting them, and controlling other parts of the computer to execute them.
  2. Memory: Memory is used to store programs and data. The von Neumann architecture uses a unified memory space , ie both instructions and data are stored in the same memory. This allows programs to be read and processed like data.
  3. Input/Output Devices (Input/Output, I/O): I/O devices are used to input data and information into the computer or to output the results of computer processing to the outside world . These devices can be keyboards, mice, monitors, printers, etc.
  4. Control Flow: The computer executes sequentially according to the sequence of instructions stored in memory . The control unit reads the instructions from the memory and performs the appropriate action depending on the instruction type. Control structures such as conditional branches and loops in instructions allow the program to change the execution flow according to conditions and needs.
  5. Stored Program (Stored Program): One of the important concepts in the von Neumann architecture is the stored program. Programs are stored in memory in the form of binary instructions, which can be executed sequentially or non-sequentially based on conditions or jump instructions .

The advantage of the von Neumann architecture lies in its simplicity and generality. Since the program and data are stored in the same memory, the program can modify and operate itself, which makes the computer highly flexible and programmable. This architecture laid the foundation for the development of modern computers and has become the basic structure of most general-purpose computer systems. However, as the technology evolved, other types of architectures emerged, such as those employed in parallel computing, vector processing, and modern Very Large Scale Integration (VLSI) technology.

insert image description here
Our common computers, such as notebooks. Most of our uncommon computers, such as servers, obey the von Neumann system.
As of now, the computers we know are composed of hardware components one by one:

  1. Input unit : including keyboard, mouse, camera, microphone, scanner, tablet, disk, network card, etc.
  2. Central Processing Unit (CPU) : Contains computing units and controllers, etc.
  3. Output unit : display, printer, stereo, disk, network card, etc.
  4. memory : internal memory

A few points must be emphasized about von Neumann :

  1. The storage here refers to the memory
  2. Regardless of the cache situation, the CPU here can and can only read and write memory, and cannot access peripherals (input or output devices)
  3. Peripherals (input or output devices) to input or output data can only be written to or read from memory.
  4. All devices can only deal directly with memory.

The understanding of von Neumann should not stop at the concept, but go deep into the understanding of software data flow. From the above hardware composition, we can know that some devices can be used as both input devices and output devices.For example, from the moment you log in to QQ and start chatting with a friend, the process of data flow. What is the data flow process from when you open the window and start sending him a message to when he receives the message?

First of all, of course, input data from the keyboard, pass through the memory, and then the memory sends a request to the CPU. The CPU allows this step to operate and at the same time sends an instruction to call the network card. The network card then sends the data to the cloud server of the chat software through the network cable.

Although the description here is not particularly detailed, the approximate structure of the system call, that is, the von Neumann architecture, is the case. Let's learn about the operating system next.

Operating system (Operator System)

insert image description here

1. Concept

An operating system is a kind of software, which is one of the most basic and important system software in a computer system. It is a bridge connecting computer hardware and application software, responsible for managing and coordinating various resources of the computer, providing services for users and application programs, and enabling the computer to run and perform tasks efficiently.

The main functions of the operating system include the following aspects:

  1. Resource management : The operating system manages the computer's hardware resources, including the central processing unit (CPU), memory (RAM), hard disk, input/output devices, and more. It decides which program can take CPU time, allocates memory to different applications, and controls access to external devices .
  2. Process management : The operating system manages the processes that the computer runs. A process is an instance of a program running on a computer. The operating system is responsible for creating, terminating, suspending, resuming processes, and switching between them for multitasking .
  3. Memory management : The operating system is responsible for allocating memory space to different processes and managing data and instructions in memory . It implements virtual memory technology to store some programs and data on auxiliary storage devices (such as hard disks) to expand the available memory space.
  4. File system management : The operating system manages the computer's file system, including the creation, deletion, reading, and writing of files. It provides persistent storage and access to data through the file system, enabling users to save and retrieve data.
  5. Device Drivers : Operating systems provide device drivers for controlling and managing hardware devices. These drivers enable the operating system to communicate with different types of hardware devices.
  6. User Interface : **The operating system provides the user interface that enables the user to interact with the computer. **Common user interfaces include command-line interfaces and graphical user interfaces (GUIs).

The operating system also has some other important functions, such as network management, security management, scheduling algorithms, etc. These functions enable the computer to efficiently run multiple tasks, improve resource utilization, and ensure data security and integrity.

Different types of computers and devices need to adapt to different operating systems. Currently, the most common operating systems on the market include Windows, macOS, Linux, and more. Each operating system has its own unique features and advantages to meet the needs of different users and applications.

2. Purpose

The main purpose of designing the operating system is to manage the hardware resources of the computer and provide a convenient, efficient, safe and stable operating environment, so that the computer can effectively execute user programs and application software. The following are the main purposes for designing an operating system:

  1. Resource management : The operating system is responsible for managing the hardware resources of the computer, including the central processing unit (CPU), memory (RAM), hard disk, input/output devices, etc. It decides how to allocate these resources to different applications and processes to maximize multitasking and resource utilization .
  2. Multitasking : Modern computers need to be able to run multiple tasks at the same time, such as running multiple applications at the same time or handling multiple user requests. The operating system enables multiple tasks to be executed alternately on the CPU through process management and scheduling algorithms, bringing better response and efficiency to users .
  3. Memory management : The operating system is responsible for allocating memory space to different programs and processes and performing memory swapping (virtual memory) when needed to expand the available memory space. This allows for better use of memory resources, allowing the computer to handle larger-scale tasks .
  4. File system management : The operating system provides a file system to manage the persistent storage and access of data . It allows users to create, read, write and delete files and ensures data security and integrity.
  5. User Interface : The operating system provides a user interface that enables the user to interact with the computer , input commands and get output from the computer. This can be a command line interface or a graphical user interface (GUI), providing a friendly way to operate the computer.
  6. Security : The operating system needs to protect the security of the computer system and user data . It ensures that only authorized users can access specific resources and data through access control, authentication, and rights management.
  7. Error handling : The operating system needs to have certain error handling capabilities, be able to detect and handle abnormalities in hardware and software, and prevent system crashes and data damage.
  8. Stability and reliability : When designing an operating system, it is necessary to pursue stability and reliability, so that the system can run stably for a long time without crashes and failures.

In general, the purpose of designing an operating system is to allow the computer system to efficiently and safely run various applications, provide a user-friendly interactive interface, and maximize the use of computer hardware resources to meet the needs of users and improve the overall performance of the computer system.

3. Management

In an actual operating system, in order to manage hardware resources more efficiently, linked lists or other efficient data structures are usually used to organize struct structures to flexibly add, delete, and modify data items , which is very suitable for managing dynamically changing hardware resources.

The following is an example showing how the operating system uses linked lists to organize struct structures to manage CPU, memory, and hard disk resources:

#include <stdio.h>
#include <stdlib.h>

// 定义CPU的结构体
struct CPU {
    
    
    int id;           // CPU编号
    int cores;        // CPU核心数
    double clock;     // CPU主频
    // 其他CPU相关的属性和方法
};

// 定义内存的结构体
struct Memory {
    
    
    int id;           // 内存编号
    int size;         // 内存大小(单位:字节)
    // 其他内存相关的属性和方法
};

// 定义硬盘的结构体
struct Disk {
    
    
    int id;           // 硬盘编号
    int size;         // 硬盘大小(单位:字节)
    // 其他硬盘相关的属性和方法
};

// 定义操作系统管理的硬件资源链表节点
struct HardwareNode {
    
    
    struct CPU cpu;
    struct Memory memory;
    struct Disk disk;
    struct HardwareNode* next; // 指向下一个节点的指针
};

// 定义操作系统的结构体
struct OperatingSystem {
    
    
    struct HardwareNode* hardwareList; // 硬件资源链表的头节点
    // 其他操作系统相关的属性和方法
};

// 初始化操作系统硬件资源链表
void initHardwareList(struct OperatingSystem *os) {
    
    
    os->hardwareList = NULL; // 初始化为空链表
}

// 添加硬件资源节点到链表
void addHardwareNode(struct OperatingSystem *os, struct HardwareNode* node) {
    
    
    if (os->hardwareList == NULL) {
    
    
        os->hardwareList = node;
        node->next = NULL;
    } else {
    
    
        node->next = os->hardwareList;
        os->hardwareList = node;
    }
}

// 使用链表中的硬件资源进行任务处理
void processTasks(struct OperatingSystem *os) {
    
    
    // 遍历链表,处理每个硬件资源节点中的任务
    struct HardwareNode* currentNode = os->hardwareList;
    while (currentNode != NULL) {
    
    
        // 处理当前节点中的任务
        // ...
        currentNode = currentNode->next; // 移动到下一个节点
    }
}

// 释放链表资源,避免内存泄漏
void freeHardwareList(struct OperatingSystem *os) {
    
    
    struct HardwareNode* currentNode = os->hardwareList;
    while (currentNode != NULL) {
    
    
        struct HardwareNode* temp = currentNode;
        currentNode = currentNode->next;
        free(temp); // 释放当前节点
    }
    os->hardwareList = NULL; // 确保链表头指针为空
}

int main() {
    
    
    struct OperatingSystem os;
    initHardwareList(&os);

    // 创建CPU、内存、硬盘节点并添加到链表中
    struct HardwareNode* cpuNode = (struct HardwareNode*)malloc(sizeof(struct HardwareNode));
    cpuNode->cpu.id = 1;
    cpuNode->cpu.cores = 4;
    cpuNode->cpu.clock = 2.6;
    addHardwareNode(&os, cpuNode);

    struct HardwareNode* memoryNode = (struct HardwareNode*)malloc(sizeof(struct HardwareNode));
    memoryNode->memory.id = 1;
    memoryNode->memory.size = 8192; // 8 GB
    addHardwareNode(&os, memoryNode);

    struct HardwareNode* diskNode = (struct HardwareNode*)malloc(sizeof(struct HardwareNode));
    diskNode->disk.id = 1;
    diskNode->disk.size = 102400; // 100 GB
    addHardwareNode(&os, diskNode);

    // 使用链表中的硬件资源进行任务处理
    processTasks(&os);

    // 释放链表资源,避免内存泄漏
    freeHardwareList(&os);

    return 0;
}

In the above example, we use a hardware resource linked list, in which each linked list node contains a CPU structure, a Memory structure and a Disk structure, respectively representing different hardware resources. Through the linked list, the operating system can dynamically add and manage hardware resources, and process tasks more flexibly and efficiently. At the same time, at the end of the program, the memory leak problem is avoided by releasing the linked list resource. The actual operating system will be more complex and complete, but this example demonstrates how to use a linked list to organize the struct structure to manage hardware resources.

4. Concepts of system calls and library functions

  1. From the perspective of development, the operating system will appear as a whole to the outside world, but it will expose part of its interface for upper-layer development. This part of the interface provided by the operating system is called a system call .
  2. In terms of system calls, the functions are relatively basic, and the requirements for users are relatively high. Therefore, interested developers can properly encapsulate some system calls to form a library . With a library, it is very beneficial for higher-level users or developers to carry out secondary development.
    insert image description here

process

A process is an instance of a program running in a computer. It is the basic unit for resource allocation and scheduling by the operating system, and one of the most basic execution units in a computer system.

1. Concept

  1. Program : A program is an ordered collection of instructions to accomplish a specific task or solve a specific problem. It is usually stored on a storage medium such as a disk and is static.
  2. Process : A process is an execution process of a program. A process is formed when a program is loaded into memory and begins executing. A process is dynamic and has information such as running status, memory space, register set, program counter, etc.
  3. Multi-Processing : The operating system supports running multiple processes at the same time. Multi-process allows the computer to execute multiple tasks at the same time, and each task runs in an independent process without interfering with each other.
  4. Process state : A process can be in various states, such as running, ready, blocked, etc. The running state means that the process is executing, the ready state means that the process is ready to run but has not yet been scheduled, and the blocked state means that the process is temporarily unable to execute for some reason (such as waiting for input/output to complete).
  5. Process Control Block (PCB): Each process has a corresponding data structure called a process control block. The PCB contains all information about the process, such as process status, program counter, register contents, memory allocation, priority, etc. The operating system manages and controls the execution of the process through the PCB.
  6. Inter-process communication (IPC): Different processes may need to communicate with each other and share data. In order to realize the interaction between processes, the operating system provides various inter-process communication mechanisms, such as pipes, message queues, shared memory, and so on.
  7. Process scheduling : Multiple processes compete for CPU time to execute, and the operating system needs to perform process scheduling to determine which process should be currently executed. The design of the process scheduling algorithm affects the response performance and resource utilization of the system.

Summary :
A process is an execution process of a program and the basic unit of operating system resource management . Multiprocessing allows a computer to run multiple tasks simultaneously. Each process has its unique state, PCB and execution information, and the operating system determines how to allocate CPU time and memory through process scheduling. Inter-process communication allows data exchange between different processes. The concept of a process is an important foundation for understanding multitasking and resource management in an operating system.

2. Describe the process - PCB

PCB (Process Control Block) is a data structure used to represent the process in the operating system. Each process has a corresponding PCB, which contains all the information related to the process, so that the operating system can manage and control the execution of the process. PCBs are usually stored in the kernel's address space, not in the process's user space.

The information contained in the PCB can vary depending on the design of the operating system, but generally includes some key information as follows:

  1. Process ID (PID) : An integer value that uniquely identifies each process in the operating system and is used to identify and manage processes in the system.
  2. Process state : Indicates the current execution state of the process. Common states include running state (executing), ready state (ready to execute but not yet scheduled), blocked state (waiting for an event to complete, such as input/output), etc.
  3. Register collection : saves the contents of registers when the process is switched out of operation, including the program counter (PC), etc.
  4. Process priority : Used in the scheduling algorithm, indicating the priority of the process to be allocated CPU time.
  5. Memory management information : including process memory allocation, page table information, etc.
  6. File descriptor table : records the status and information of files and pipes opened by the process.
  7. Process statistics : such as running time, cumulative CPU time, IO operation times, etc.

The PCB containsProcess status, identification information, resource management information, context information, scheduling information, inter-process communication information, etc.. It is the basic unit for resource allocation and scheduling by the operating system, and the key data structure for the operating system to understand and manage processes.

Whenever the operating system performs a process switch, it will save the context information of the current process to its PCB, and then load the context information of the next process into the CPU register, so that the next process can continue to execute.

In the Linux operating system, the data structure used by the process control block (PCB) istask_struct. task_struct is a data structure representing a process in the Linux kernel, defined in the header file <linux/sched.h>. task_struct contains a large amount of information used to manage all aspects of the process.

In task_struct, it contains some of the key information mentioned above, and also contains other information related to Linux process management, such as scheduling information, signal processing information, users and groups to which the process belongs, and so on. Since Linux is an open source operating system, the structure of task_struct is very complicated and may be different in different versions of the kernel.

In general, PCB (or task_struct) is an important data structure used by the operating system to manage the process. It will be loaded into RAM (memory) and contains all the information related to the process, helping the operating system to manage the execution and resource allocation of the process .

task_ struct content classification

  1. Identifier : Describe the unique identifier of this process, which is used to distinguish other processes.
  2. Status : Task status, exit code, exit signal, etc.
  3. Priority : Priority relative to other processes.
  4. Program Counter : The address of the next instruction in the program to be executed.
  5. Memory pointers : including pointers to program code and process-related data, as well as pointers to memory blocks shared with other processes
  6. Context data : The data in the registers of the processor when the process is executed [example of suspension of studies, to add pictures of CPU, registers].
  7. I/O status information : including displayed I/O requests, I/O devices assigned to the process and a list of files used by the process.
  8. Billing information : May include sum of processor time, sum of clocks used, time limits, billing account number, etc.
  9. other information

Note :

  1. In computer architecture,The PC (Program Counter) in the CPU (Central Processing Unit) points to the address of the instruction currently being executed
  2. The PC is a special register that stores the address of the instruction to be executed. The PC plays a vital role in the operation of the CPU. When the CPU executes an instruction, it reads the instruction from the address pointed to by the PC, and performs the corresponding operation according to the instruction's opcode and operands.After the current instruction is executed, the PC will automatically increment to point to the address of the next instruction to be executed, so as to continue to execute the next instruction. In this way, the CPU can execute a series of instructions sequentially to complete the execution process of the program.
  3. The value of PC changes continuously during the execution of instructions, so that the CPU can execute instructions in sequence according to the sequence of the program, and realize the flow control of the program.In control structures such as conditional branches and loops, the value of the PC will be modified according to the result of the instruction execution, so as to jump to different instruction addresses and realize the jump and transfer of the program.
  4. PC saving and switching is key to multitasking.In a multitasking operating system, each process has its own PC value. When the operating system performs a process switch, it saves the PC of the current process into its process control block (PCB), and then loads the PC of the next process into the PC register of the CPU to continue executing the instructions of the process
  5. In short, the PC pointer in the CPU is used to store the address of the currently executing instruction, and it changes continuously during the execution of the instruction, controlling the flow and execution order of the program.

3. View process

Process information can ls /procbe viewed through the system folder
insert image description here

For example: To get the process information with PID 1, you need to check /proc/1this folder.
insert image description here

Most process information can also be obtained using topthese psuser-level tools.
top
insert image description here
top is often used to monitor the system status of Linux. It is a commonly used performance analysis tool that can display the resource usage of each process in the system in real time.
ps
insert image description here
But the ps command is usually not used in this way. First, create the following infinite loop code, so that we can check the process

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
    
    
 	while(1){
    
    
 	sleep(1);
 	}
 	return 0;
}

After the executable program is generated, we execute it and enter the command at the same timeps axj | head -1 && ps ajx | grep 'test'

insert image description here

4. Obtain the process identifier through the system call

First of all, we need to understand the two system call functions getpid()and getppid(), through the man tool, we can see the following definitions:

The header files required by the function are <sys/types.h>and<unistd.h>

getpid()Returns the process ID, ie PID
getppid()Returns the parent process ID, ie PPID

insert image description here
Next we call these two functions

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 int main()
  5 {
    
    
  6   while(1){
    
    
  7    printf("pid: %d\n", getpid());
  8    printf("ppid: %d\n", getppid());
  9    sleep(1);
 10   }                                                                                                         
 11  return 0;                                                  
 12 }

insert image description here
When the program is running, we enter instructions ls /proc/PID(PPID) -alto view the process information
of the
insert image description here
parent process of the child process
insert image description here
. We can see that the parent process is bash, which is the shell program of the Linux system. If we enter a command to kill -9 PIDkill the process, the parent process will not be affected, but if we enter the command kill -9 PPIDto kill the parent process, it is also the shell process.

insert image description here
Don’t panic if you accidentally kill the shell process, we just need to reconnect to the server and it’s ok, and we can see that the shell process killed in the right window will not affect the left window, because the Linux system will create different shell processes when establishing different connections, so the two connections do not interfere with each other. After we establish a few more connections and query the bash process, we will see that multiple bash processes are running with different PIDs.
Orderps axj | head -1 && ps ajx | grep 'bash'
insert image description here

5. Create a process through a system call - fork

First, let's take a look at the fork function.
insert image description here
Let's look at this magical code:

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 int main()
  5 {
    
    
  6    int ret = fork();
  7    if(ret < 0){
    
    
  8        perror("fork");
  9        return 1;
 10    }
 11    else if(ret == 0){
    
     //child
 12        printf("I am child : %d!, ret: %d\n", getpid(), ret);
 13    }
 14    else{
    
     //father
 15        printf("I am father : %d!, ret: %d\n", getpid(), ret);
 16    }
 17    sleep(1);
 18    return 0;
 19 }  

When we are learning C/C++ language, we all know that the function can only return one value, but the fork function can have two, let's see the following operation results

Orderwhile :; do ps axj | head -1 && ps ajx | grep test | grep -v grep;sleep 1;echo "--------------------------";done

insert image description here
So why is this? This is because when the fork function is executed, a child process is established, that is to say, two identical processes are executed here at the same time, and it is a parent-child relationship. The child process and the parent process share code, and the data each opens up space. We will talk about virtual addresses later in order to better explain.

process status

1. Linux kernel source code

In order to figure out what a running process means, we need to know the different states of the process. A process can have several states (in the Linux kernel, processes are sometimes called tasks).
The following states are defined in the kernel source code:

static const char * const task_state_array[] = {
    
    
"R (running)", 		/* 0 */
"S (sleeping)", 	/* 1 */
"D (disk sleep)", 	/* 2 */
"T (stopped)", 		/* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", 		/* 16 */
"Z (zombie)", 		/* 32 */
};
  1. R running status (running) : It does not mean that the process must be running, it indicates that the process is either running or in the run queue.
  2. S sleeping state (sleeping) : means that the process is waiting for the event to complete (sleep here is sometimes called interruptible sleep (interruptible sleep)).
  3. D disk sleep state (Disk sleep) : sometimes called uninterruptible sleep state (uninterruptible sleep), the process in this state usually waits for the end of IO.
  4. T stop state (stopped) : You can stop (T) the process by sending a SIGSTOP signal to the process. The suspended process can be resumed by sending the SIGCONT signal.
  5. X dead state (dead) : This state is just a return state, you will not see this state in the task list.
  6. Z zombie state (zombie) : Zombie state (Zombies) is a relatively special state. When the process exits and the parent process (using the wait() system call)
    does not read the return code of the child process exit, a zombie (corpse) process will be generated. A zombie process will remain in the process table in a terminated state, and will keep waiting for the parent process to read the exit status code. Therefore, as long as the child process exits, the parent process is still running, but the parent process does not read the state of the child process, and the child process enters the Z state

2. View process status

R running status

  1 #include <stdio.h>  
  2 #include <sys/types.h>  
  3 #include <unistd.h>  
  4 int main()  
  5 {
    
    
  6    while(1)
  7    {
    
    
  8                                                                                                                                                                                                                                  
  9    }
 10    return 0;
 11 }

insert image description here
S sleep state

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 int main()
  5 {
    
    
  6    while(1)
  7    {
    
    
  8       sleep(1);                                                                                             
  9    }                                                             
 10    return 0;                                                     
 11 }  

insert image description here
The code of the stopped state (stopped)
is the same as above. When running, we enter the command to suspend the process kill -19 PID, and then restore the process kill -18 PID
insert image description here
Z zombie state
. First, let’s create an example of a zombie process that lasts for 30 seconds:

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 int main()
    4 {
    
    
    5    pid_t id = fork();
    6    if(id < 0){
    
    
    7        perror("fork");
    8        return 1;
    9    }
   10    else if(id > 0){
    
     //parent
   11        printf("parent[%d] is sleeping...\n", getpid());
   12        sleep(30);
   13   }else{
    
    
   14       printf("child[%d] is begin Z...\n", getpid());
   15       sleep(5);
   16       exit(EXIT_SUCCESS);
   17         
   18   }
   19    return 0;
   20  } 

Input monitoring instructions while :; do ps aux | grep test | grep -v grep; sleep 1;echo "----------------------------"; done
insert image description here
A zombie process is a child process that has finished executing, but its process control block (PCB) remains in the system, waiting for the parent process to call the system call wait() or waitpid() to obtain its exit status . Although the existence of zombie processes will no longer consume CPU time or other resources, they still have certain hazards and potential problems, mainly in the following aspects:

  1. Waste of resources : The PCB of the zombie process still occupies system kernel resources, especially in the case of a large number of zombie processes, it will waste the space of the system memory and the process table.
  2. Limit on the number of processes : The system has a limit on the number of processes that exist at the same time. If there are too many zombie processes backlogged, the system may reach the limit on the number of processes, which will affect the normal operation of the system.
  3. Parent process resource leak : If the parent process does not properly handle the zombie process and does not call wait() or waitpid() to reclaim the child process resources, it may cause resource leaks in the parent process, especially resources such as file descriptors are not released.
  4. May lead to resource exhaustion : If there are too many zombie processes and the parent process does not process them, the process table resources may be exhausted, resulting in the inability to create other legitimate processes.

The way to solve the zombie process problem is that the parent process calls wait() or waitpid() and other system calls after the child process exits to reclaim the resources of the child process and obtain its exit status. Through such processing, the operating system will remove the PCB of the zombie process from the process table, avoiding resource waste and potential problems. If the parent process does not care about the exit status of the child process, you can also use the WNOHANG parameter of the waitpid() function to make waitpid() a non-blocking mode, even if the child process has not exited, it will not block the execution of the parent process.

In general, although zombie processes will not cause huge direct harm to the system, they may cause problems such as resource waste, process number limitation, and parent process resource leakage. Therefore, when writing programs, you should correctly handle the exit status of child processes to avoid excessive zombie processes.

Note:

  1. The X death state (dead) is the state at the end of the program, which is not easy to demonstrate.
  2. It is also inconvenient to display the sleep state of the D disk. Under normal circumstances, the number of processes in the D state should be relatively small. However, we can ps aux | grep "^D"display all D state processes of the system through commands.
  3. The process is in the R state, not necessarily running on the CPU. The process is in the running queue, which is the R state (the process is ready and waiting for scheduling)
  4. The S state is shallow sleep (responsive to external events), which is the state in most cases
  5. D state is deep sleep (it cannot be killed, even if it is an operating system, it can only wait for the process in D state to wake up automatically, or shut down and restart (may be stuck))
  6. S state and D state are called waiting states
  7. When the process exits, it generally does not let the OS recycle the information immediately and release all the resources of the process

After the process exits, the operating system usually does not reclaim all the resources of the process immediately. Instead, it leaves it around for a while to give the parent process or other related mechanisms a chance to process information about the exiting process, possibly doing resource reclamation and aftermath.
When the process exits, the operating system will do the following operations:
--------terminate the execution of the process: the operating system will immediately stop the execution of the process and no longer allocate CPU time to the process.
--------Save the exit status in the PCB: The operating system will save the exit status (exit code) of the process in the process control block (PCB), so that the parent process or other processes can query and process it.
--------Process resource recovery: The operating system will reserve process resources for a period of time, including memory, file descriptors, etc., so that other processes or parent processes can obtain information about the exited process or deal with the aftermath. This period is also known as "Zombie State".
--------Send a signal to the parent process: The operating system will send a SIGCHLD signal to the parent process to notify the parent process that the child process has exited, and the exit status can be queried.
The parent process usually calls the system call wait() or waitpid() to wait for the exit of the child process and obtain its exit status. Once the parent process calls these functions, the operating system reclaims the resources of the zombie process and removes the process PCB from the process table.
When the parent process does not handle the exit status of the child process, if the child process exits, but the parent process does not call wait() or waitpid(), the child process will enter the zombie state, the PCB remains in the system, but the resources are not recycled, which will lead to resource waste.
Therefore, the parent process should handle its exit status in time after the child process exits, to ensure that the resources of the child process are recovered correctly, and to avoid the generation of zombie processes.

The difference between normal status and + status
insert image description here
When the background is running, we can enter the command jobsto view the status of the background process fg+对应序号and turn the background to the foreground
insert image description here
kill command
Used to delete a program or job in progress
Instruction: kill [-s <信息名称或编号>][PID] 或 kill [-l <信息编号>]
insert image description here
Connecting the number is the corresponding function, for example, kill -9 PIDit is to terminate the process directly. It should be noted that the D state and Z state cannot be killed by the kill command.

Process state diagram
insert image description here
Orphan process
** Orphan Process (Orphan Process) ** refers to theAfter the parent process terminates or exits unexpectedly, the child process continues to run, but at this time its parent process no longer exists. The orphan process will be taken over by the operating system, and the systemd process (process No. 1 in centos6.5 is initd) (usually the process with PID 1) becomes its new parent process.

Normally, when a process creates a child process, the parent process waits for the child process to complete, and calls system calls such as wait() or waitpid() to obtain the exit status of the child process. However, if the parent process exits unexpectedly or terminates prematurely, the child process becomes an orphan process.

The creation mechanism of an orphan process mainly involves the following steps:

  1. A parent process creates a child process.
  2. The parent process exited or was terminated unexpectedly, unable to handle the exit status of the child process.
  3. The child process continues to run in the system, but its parent process no longer exists and becomes an orphan process.
  4. The operating system sets the parent process ID (PPID) of the orphan process to the process ID of the systemd process, and the systemd process becomes the new parent of the orphan process.

Since the init process periodically calls wait() or similar system calls to handle terminated child processes, the exit status of the orphaned process is eventually handled and its resources reclaimed. Therefore, orphan processes do not cause resource leaks like zombie processes.

All in all, an orphan process refers to a process that is still running in the system after its parent process terminates, and its parent process no longer exists. The operating system will set the parent process ID of the orphan process to the process ID of the systemd process, making the systemd process the new parent of the orphan process, and finally handle the exit status of the orphan process.

Example:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    
    
 	pid_t id = fork();
 	if(id < 0){
    
    
		perror("fork");
 		return 1;
	}
 	else if(id == 0){
    
    //child
 		printf("I am child, pid : %d\n", getpid());
 		sleep(10);
 	}
 	else{
    
    //parent
 		printf("I am parent, pid: %d\n", getpid());
 		sleep(3);
 		exit(0);
 	}
 return 0;
}

insert image description here
We use the top command to check that the process with PID 1 is the operating system. All orphan processes are adopted by the systemd process No. 1, and of course they are also recycled by the systemd process
insert image description here

process priority

1. Basic concepts

Process priority is an important concept used in operating systems to schedule process execution.Each process has a corresponding priority, which determines the priority level of the process to obtain CPU time slices in a multi-tasking environment. The adjustment of process priority can affect the execution order of processes, thereby affecting the performance and responsiveness of the system.

In a multitasking operating system, CPU time is divided into small time slices and allocated to different processes according to a certain scheduling algorithm. A scheduling algorithm decides which process should be running at a given moment based on the priority of the process. Higher-priority processes get priority CPU time when scheduled, while lower-priority processes may have to wait longer for a chance to execute.

Process priority is usually set and adjusted by the operating system according to some rules. Some common prioritization rules include:

  1. Static priority: Specified when the process is created, usually by the programmer or system administrator, indicating the fixed priority of the process.
  2. Dynamic Priority: Dynamically adjusts based on process behavior and resource usage. For example, some operating systems may dynamically adjust the priority according to the CPU usage time of the process, I/O requests, etc., to achieve fair resource allocation.
  3. Real-time priority: Applicable to real-time systems to ensure the response time of specific tasks. Real-time processes usually have a higher priority to ensure that they can respond to specific events in a timely manner.

Reasonable adjustment of process priority can optimize system performance and response time. For example, when responding to user interaction operations, processes related to the user interface can be set to a higher priority to ensure that the system can respond to user operations in a timely manner. For some resource-intensive tasks, you can set their priority to be low, so as not to affect the execution of other tasks by taking up too much CPU time.

It should be noted that priority adjustment needs to be done carefully. Too high a priority may cause other tasks to not get enough execution time, while too low a priority may cause some tasks to fail to respond in time, affecting the overall performance of the system. Therefore, when performing priority adjustment, the importance and resource requirements of different tasks need to be carefully considered to balance system performance and resource utilization.

2. Check the system process priority

First enter the command ps -l
insert image description here
We can easily notice several important information:

name effect
UID The identity of the representative executor
PID The code name representing this process
PPID Represents which process this process is derived from, that is, the code name of the parent process
AT Represents the priority that this process can be executed, the smaller the value, the earlier it will be executed
IN Represents the nice value of this process

3. PRI and NI

In the Linux system, the priority is jointly determined by the value of pri and nice (the smaller the value of the priority, the higher the priority; the larger the value of the priority, the lower the priority)

PRI (Priority): Indicates the static priority of the process, which is an integer value, the default value of the benchmark is 80, and the default range is 60 to 99 (for ordinary processes) . The smaller the value, the higher the priority. Negative numbers indicate high priority, positive numbers indicate low priority. For processes created by ordinary users, the default priority is 0. If a process has a negative priority value, it means it is a real-time process.

NI (Nice Value): Indicates the dynamic priority of the process, which is also an integer value, between -20 and +19 . The higher the number, the lower the priority. NI values ​​can be adjusted at runtime by the user or administrator to affect the scheduling priority of a process.

The smaller the PRI value, the faster it will be executed. After adding the nice value, the PRI will become: PRI(new)=PRI(old)+nice

In this way, when the nice value is negative, the priority value of the program will become smaller, that is, its priority will become higher, and the sooner it will be executed

Therefore, adjusting the process priority, under Linux, is to adjust the process nice value

It should be emphasized that the nice value of a process is not the priority of the process. They are not a concept, but the nice value of a process will affect the change of the
priority of the process. It can be understood that the nice value is the correction data of the process priority

Example:
First, we generate and run an executable program with an endless loop, check its PID, and press “r” after
insert image description here
entering the second step top–> enter the process PID –> enter the nice value to
insert image description here
exit and check the PRI
insert image description here
, and we can see the 100 we entered, but normally we can add up to 99, and the same is true for raising the priority, the lowest can only be 60, but it should be noted that no matter when the addition or subtraction is performed, it is added or subtracted from the base PRI (80), and root privileges are required to increase the priority (that is, NI is a negative value).
insert image description here

environment variable

1. The basic concept of environment variables

**Environment variables (environment variables)** is a mechanism used in the operating system to configure the operating environment of the program. It is some key-value pairs (Key-Value pairs) defined by the operating system or user, where the key represents the name of the environment variable, and the value represents the content of the environment variable. Environment variables usually take effect in the global scope of the operating system and are used to store configuration information, paths, language settings, system resources, etc. required for program operation.

  1. Environment variables generally refer to some parameters used in the operating system to specify the operating environment of the operating system
  2. For example, when we write C/C++ code, we never know where our linked dynamic and static libraries are when we link, but we can still link successfully and generate executable programs because there are relevant environment variables to help the compiler find them.
  3. Environment variables usually have some special purposes, and usually have global characteristics in the system

The advantage of environment variables is that they can be used to pass configuration information to a program without modifying the program's source code. This makes programs more flexible and can be run in different environments without recompilation. Many programs and the operating system itself use environment variables to configure their behavior and settings. For example, the PATH environment variable is used to specify the path where the system's executable program is located, so that users can run the program directly in the terminal without specifying an absolute path.

2. Common environment variables and how to view environment variables

Common Environment Variables

PATH: The search path for the specified command
HOME: Specify the user's main working directory (that is, the default directory when the user logs in to the Linux system)
SHELL: The current Shell, its value is usually /bin/bash.

View environment variables

echo $环境变量名称
insert image description here
When we create an executable file, why do ./we need to export PATH=$PATH:程序所在路径add it to run, but other instructions and programs do not need ./it?

3. Commands and organization related to environment variables

Order

  1. echo: display the value of an environment variable
  2. export: set a new environment variable
  3. env: display all environment variables
  4. unset: clear environment variables
  5. set: display locally defined shell variables and environment variables

insert image description here
Each program will receive an environment table, which is an array of character pointers, and each pointer points to an environment string ending with '\0'

4. Obtain environment variables through code

①The third parameter of the main function
First of all, let's introduce the first two parameters int argc, char *argv[]
among them:

argc (Argument Count) : Indicates the number of command line arguments, including the program itself. That is, the value of argc is at least 1.
argv (Argument Vector) : It is an array of pointers, each of which points to a string representing the command line parameters.

For example the following code:

#include <stdio.h>

int main(int argc, char *argv[]) {
    
    
    printf("Number of command-line arguments: %d\n", argc);
    for (int i = 0; i < argc; i++) {
    
    
        printf("Argument %d: %s\n", i, argv[i]);
    }

    return 0;
}

When running we enter the command ./program arg1 arg2 arg3 output will be:

Number of command-line arguments: 4
Argument 0: ./program
Argument 1: arg1
Argument 2: arg2
Argument 3: arg3

Let's look at the third parameterchar *env[]

env(Environment Variable) : It is an array of pointers, each of which points to an environment variable string.

Through the env parameter, the program can obtain and modify the environment variables of the current process. Environment variables are usually used to set configuration information, paths, etc. of programs. In some specific programming languages ​​and operating systems, the main function supports such parameter forms.

The following is a simple example showing how to use the env parameter to get environment variables in C language:

#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
    
    
 	int i = 0;
 	for(; env[i]; i++){
    
    
		printf("%s\n", env[i]);
 	}
 	return 0;
}

insert image description here

② Acquired through the third-party variable environ

The global variable environ defined in libc points to the environment variable table, and environ is not included in any header file, so it must be declared with extern when using it

code show as below:

#include <stdio.h>
int main(int argc, char *argv[])
{
    
    
 	extern char **environ;
 	int i = 0;
 	for(; environ[i]; i++){
    
    
 		printf("%s\n", environ[i]);
 	}
 	return 0;
}

The result is the same as the first one
③ Obtaining environment variables through system calls
getenv()函数
insert image description here

code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    
 	printf("%s\n", getenv("PATH"));
 	return 0;
}

insert image description here

Environment variables usually have global attributes and can be inherited by child processes.
For example, the following code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    
 	char * env = getenv("MYENV");
 	if(env){
    
    
 		printf("%s\n", env);
 	}
 	return 0;
}

It is definitely useless to run directly, because there is no such global variable, but we input the command export MYENV="hello world" to export the environment variable, and run it again, there will be an output result, which shows that the environment variable can be inherited by the child process!

process address space

Based on Linux kernel 2.6.32
32-bit platform

1. Memory distribution

Normally, when we learn C/C++, we learn about the memory distribution as shown in the figure below.
insert image description here
Let's learn more about the memory structure through the following code.

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 
    4 int g_val=100;
    5 int g_unval;
    6 
    7 int main(int argc,char* argv[],char* env[])
    8 {
    
    
    9     printf("code addr         :%p\n",main);
   10     const char* p="hello";
   11     printf("read only         :%p\n",p);
   12     printf("global val        :%p\n",&g_val);
   13     printf("global uninit  val:%p\n",&g_unval);
   14     char* q1=(char*)malloc(10);
   15     char* q2=(char*)malloc(10);
   16     char* q3=(char*)malloc(10);
   17     char* q4=(char*)malloc(10);
   18     printf("heap addr         :%p\n",q1);
   19     printf("heap addr         :%p\n",q2);
   20     printf("heap addr         :%p\n",q3);
   21     printf("heap addr         :%p\n",q4);
   22 
   23     printf("stack addr        :%p\n",&q1);
   24     printf("stack addr        :%p\n",&q2);
   25     printf("stack addr        :%p\n",&q3);
   26     printf("stack addr        :%p\n",&q4);
   27 
   28     static int i=0;
   29     printf("static addr       :%p\n",&i);
   30     printf("args addr         :%p\n",argv[0]);
   31     printf("env addr          :%p\n",env[0]);                                                             
   32     return 0;                       
   33 } 

insert image description here

2. What is a process address space

Let's look at the following code

    1 #include <stdio.h>
    2 #include <unistd.h>
    3 #include <stdlib.h>
    4 int g_val = 100;
    5 int main()
    6 {
    
    
    7 		pid_t id = fork();
    8   	if(id < 0)                                                                                              
    9   	{
    
    
   10   		perror("fork");
   11    		return ;
   12   	}
   13   	else if(id == 0)
   14   	{
    
     //child
   15    	
   16    		g_val = 50;
   17    		printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
   18   	}
   19   	else
   20   	{
    
     //parent
   21    
   22    		sleep(3);
   23   		printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
   24   	}
   25   	sleep(1);
   26   	return 0;
   27 }

When we create a child process, we modify the value of the global variable in the child process, and then let the parent process run. We look at the output and then we
insert image description here
find an amazing result. When the child process runs first, the global variable is modified, and the parent process is run later, but the global variable is still the initial value. The most surprising thing is that they have the same address.

Here we want to propose a concept: process address space (that is, what we often call virtual memory)

In fact, when we generate each C/C++ program, he has already allocated addresses for all content, and we can input instructions objdump -afh 程序名
insert image description here
. Virtual memory allows multiple processes to run at the same time, and each process has its own independent address space, thereby achieving isolation and protection between processes.

The main idea of ​​virtual memory is to combine physical memory and disk space to form a continuous address space called virtual address space. This virtual address space is independent for each process, and each process thinks that it is exclusively using the entire address space without caring about the existence of other processes.

When a process accesses a virtual address space, the operating system maps the virtual address to a corresponding location in physical memory or on disk through a hardware component called the memory management unit (MMU). If the required data is in physical memory, then the access is done immediately. If the required data is not in physical memory, the operating system will replace (swap out) the infrequently used data from physical memory to a specific area on the disk, and then read the required data from the disk into physical memory (swap in). This process is transparent and invisible to the process.

So in the eyes of each process, only its own existence exists in the entire memory.

3. Why there is an address space

Address spaces exist for memory management and process isolation.

  1. Memory management : The address space allows the operating system to efficiently manage the computer's memory resources. Through the address space, the operating system can keep track of which memory addresses are used, which are free, and which belong to different processes. When a process needs to access memory, the operating system converts the virtual address into a physical address through address mapping to ensure that the memory accessed by the process is legal and will not conflict with the memory of other processes.
  2. Process isolation : The address space realizes the isolation between processes. Each process has its own independent address space, so that the data and instructions between the processes are isolated from each other and do not interfere with each other. This way, even if one process fails or crashes, it won't affect other processes. Isolation between processes also provides greater system stability and security.
  3. Multiprogramming : The address space enables the operating system to switch between multiple processes, enabling multiprogramming. By storing the address space of a process in memory, the operating system can suspend the execution of one process, save its state, and then resume the execution of another process, thereby enabling concurrent execution of multiple processes and improving system utilization and responsiveness.
  4. Virtualization : The address space is virtualized, making each process think that it is monopolizing the entire memory space. The address accessed by the process is a virtual address, and there is no need to care about the layout of the actual physical memory. This virtualization makes programming and application development easier, regardless of the actual physical memory location.

Generally speaking, the address space is the basis of memory management and process isolation in computer systems. It provides an effective memory management mechanism for the operating system, and at the same time ensures data security and isolation between different processes. The existence of the address space enables the computer system to run multiple processes at the same time, which improves the efficiency and flexibility of the system.

The definition of address space in the Linux kernel

struct mm_struct {
    
    
    struct vm_area_struct * mmap;      /* 映射区域列表的头部 */
    struct rb_root mm_rb;             /* 存储映射区域的红黑树 */
    struct vm_area_struct *mmap_cache; /* 最后查找的映射区域 */
    unsigned long (*get_unmapped_area)(struct file *filp, unsigned long addr,
                                       unsigned long len, unsigned long pgoff,
                                       unsigned long flags);
    pgd_t * pgd;                       /* 页全局目录(Page Global Directory) */
    atomic_t mm_users;                 /* 进程共享的计数器 */
    atomic_t mm_count;                 /* 进程描述符的引用计数 */
    int map_count;                     /* 映射区域的计数器 */
    rwlock_t page_table_lock;          /* 页表的自旋锁 */
    struct rw_semaphore mmap_sem;      /* mmap_sem 用于对进程的映射区域进行保护 */
    spinlock_t page_table_lock;        /* 页表锁,保护页表修改 */
    struct list_head mmlist;           /* 进程的链表节点,链接到所有进程的mm_struct列表 */
    unsigned long start_code, end_code;/* 进程代码段的起始地址和结束地址 */
    unsigned long start_data, end_data;/* 进程数据段的起始地址和结束地址 */
    unsigned long start_brk, brk;      /* 进程堆的起始地址和当前堆顶地址 */
    unsigned long arg_start, arg_end;  /* 进程参数的起始地址和结束地址 */
    unsigned long env_start, env_end;  /* 进程环境变量的起始地址和结束地址 */
    unsigned long saved_auxv[AT_VECTOR_SIZE]; /* 辅助向量 */
    unsigned long rss;                 /* resident set size, 进程使用的物理页数量 */
    unsigned long total_vm;            /* 进程总共的虚拟页数量 */
    unsigned long locked_vm;           /* 锁定的虚拟页数量 */
    unsigned long pinned_vm;           /* 固定的虚拟页数量 */
    unsigned long shared_vm;           /* 共享的虚拟页数量 */
    unsigned long exec_vm;             /* 程序映像的虚拟页数量 */
    unsigned long stack_vm;            /* 栈的虚拟页数量 */
    unsigned long def_flags;           /* 默认页标志 */
    unsigned long nr_ptes;             /* 被映射页表项的数量 */
    unsigned long nr_pmds;             /* 被映射页中间表项的数量 */
    unsigned long dummy_page;          /* 空页表项指针 */
    struct vmacache vmacache_seqnum;   /* 虚拟页的页表缓存 */
    struct vmacache vmacache;          /* 虚拟页的页表缓存 */
    struct rw_semaphore pagetable_rwsem; /* 用于页表的读写信号量 */
    struct page ** pmd_huge_pte;       /* Huge PMD页表项 */
    int map_count;                     /* 映射区域的计数器 */
    struct list_head mmlist;           /* 进程的链表节点,链接到所有进程的mm_struct列表 */
    unsigned long start_code, end_code;/* 进程代码段的起始地址和结束地址 */
    unsigned long start_data, end_data;/* 进程数据段的起始地址和结束地址 */
    unsigned long start_brk, brk;      /* 进程堆的起始地址和当前堆顶地址 */
    unsigned long arg_start, arg_end;  /* 进程参数的起始地址和结束地址 */
    unsigned long env_start, env_end;  /* 进程环境变量的起始地址和结束地址 */
    unsigned long saved_auxv[AT_VECTOR_SIZE]; /* 辅助向量 */
    unsigned long rss;                 /* resident set size, 进程使用的物理页数量 */
    unsigned long total_vm;            /* 进程总共的虚拟页数量 */
    unsigned long locked_vm;           /* 锁定的虚拟页数量 */
    unsigned long pinned_vm;           /* 固定的虚拟页数量 */
    unsigned long shared_vm;           /* 共享的虚拟页数量 */
    unsigned long exec_vm;             /* 程序映像的虚拟页数量 */
    unsigned long stack_vm;            /* 栈的虚拟页数量 */
    unsigned long def_flags;           /* 默认页标志 */
    unsigned long nr_ptes;             /* 被映射页表项的数量 */
    unsigned long nr_pmds;             /* 被映射页中间表项的数量 */
    unsigned long dummy_page;          /* 空页表项指针 */
    struct vmacache vmacache_seqnum;   /* 虚拟页的页表缓存 */
    struct vmacache vmacache;          /* 虚拟页的页表缓存 */
    struct rw_semaphore pagetable_rwsem; /* 用于页表的读写信号量 */
    struct page ** pmd_huge_pte;       /* Huge PMD页表项 */
    struct mm_rss_stat rss_stat;       /* 进程内存使用的统计信息 */
    unsigned int def_flags;            /* 默认页标志 */
    pgd_t *pgd;                        /* 页全局目录表项指针 */
    struct mm_struct *mm;              /* 进程所属的mm_struct */
    unsigned long start_brk;           /* 堆的起始地址 */
    unsigned long brk;                 /* 当前堆的顶部地址 */
    unsigned long start_stack;         /* 栈的起始地址 */
    unsigned long arg_start;           /* 参数区的起始地址 */
    unsigned long arg_end;             /* 参数区的结束地址 */
    unsigned long env_start;           /* 环境变量区的起始地址 */
    unsigned long env_end;             /* 环境变量区的结束地址 */
    unsigned long saved_auxv[AT_VECTOR_SIZE];

4. Page table

The process in the virtual memory is mapped to the physical memory through the page table, so what is the page table?

Page Table (Page Table) is a data structure used in the computer operating system to realize the translation from virtual address to physical address. In a system using virtual memory, each process has its own virtual address space, and the virtual address needs to be mapped to an actual physical address through the page table to locate data in memory.

When a process accesses a virtual address, the operating system's memory management unit (MMU) uses page tables to perform address translation. The page table records the mapping relationship between virtual addresses and corresponding physical addresses. It divides the virtual address into pages of fixed size (usually 4KB or 2MB), and then maps these pages to corresponding locations in physical memory.

The page table is usually stored in the form of a table, where each row corresponds to a virtual page, including the mapping relationship between virtual addresses and physical addresses. The upper part of the virtual address is used to look up the row in the page table, while the lower part is used to locate the actual physical address in the corresponding page.

The page table is usually stored in the computer's main memory (physical memory), specifically, the page table is stored in the kernel address space of the process. Each process has its own page table at runtime, which is used to map virtual addresses in its virtual address space to actual physical addresses.

In a 32-bit system, the page table is generally stored in a specific area in the address space of the operating system kernel. For example, the common location in the Linux kernel is an address space starting from 0xC0000000 (3GB). This area is called the kernel space and is used to store the code, data and data structures of the operating system kernel, including page tables. The user space of a process usually starts from 0, and the virtual address ranges from 0 to 3GB in user space.

On 64-bit systems, page tables may be stored differently due to the wider address space. However, the page table is generally stored in the kernel address space, while the user space will occupy a larger range.

Page tables are usually dynamically created and managed by the operating system, and there are separate page tables for each process. When a process is switched, the operating system will switch the corresponding page table to provide each process with an independent virtual address space. The management and switching of page tables is one of the core functions of operating system memory management, which enables the virtual memory system to efficiently implement isolation and memory sharing between processes.

In a virtual memory system, common page table structures include:

  1. Single-level page table : Use a separate table to store the mapping relationship between all virtual pages and physical pages. This method is simple and direct, but for a large address space, a large page table is required, which may cause space waste.
  2. Multi-level page table : The page table is divided into multiple levels, and each level contains a mapping of a part of virtual address and physical address. Through the multi-level page table, the size of the page table can be effectively reduced and the memory space can be saved. Common multi-level page table structures include two-level page tables and three-level page tables.
  3. Reverse page table : Usually used to deal with large physical memory. The reverse page table maps physical pages to virtual pages, so that the virtual address corresponding to the physical address can be looked up more quickly.

The translation of virtual addresses to physical addresses is a core part of operating system memory management. The use of page tables allows the operating system to implement transparent address translation between virtual memory and physical memory, making the process feel that it is monopolizing the entire memory space, improving system flexibility and resource utilization.

In the Linux kernel, page tables are implemented through multi-level page tables. Each process has its own separate page table for mapping virtual addresses to physical addresses. The data structure of the page table mainly includes the following levels:

pgd_t (Page Global Directory) : Page global directory, the top-level page table, used to map virtual addresses to the intermediate page table pmd.
pmd_t (Page Middle Directory) : The middle directory of the page, the second-level page table, used to map the virtual address to the underlying page table pte.
pte_t (Page Table Entry) : page table entry, the bottom page table, used to map virtual addresses to physical addresses.

Definitions of these page table data structures can be found in the Linux kernel header file linux/pgtable.h.

5. Understand the process address space

insert image description here
Through the above picture combined with the previous knowledge, it is not difficult to understand why the same code and the same memory address of the child process and the parent process can have different results, or why the previous fork function can have two return values. The same variable with the same address is actually the same virtual address, and the different contents are actually mapped to different physical addresses!

Therefore, when each process is established, the content in it has its corresponding virtual address, and then the memory is accessed through the page table. The page table finds the appropriate physical address through mapping and then runs. The process virtual address in the page table and the corresponding physical address can achieve better interaction.

Summarize

Interested friends can pay attention to the author, if you think the content is good, please give a one-click triple link, you crab crab! ! !
It is not easy to make, please point out if there are any inaccuracies
Thank you for your visit, UU watching is the motivation for me to persevere.
With the catalyst of time, let us all become better people from each other! ! !
insert image description here

Guess you like

Origin blog.csdn.net/kingxzq/article/details/131809686