5, Memory structure

1. Virtual memory, physical memory, semiconductor memory and page feed file

Virtual memory: address space, virtual storage area, and virtual memory accessed by applications.
Physical memory: storage space, the actual storage area. Only the system kernel can access the physical memory.
Physical memory includes semiconductor memory and page feed file (disk).

There is a corresponding relationship between virtual memory and physical memory. When an application accesses virtual memory, the system kernel will find the corresponding physical memory according to this corresponding relationship. The above correspondence is stored in the memory mapping table in the kernel.

When the semiconductor memory is not enough, some long-term idle codes and data can be cached from the semiconductor memory to the page change file, which is called page change out. Once the replaced codes and data need to be used, they can be restored from the page change file to the semiconductor memory, which is called page change in. Therefore, the virtual memory in the system is much larger than the semiconductor memory.

  1. Process maps

Each process has an independent 4G bytes of virtual memory, which is mapped to different physical memory areas. Both memory mapping and swap in and swap out are in pages. 1 page = 4096 bytes.

1G with high address in 4G virtual memory is mapped to the code and data area of the kernel, which is shared among processes. The user's application can only directly access three G virtual memory with low address, so this area is called user space, while one g virtual memory with high address is called kernel space. The code in user space can only directly access the data in user space. If you want to access the code and data in kernel space, you must complete it with the help of special system calls.

4G virtual memory is divided into the following areas:

You can view the size of code area, data area and BSS area of an executable program through the size command. (the unit is byte, dec decimal represents the total size of the file, hex hexadecimal represents the total size of the file)

The user space of each process has an independent mapping from virtual memory to physical memory, which can be called inter process memory barrier.
The following experiments were carried out:

#include <stdio.h> int g_vm = 0; int main(void) {
    printf("Virtual memory address:%p\n", &g_vm);//%*c means to read a character and discard it,
    									//Here, the carriage return character in the buffer is discarded to prevent the next reading error
    printf("Enter an integer:");
    scanf("%d%*c", &g_vm);
    printf("Start another process, enter different data,"
        "Press<enter>Key continue...");
    getchar();
    printf("Virtual memory data:%d\n", g_vm);
    return 0; 
    }

The results are as follows: even if the virtual addresses are the same between different processes, the mapped physical addresses are different. There will be no interference between the two.

3. Allocation and release of memory

The layer by layer calling relationship is as follows:
Implementation of the standard library (C) of call / lock - / freeze - / snap - / snap - / snap - > (C)
(kernel level)

Allocate or free virtual memory incrementally

Allocation: mapping + possession
Mapping: establish a mapping relationship between address space (virtual memory) and storage space (physical memory)
Occupancy: Specifies the attribute of memory space

Release: abandon possession + unmap
Give up possession: release the ownership constraint of memory space
Unmap: eliminate the mapping relationship between address space (virtual memory) and storage space (physical memory)

#include <unistd.h>
void* sbrk(intptr_t increment);
The heap top pointer before calling the function is returned successfully, and - 1 is returned for failure.
Increment > 0 - the heap top pointer moves up to increase heap space and allocate virtual memory
Increment < 0 - the heap top pointer moves down to reduce heap space and free virtual memory
increment = 0 - does not allocate or free virtual memory, only returns the current heap top pointer
The system kernel maintains a pointer to the top of the heap memory, that is, the next position of the last byte in the effective heap memory. The sbrk function adjusts the position of the pointer according to the increment parameter increment and returns the original position of the pointer. If memory is exhausted or idle during this period, the mapping of the corresponding memory page will be automatically appended or cancelled.

#include <stdio.h>
#include <unistd.h> 
int main(void) {
	setbuf(stdout, NULL);//In order to avoid the interference of applications in the heap, cancel the buffer of standard IO, because the buffer of printf is also applied from the heap
    int* p1 = (int*)sbrk(sizeof(int));
    if (p1 == (int*)-1) { 
        perror("sbrk");
        return -1;
    }
    *p1 = 0;
    printf("%d\n", *p1);

    double* p2 = (double*)sbrk(sizeof(double));
    if (p2 == (double*)-1) {
        perror("sbrk");
        return -1;
    }
    *p2 = 1.2;
    printf("%g\n", *p2);

    char* p3 = (char*)sbrk(256 * sizeof(char));
    if (p3 == (char*)-1) {
        perror("sbrk");
        return -1;
    }
    sprintf(p3, "Hello, World!");
    printf("%s\n", p3);

    //Release memory by passing in the opposite number of the sum of all requested memory sizes
    if (sbrk(-(256 * sizeof(char) +
                     sizeof(double) +
                     sizeof(int))) == (void*)-1) {
        perror("sbrk");
        return -1;
    }
    
    
    return 0; 
   } 

