# cpp_learn **Repository Path**: zangai98/cpp_learn ## Basic Information - **Project Name**: cpp_learn - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-06-19 - **Last Updated**: 2025-10-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 通过解析/proc/pid/pagemap来分析malloc和fork行为 fork之后子进程的内存空间是如何继承的(pss如何变化) malloc申请空间后,未执行memset之前,物理内存实际未被分配给此进程 pagemap介绍 /proc/pid/pagemap是Linux操作系统中的一个特殊文件,它提供了一种机制将虚拟内存地址映射到物理内存地址。在Linux中,每个进程都有一个唯一的进程ID(PID),/proc/pid/pagemap文件存储了与该进程相关联的页面映射信息。访问/proc/pid/pagemap文件需要root权限或具有相应权限的用户身份。对于非活动进程,页面映射信息可能不完整或不可靠。 /proc/pid/pagemap文件包含了进程虚拟地址空间中每个页面的详细信息,包括页面是否被映射、页面的物理地址、页面的访问权限等。通过读取和分析/proc/pid/pagemap文件,可以将虚拟地址转换为物理地址。 pagemap结构 64位页表项(Page Table Entry, PTE):每个PTE有64位,代表对应虚拟内存页的映射情况。 Bits 0-54: 物理帧号(Physical Frame Number, PFN),表示虚拟内存页映射的物理页面编号。 Bits 0-4: swap类型 Bits 5-54: swap偏移量 Bit 55: 该页面是否被修改(即脏页) Bit 56: 是否为独占页面 Bit 57: pte is uffd-wp write-protected (since 5.13) (see Userfaultfd) Bits 58-60 保留位 Bit 61: 页面为文件映射还是匿名映射  Bit 62: 是否为交互分区页面(是否存在于swap分区) Bit 63: 是否分配物理帧(是否存在于RAM) malloc测试 测试代码: #include #include #include #include #include constexpr int PAGE_SIZE = 1024 * 4; int main(void){ printf("pid:%d", getpid()); getchar(); printf("-----------------malloc within brk--------------\n"); int *buff = (int *)malloc(4 * PAGE_SIZE); printf("------addr 1 : %p\n", buff); getchar(); printf("-----------------memset buff--------------\n"); memset(buff, 0, 4 * PAGE_SIZE); getchar(); printf("-----------------free buff--------------\n"); free(buff); getchar(); printf("-----------------malloc exceed brk--------------\n"); int *buff1 = (int *)malloc(21 * PAGE_SIZE); printf("------addr : %p\n", buff1); getchar(); printf("-----------------memset buff--------------\n"); memset(buff1, 0, 21 * PAGE_SIZE); getchar(); printf("-----------------free buff--------------\n"); free(buff1); printf("-----------------malloc large memory--------------\n"); int *buff2 = (int *)malloc(33 * PAGE_SIZE); printf("------addr : %p\n", buff); getchar(); printf("-----------------memset buff--------------\n"); memset(buff2, 0, 33 * PAGE_SIZE); getchar(); printf("-----------------free buff--------------\n"); free(buff2); getchar(); return 0; } 结论: 程序启动时调用brk申请20KB堆内存 malloc根据M_MMAP_THRESHOLD(128kb)来判断调用brk还是mmap,小内存分配使用brk,大内存分配使用mmap brk指针内的内存申请,尾页会被分配物理帧;mmap申请的内存,首页会被分配物理帧;memset之后,全部分配物理帧。 malloc->判断是否超过brk指针->否:申请的地址范围,尾页分配物理帧->memset:中间页分配物理帧->free:不会立刻返回内存给os free: brk申请的内存,不做操作;mmap申请的内存,返还给os malloc之后,申请空间的尾页被分配物理帧 memset之后,中间页面被分配物理帧 注:根据strace观察程序执行也能看到brk和mmap的系统调用执行。 fork测试 写时复制(copy-on-write)技术: 内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。 测试代码: #include #include #include #include using namespace std; constexpr int PAGE_SIZE = 1024 * 4; int g_var; int main(void){ printf("---------begin, the PID: %d\n",getpid()); printf("---------before fork\n"); getchar(); int PID=fork(); if (PID==0){ printf("----child process: %d\n",getpid()); getchar(); printf("----child malloc\n"); size_t length = 4 * PAGE_SIZE; void *buff = (void *)malloc(length); printf("------%p\n", buff); getchar(); memset(buff, 1, length); const int child_const = 1; const int child_static = 1; getchar(); }else if(PID>0){ printf("----father process: %d\n",getpid()); // printf("-----------------father malloc: %d--------------\n", getpid()); getchar(); printf("------father g_var\n"); g_var = 10; getchar(); }else{ printf("----error\n"); } getchar(); return 0; } 测试1: 子进程和父进程的代码段、数据段以及堆区都是共享的 子进程malloc之后,父进程堆区重新申请 测试2: 父子进程共享数据段 父进程对全局变量做了赋值操作后,数据段物理地址改变 参考链接 https://docs.kernel.org/admin-guide/mm/pagemap.html linux内存管理-CSDN博客