Memory mapping principle
Physical address space
-
The address that the processor sees on the system bus.
-
Processors using RISC (Reduced Instruction Set Computer RISC) usually implement only one physical address space, and peripheral devices and physical memory use a unified physical address space. Some processor architectures refer to the physical address area allocated to peripheral devices as device memory.
-
The processor accesses the peripheral device through the register of the peripheral device controller. The register is divided into three categories: control register, status register and data register. Peripheral registers are usually continuously addressed in two ways: I/O mapped and memory mapped.
-
Applications can only access peripheral registers through virtual addresses. The kernel provides API functions to map the physical address of peripheral registers to virtual address space.
-
ARM64 architecture (the maximum physical address width supports 48 bits) is divided into two types:
- Normal Memory: includes physical memory and read only memory (ROM)
- Device Memory: the physical address area allocated to peripheral registers.
The device memory sharing attribute is always shared externally, and the cache attribute is always uncacheable (the processor's cache must be bypassed).
Memory mapping principle
Memory mapping is to create a mapping in the virtual address space of the process, which is divided into two types:
- File mapping: the memory mapping supported by the file maps an interval of the file to the virtual address space of the process. The data source is the file on the storage device.
- Anonymous mapping: there is no file supported memory mapping, which maps the physical memory to the virtual address space of the process, and there is no data source.
Two processes can implement shared memory using shared file mapping. Anonymous mapping is usually private mapping, and shared anonymous mapping can only occur between parent and child processes.
In the virtual address space of a process, code segments and data segments are private file mappings, and uninitialized data segments and stacks are private anonymous mappings.
The modified dirty page will not be updated to the file immediately. You can call msync to force synchronous writing to the file.
data structure
system call
Applications typically use malloc() to request memory. The memory allocator ptmalloc of glibc library uses brk or mmap to apply for virtual memory in pages to the kernel, and then divides the pages into small memory blocks to allocate to applications. The default threshold is 128Kb. If the memory length applied by the application is less than the threshold, the ptmalloc allocator uses brk to apply for virtual memory from the kernel. Otherwise, the ptmalloc allocator uses mmap to apply for virtual memory from the kernel.
Applications can directly use mmap to request virtual memory from the kernel.
mmap memory mapping principle has three stages:
- The process starts the mapping process and creates a virtual mapping area for the mapping in the virtual address space;
- Call the system call function mmap (different from user space function) in kernel space to realize the one-to-one mapping relationship between file physical address and process virtual;
- The process initiates the access to this mapping space, causes page missing exception, and realizes the file content to physical memory (copy of main memory).
Test code
#include <stdio.h> #include <sys/mman.h> #include <sys/types.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> typedef struct { char name[6]; int age; }people; int main(int argc, char *argv[]) { int fd, i; people *p_map; char temp; fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 00777); lseek(fd, sizeof(people) * 5 - 1, SEEK_SET); write(fd, "", 1); p_map = (people *)mmap(NULL, sizeof(people) * 10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (p_map == (void *) - 1) { fprintf(stderr, "mmap : %s \n", strerror(errno)); return -1; } close(fd); temp = 'A'; for (i = 0; i < 10; i++) { temp = temp + 1; (*(p_map + i)).name[1] = '\0'; memcpy((*(p_map + i)).name, &temp, 1); (*(p_map + i)).age = 30 + i; } printf("Initialize.\n"); sleep(15); munmap(p_map, sizeof(people) * 10); printf("UMA OK.\n"); return 0; }
#include <stdio.h> #include <sys/mman.h> #include <sys/types.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> typedef struct { char name[6]; int age; }people; int main(int argc, char *argv[]) { int fd, i; people *p_map; char temp; fd = open(argv[1], O_CREAT|O_RDWR, 00777); lseek(fd, sizeof(people) * 5 - 1, SEEK_SET); write(fd, "", 1); p_map = (people *)mmap(NULL, sizeof(people) * 10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (p_map == (void *) - 1) { fprintf(stderr, "mmap : %s \n", strerror(errno)); return -1; } close(fd); for (i = 0; i < 10; i++) { printf("name:%s age:%d\n", (*(p_map + i)).name, (*(p_map + i)).age); } munmap(p_map, sizeof(people) * 10); printf("UMA OK.\n"); return 0; }
#include <unistd.h> #include <signal.h> #include <malloc.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/mman.h> #define handle_error(msg) do{ perror(msg); exit(EXIT_FAILURE);}while(0) static char *buffer; static void handler(int sig,siginfo_t *si,void *unused) { printf("Get SIGSEGV at address : %p\n",si->si_addr); exit(EXIT_FAILURE); } int main(int argc,char *argv[]) { int pageSize; struct sigaction sa; sa.sa_flags = SA_SIGINFO; sigemptyset(&sa. sa_mask); sa.sa_sigaction = handler; if(sigaction(SIGSEGV,&sa,NULL) == -1) handle_error("siaction"); pageSize = sysconf(_SC_PAGE_SIZE); if(pageSize == -1) handle_error("sysconf"); buffer = memalign(pageSize, 4 * pageSize); if(buffer == NULL) handle_error("memalign"); printf("start of region : %p\n" ,buffer); if(mprotect(buffer + pageSize * 2, pageSize, PROT_READ) == -1) handle_error("mprotect"); for(char *p = buffer;;) *(p++) = 'A'; printf("for completed.\n"); exit(EXIT_SUCCESS); return 0; }
summary
This paper mainly introduces the physical address space, the principle of memory mapping, the data structure diagram used for file mapping to virtual address, system call, and the demo of the three functions are given respectively.
Technical reference
Source of technical points of this article: Linux kernel source code Video Series: https://ke.qq.com/course/3294666