Linux | Process address space

Table of contents

Preface

1. Initial process address space

1. Experimental introduction

2. Virtual address space

2. What is process address space?

1. Basic concepts 

2. In-depth understanding of process address space

3. The nature of process address space

4. Solve the remaining problems

3. Why is there a process address space?

1. Knowledge expansion

2. The significance of process address space

3. Re-understand suspension


Preface

         This chapter mainly introduces the concepts related to the process address space. We introduce our process address space from an experiment, and then step by step to understand the process address space in depth and refine the surrounding concepts;

1. Initial process address space

1. Experimental introduction

        We have the following code to observe the code running phenomenon;

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int g_val = 10; // 已初始化全局变量 
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        // fork执行失败
        perror("fork");
        exit(-1);
    }
    else if(id == 0)
    {
        // 子进程
        int cnt = 0;
        while(1)
        {
            if(cnt == 5)
            {
                g_val = 20;
            }
            printf("我是子进程,g_val:%d, &g_val:%p\n", g_val, &g_val);
            cnt++;
            sleep(1);
        }
    }
    else 
    {
        // 父进程
        while(1)
        {
            printf("我是父进程,g_val:%d, &g_val:%p\n", g_val, &g_val);
            sleep(1);
        }
        
    }                                                                                                                                                                                   
    return 0;
}

        We compile and run the above code, and the results are as follows;

        This is exactly the same as the problem left by the return value of the fork function before. Why does this magical phenomenon occur? This article mainly explores this;

2. Virtual address space

        I don’t know if you have seen the picture below in your previous studies (under 32-bit machine);

        I think this is a picture that every computer student should have seen. This is our core virtual address space today. We divide the space as above and allocate addresses. We can write the corresponding code based on the above picture. Is it as shown in the figure? shown;

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

// 未初始化全局变量
int g_unval;
// 已初始化全局变量
int g_val = 10;

int main(int argc, char* args[], char* env[])
{
    // 代码段
    printf("code addr: %p\n", main);
   
    // 常量
    const char* str = "hello world";
    printf("constant quantity: %p\n", str);
    
    // 已初始化全局变量
    printf("init global var: %p\n", &g_val);
    // 未初始化全局变量
    printf("uninit global var: %p\n", &g_unval);
    // 堆
    char* p1 = (char*)malloc(10);
    char* p2 = (char*)malloc(10);
    char* p3 = (char*)malloc(10);
    printf("heap: %p\n", p1);
    printf("heap: %p\n", p2);
    printf("heap: %p\n", p3);
    // 栈 
    printf("stack: %p\n", &p1);
    printf("stack: %p\n", &p2);                                                                                                                        
    printf("stack: %p\n", &p3);

    // 命令行参数与环境变量
    printf("args[0]: %p\n", args[0]); 
    printf("args[1]: %p\n", args[1]); 
    printf("args[2]: %p\n", args[2]);

    printf("env[0]: %p\n", env[0]);
    printf("env[1]: %p\n", env[1]);
    printf("env[2]: %p\n", env[2]);
    return 0;
}

        The test results are shown in the figure below;

        The results are the same as we expected. The overall address is increasing. It is the same as our process address space distribution diagram above. This is the test result under Linux. The test results under window may be different. This may be due to the compiler. The result of optimization;

2. What is process address space?

1. Basic concepts 

        The process address space is the memory space seen from the perspective of the process. In fact, we will record the mapping from virtual address to physical address through a data structure;

2. In-depth understanding of process address space

        To understand this, we first pull the timeline back to the past. When computers first started, there was no concept of process address space. The programs we wrote directly used the physical address of the memory to access the data on the memory; as shown in the figure below;

        At this time, we want to execute a program. We first load the executable program into the memory and generate the corresponding PCB control block, which is queued in the ready queue under the CPU and waits for scheduling. It seems that there is no problem. If we call A at this time At this time, program A accessed out of bounds and directly modified the code of our process B, causing process B to directly crash. At this time, where does the independence of the process come from? The independence of the process completely depends on the correctness of the programmer's code; therefore, this kind of Letting a process directly access the real physical address is unreliable;

        Our modern computers will not use the above strategy. We introduce a virtual address so that the program cannot directly access the real physical memory, as shown in the figure below;

        When our program is loaded into memory, first, the operating system will generate the PCB control block (task_struct), process address space (mm_struct) and user-level page table corresponding to the process. Together, we call it a process, that is, process = kernel data Structure + code and data; for each process, they think that their addresses are from 0x0000 0000 to 0x FFFF FFFF. These addresses are virtual addresses. The CPU maps these virtual addresses to real physical addresses through the page table. Manipulate memory data;

        The problem is, an extra virtual address is created. In the end, the data in the memory is not accessed by mapping the virtual address to the physical address. So the virtual address is also an illegal address? Wouldn’t that also be a cross-border visit?

        In fact, if the virtual address is also an illegal address, it will be found in the page table and will not be mapped to an illegal physical address at all, and it will not affect other processes;

3. The nature of process address space

        Think about it carefully, since each process must be allocated a process address space, there cannot be only one process in the memory, so there cannot be only one process address space. Since there are many, does our operating system need to separate these processes? The address space is managed, so how to manage these process address spaces? Similarly, "describe first, then organize", we first use a structure to describe the process address space, and then use a data structure to organize these structures, so that we can add, delete, check and modify these structures. Is it the same as the operating system's management of PCB control blocks? Under Linux, this data structure is called mm_struct;

        How to describe it? Let's think about it, isn't the process address space just one area after another? Then what we can be sure of is that partitioning will definitely be carried out, so how to carry out partitioning? Isn't it just that the actual position of a record is the end position of the record? As follows;