Allocate or release virtual memory in the form of absolute address (that is, allocate or release according to the position relationship between parameters and the top of the stack)
#include <unistd.h>
int brk(void* end_data_segment);
0 is returned for success and - 1 is returned for failure.
end_ data_ Segment > current heap top, allocate virtual memory
end_ data_ Segment < current heap top, then free virtual memory
end_data_segment = current heap top, then null operation
The system kernel maintains a pointer to the top of the current heap. The brk function uses the pointer parameter end_data_segment sets the new position of the heap top. If memory is exhausted or idle during this period, the corresponding memory page will be automatically appended or unmapped.

#include <stdio.h>
#include <unistd.h> 
 int main(void) {
    setbuf(stdout, NULL);//In order to avoid the interference of applications in the heap, cancel the buffer of standard IO, because the buffer of printf is also applied from the heap
    int* p1 = (int*)sbrk(0);//Get the address of the top of the pile first
    if (p1 == (int*)-1) {
        perror("sbrk");
        return -1;
    }
    double* p2 = (double*)(p1 + 1);
    if (brk(p2) == -1) {
        perror("brk");
        return -1;
    }
    *p1 = 0;
    printf("%d\n", *p1);
    char* p3 = (char*)(p2 + 1);
    if (brk(p3) == -1) {
        perror("brk");
        return -1;
    }
    *p2 = 1.2;
    printf("%g\n", *p2);
    void* p4 = p3 + 256;
    if (brk(p4) == -1) {
        perror("brk");
        return -1;
    }
    sprintf(p3, "Hello, World!");
    printf("%s\n", p3);
    if (brk(p1) == -1) {
        perror("brk");
        return -1;
    }
    return 0; } 

Establish a mapping from virtual memory to physical memory or files
#include <sys/mman.h>
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
The starting address of virtual memory in the mapping area is returned successfully, and map is returned in case of failure_ Failed (void * - 1)
The first four parameters deal with physical memory, and the last two parameters deal with files

start - select the starting address of the virtual memory of the area to be mapped. NULL means automatic selection (NULL safe). If you specify it yourself, it is also done according to the integer multiple of 4096 (rounded down) of the size of page feed

length - the number of bytes in the mapping area, rounded automatically by page

prot - access authority, which can take the following values:
PROT_READ - readable
PROT_WRITE - writable
PROT_EXEC - executable
PROT_NONE - not accessible

flags - mapping flag, which can take the following values:
MAP_ANONYMOUS - anonymous mapping, which maps virtual memory to physical memory. The last two parameters fd and offset of the function are ignored
MAP_PRIVATE - Private mapping, which maps virtual memory to the memory buffer of the file instead of the disk file (there is a memory barrier at this time)
MAP_SHARED - shared mapping, which maps virtual memory to disk files (there is no memory barrier at this time)
MAP_DENYWRITE - write denied. No other write operations are allowed in the mapped area of the file
MAP_FIXED - fixed mapping. If the mapping cannot be created on start, it will fail (without this flag, the system will automatically adjust to the area not mapped)
MAP_LOCKED - locked mapping. It is forbidden to be swapped out to the page feed file (i.e. it always exists in the semiconductor memory)

fd - file descriptor

Offset - file offset, automatically aligned by page

Unmap virtual memory from physical memory or file (operate according to integral multiple of page feed size)
#include <sys/mman.h>
int munmap(void* start, size_t length);
0 is returned for success and - 1 is returned for failure.
start - the starting address of the mapping area
length - number of bytes in the mapping area

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
int main(void) {
    char* psz = mmap(NULL, 8192,
        PROT_READ | PROT_WRITE,
        MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    if (psz == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    sprintf(psz, "first page");
    sprintf(psz + 4096, "Page 2");
    printf("%s\n", psz);
    printf("%s\n", psz + 4096);
    if (munmap(psz, 4096) == -1) {
        perror("munmap");
        return -1;
    }
    //There will be a paragraph error here because the first page of psz has been removed
    //printf("%s\n", psz);

    //There will be no paragraph errors here, because only the first page of psz is released, and the second page is not. You can still access it
    printf("%s\n", psz + 4096);

    //Here the mapping is all released
    if (munmap(psz + 4096, 4096) == -1) {
        perror("munmap");
        return -1;
    }
    return 0;
}

You can view the usage of the process's memory heap through the maps file in the cd /proc/(pid number) directory

Keywords: Linux Unix Cache

Added by vtolbert on Fri, 18 Feb 2022 23:00:29 +0200