Linux内存映射原理
2022/1/16 7:03:27
本文主要是介绍Linux内存映射原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
内存映射原理
物理地址空间
-
处理器在系统总线上看到的地址。
-
使用RISC(Reduced Instruction Set Computer RISC 精简指令集)的处理器通常只实现一个物理地址空间,外围设备和物理内存使用统一的物理地址空间。有些处理器架构把分配给外围设备的物理地址区域称为设备内存。
-
处理器通过外围设备控制器的寄存器访问外围设备,寄存器分为控制寄存器、状态寄存器和数据寄存器三大类。外围设备寄存器通常被连续编址,分为两种方式:I/O映射方式(I/O-mapped),内存映射方式(memory-mapped)。
-
应用程序只能通过虚拟地址访问外设寄存器,内核提供API函数把外设寄存器的物理地址映射到虚拟地址空间。
-
ARM64架构(物理地址宽度最大支持48位)分为两种类型:
- 正常内存(Normal Memory):包括物理内存和只读存储器(ROM)
- 设备内存(Device Memory):分配给外围设备寄存器的物理地址区域。
设备内存共享属性总是外部共享,缓存属性总是不可缓存(必须绕过处理器的缓存)。
内存映射原理
内存映射即在进程的虚拟地址空间中创建一个映射,分为两种:
- 文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。
- 匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源。
两个进程可以使用共享的文件映射实现共享内存。匿名映射通常是私有映射,共享的匿名映射只可能出现在父进程和子进程之间。
在进程的虚拟地址空间中,代码段和数据段是私有的文件映射,未初始化的数据段、堆栈是私有的匿名映射。
修改过的脏页面不会立即更新到文件中,可以调用msync强制同步写入文件。
数据结构
系统调用
应用程序通常使用malloc()申请内存。glibc库的内存分配器ptmalloc使用brk或mmap向内核以页为单位申请虚拟内存,然后把页划分成小内存块分配给应用程序。默认阈值是128Kb,如果应用程序申请的内存长度小于阈值,ptmalloc分配器使用brk向内核申请虚拟内存,否则ptmalloc分配器使用mmap向内核申请虚拟内存。
应用程序可以直接使用mmap向内核申请虚拟内存。
mmap内存映射原理三个阶段:
- 进程启动映射过程,并且在虚拟地址空间中为映射创建虚拟映射区域;
- 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟的一一映射关系;
- 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存的拷贝)。
测试代码
#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; }
总结
本文主要介绍了物理地址空间,内存映射原理,文件映射到虚拟地址所用数据结构示意图,系统调用,三个函数分别给出demo。
技术参考
本文技术点出处:Linux内核源码视频系列:https://ke.qq.com/course/3294666
这篇关于Linux内存映射原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法