strcut mm_strcut
{
    // 代码段
    int code_start, code_end;
    // 栈区
    int stack_start, stack_end;
    // 堆区
    int heap_start, heap_end;
    // 等等... 其他属性

};

        Doesn’t this describe the process address space? Will the stack area and heap area grow? How to maintain? Can't we just change its start or end value directly? The process address space is actually a structure, and the PCB control block task_struct we learned before also saves the pointer of mm_struct;

4. Solve the remaining problems

        When we started the experiment, we had the same variable and the same address. Why did it have different values? There is also the return value of the fork function. Why does one variable have two values? These questions are easy to answer, as shown below;

        When a child process is created, if no process modifies g_val, that is, in the first 5 seconds of our program, both the parent process and the child process are mapped to the same physical address space through the page table, because the child process inherits from the parent process. , so a lot of information such as page table, PCB and process address space are also copied from the parent process, and their virtual addresses of g_val are the same. If the child process modifies g_val at this time, as shown below;

        At this time, because the child process needs to modify the data, our OS will reopen a space and modify the page table mapping relationship. At this time, our child process will write the data into the newly opened space, and the page Only the piece of data mapped to the physical address is changed in the table entry, so the virtual address does not change. Therefore, we will see the phenomenon that the address is the same but the values ​​stored in it are different; the same is true for the return value of fork, and the copy-on-write phenomenon occurs. ;

3. Why is there a process address space?

1. Knowledge expansion

        Before we answer this question, let us first add some knowledge. After our program is compiled and an executable program is generated, is there an address in this executable program? If so, what's the address?

        In fact, the concept of virtual address not only exists within our operating system, but our compiler must also comply with this. Therefore, after we compile, the virtual address has been used internally in the program, and there will also be various segments, such as data segments, code segments, etc.; until our program is loaded into the memory, physical space will be allocated to our program and the mapping of the page table will be filled;

2. The significance of process address space

        First,Once we have the process address space, all addresses our CPU sees are virtual addresses, and all addresses need to be found through page table mapping. Real physical address. This can effectively protect the memory and prevent out-of-bounds modification of data from affecting other processes; once the user modifies data out-of-bounds, we can detect this behavior in the page table, refuse to perform the operation, and directly kill the current process. , thus ensuring the security of memory;

        Second,After we have the address space, our program is not necessarily continuous in the real memory due to the page table mapping. How to allocate memory to our program can be decided by us. The memory management module decides how to allocate it has nothing to do with our process management module, because we have the process address space, we have the virtual address, we don't care where the real physical address is, and we don't care how to allocate it. You only need to establish a mapping between the page table and the physical memory space. This completes the process management module and memory Decoupling of management module;

Replenish:

        Our C language’s malloc function and C++’s new What is the space address applied for? Based on the above, it is not difficult for us to judge that we are applying for a virtual address, so the question arises again. If we do not use this space immediately after applying for it, will the OS system apply for physical memory space for the virtual space we applied for? The answer is no, of course not. If we apply for malloc virtual space and don't use it, and the OS also applies for physical space for us, then this physical space will not be used, so aren't the resources wasted in vain?

        What the operating system does is that when we call a function like malloc, the operating system will first apply for virtual memory space for us and fill in the page table, but it will not apply for real physical space for us. Once we want to use it, the operating system will Apply for real physical space for us and complete the page table mapping relationship;

        This delayed allocation strategy greatly improves memory utilization and is zero-aware for the process, because our process does not care about memory management, only cares about the virtual memory space and finds the corresponding mapping in the page table;

        Third, due to the page table + process address space, we can disperse our code and data anywhere in the physical memory, but from the process perspective, it looks like continuous. For example, we create a variable a, and then create A variable b, our two variables can be regarded as continuous in the virtual address, but the physical memory is not necessarily continuous, so our physical address is mapped to any location through the page table and can be unordered; for computers For multiple processes in it, as long as the correctness of the page table mapping can be ensured, the independence between processes can be guaranteed!

3. Re-understand suspension

        We can once again understand the hang phenomenon through the above theory. The so-called hang means that due to certain circumstances, we need to temporarily store the code and data of certain processes in the memory to the swap area in the disk; then let's think about it again, we Is it possible not to put code data into memory when loading a process?

        It is actually possible. When our memory resources are extremely tight, we run an executable program, that is, load this program into the memory. However, our memory is already very tight. We can first create this executable program on the OS. The corresponding PCB control block, process address space, page table, etc., code and data are temporarily stored in memory. Isn't this also the essence of our suspension?

        With the above knowledge in mind, let’s think about the large-scale games we usually play on computers, which often exceed 100 GB, such as GTA5, but our memory is only 8G or 16G. So how do we convert such a large executable program? What about loaded into memory? First of all, it is definitely unrealistic to load it completely into the memory. When we talked about suspension, we only created the kernel data without loading the code and data into the memory. Then we can definitely load it partially. What code and data do we need to use? Just load it into the memory. When it is not used for a long time, swap out the memory. This can create the illusion that we have loaded the entire program into the memory;

Guess you like

Origin blog.csdn.net/Nice_W/article/details/134018237