2026/4/18 11:11:59
网站建设
项目流程
企业建站电话多少,小程序游戏搭建,seo搜索引擎优化报价,李勇seo的博客文章目录内存映射内存分配内存布局mmap() 系统调用查看进程内存映射mmap()四种映射方式私有文件映射 (MAP_PRIVATE)私有匿名映射 (MAP_PRIVATE | MAP_ANONYMOUS)共享文件映射 (MAP_SHARED)共享匿名映射 (MAP_SHARED | MAP_ANONYMOUS)文件映射进程通信实现原理实现代码匿名共享内…文章目录内存映射内存分配内存布局mmap() 系统调用查看进程内存映射mmap()四种映射方式私有文件映射 (MAP_PRIVATE)私有匿名映射 (MAP_PRIVATE | MAP_ANONYMOUS)共享文件映射 (MAP_SHARED)共享匿名映射 (MAP_SHARED | MAP_ANONYMOUS)文件映射进程通信实现原理实现代码匿名共享内存实现通信POSIX 共享内存进程通信POSIX共享内存API创建 / 打开 POSIX 共享内存对象调整共享内存大小删除共享内存对象使用流程实现代码System V 共享内存进程通信POSIX 共享内存API创建 / 获取共享内存附加到进程地址空间分离共享内存控制操作 含删除、查询、修改 实现代码共享内存数据竞争问题解决方案对比C语言当中的内存分区内存映射内存分配Linux内核为进程分配内存空间虚拟内存管理Linux使用虚拟内存管理每个进程都有独立的虚拟地址空间通常分为用户空间和内核空间。用户空间供进程使用内核空间由内核管理内存描述符 (mm_struct)每个进程的虚拟内存信息由mm_struct结构体管理包含内存映射、堆、栈等信息内存映射进程的内存映射通过vm_area_struct结构体描述记录虚拟内存区域的起始、结束地址、权限等常见的内存区域包括代码段存放可执行代码数据段存放全局和静态变量堆动态分配的内存栈用于函数调用和局部变量内存分配内存分配主要通过以下系统调用brk和sbrk调整堆的大小mmap创建新的内存映射可用于文件映射或匿名内存分配缺页异常处理当进程访问未映射的虚拟内存时触发缺页异常内核通过以下步骤处理检查访问权限确认访问是否合法分配物理内存若合法分配物理页并更新页表恢复执行进程继续执行页面回收当物理内存不足时内核通过页面回收机制释放内存包括页面置换将不常用的页面换出到交换空间内存压缩压缩内存页以减少使用内核内存分配内核使用kmalloc、vmalloc等函数分配内存kmalloc用于小块连续内存vmalloc用于大块不连续内存内存布局Linux 进程内存布局代码段 (Text)存放可执行代码数据段 (Data)全局变量和静态变量BSS段未初始化的全局变量堆 (Heap)动态分配的内存malloc/free栈 (Stack)函数调用、局部变量共享库动态链接库映射区域mmap() 系统调用mmap() 是 Linux/Unix 下核心的系统调用用于将文件或共享内存对象映射到当前进程的虚拟地址空间映射完成后进程可通过直接操作虚拟内存的方式读写文件 / 共享内存无需调用 read()/write() 等传统 I/O 函数效率更高#includesys/mman.hvoid*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);addr建议映射地址通常为NULL由内核决定length映射长度prot保护标志PROT_READ | PROT_WRITE | PROT_EXECflags映射类型和选项fd文件描述符匿名映射时为-1offset文件偏移量// 配套的解除映射函数intmunmap(void*addr,size_tlength);用于解除 mmap() 建立的内存映射释放进程虚拟地址空间查看进程内存映射# 查看进程的内存映射cat/proc/PID/maps# 示例输出00400000-00401000 r-xp 000060000 08:01123456/bin/program# 代码段00600000-00601000 rw-p 00000000 08:01123456/bin/program# 数据段7ffff7a00000-7ffff7bc1000 r-xp 00000000 08:01789012/lib/libc.so.6mmap()四种映射方式按数据来源分类类型文件映射匿名映射共享 (SHARED)共享文件映射共享匿名映射私有 (PRIVATE)私有文件映射私有匿名映射私有文件映射 (MAP_PRIVATE)用途用文件内容初始化内存如可执行文件加载变更对其他进程不可见不会写回文件常用于只读数据共享私有匿名映射 (MAP_PRIVATE | MAP_ANONYMOUS)用途分配新内存malloc() 大块内存时使用初始化为0变更私有代替 brk() 分配大内存共享文件映射 (MAP_SHARED)用途内存映射I/O、进程间通信变更对其他进程可见会写回文件高效的文件读写方式共享匿名映射 (MAP_SHARED | MAP_ANONYMOUS)用途相关进程间共享内存无文件支持纯内存共享必须相关进程如父子进程文件映射进程通信实现原理多个进程映射同一文件的同一区域使用 MAP_SHARED 标志对映射区域的修改会同步到文件和其他进程实现代码发送端#includefcntl.h#includesys/mman.h#includestdio.hstructstudent{charname[32];intage;floatscore;}*p;intmain(){intfdopen(stu.bin,O_RDWR|O_CREAT,0666);// 将文件映射到内存pmmap(NULL,sizeof(structstudent),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);// 持续更新分数while(1){scanf(%f,p-score);}munmap(p,sizeof(structstudent));return0;}接收端#includefcntl.h#includesys/mman.h#includestdio.hstructstudent{charname[32];intage;floatscore;}*p;intmain(){intfdopen(stu.bin,O_RDWR);pmmap(NULL,sizeof(structstudent),PROT_READ,MAP_SHARED,fd,0);floatlast_scorep-score;printf(当前分数%.2f\n,p-score);while(1){if(p-score!last_score){printf(分数变更为%.2f\n,p-score);last_scorep-score;}sleep(1);}munmap(p,sizeof(structstudent));return0;}匿名共享内存实现通信#includesys/mman.h#includestring.hintmain(){// 创建匿名共享映射char*shmmmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);strcpy(shm,Hello from parent);if(fork()0){// 子进程读取printf(Child: %s\n,shm);exit(0);}wait(NULL);munmap(shm,4096);return0;}POSIX 共享内存进程通信POSIX共享内存API创建 / 打开 POSIX 共享内存对象intshm_open(constchar*name,intoflag,mode_tmode);name系统级唯一标识名命名规则严格必须以 / 开头且后续不能再包含其他 /例如 /my_shm 合法/my/shm 非法oflag打开 / 创建标志可通过 按位或组合多个标志O_RDONLY、O_WRONLY、O_RDWR、O_CREAT、O_EXCL、O_TRUNCmode仅当 oflag 包含 O_CREAT 时有效用于指定新共享内存对象的访问权限取值与 open()、mq_open() 权限一致最终实际权限会被进程的 umask 掩码修正实际权限 mode ~umask、若不使用 O_CREAT该参数可传入 0仅为占位无实际意义返回值成功返回一个有效的文件描述符非负整数后续对共享内存的操作ftruncate、mmap 等均依赖该文件描述符失败返回 -1同时设置全局变量 errno 指示错误原因例如 EEXISTO_CREAT|O_EXCL 时共享内存已存在ENOENT对象不存在且未指定 O_CREATEINVALname 命名格式非法调整共享内存大小intftruncate(intfd,off_tlength);fd有效的文件描述符此处必须是由 shm_open() 成功返回的共享内存对象描述符且需拥有可写权限即 shm_open() 时指定了 O_WRONLY 或 O_RDWRlength共享内存对象调整后的目标字节长度非负整数若 length 小于当前共享内存的大小超出 length 的部分数据会被丢弃内存空间被回收若 length 大于当前共享内存的大小扩展的部分会被初始化为 0新增的内存空间可被后续 mmap 映射使用共享内存创建后默认长度为 0必须通过 ftruncate() 设定有效大小否则无法正常映射使用返回值成功返回 0表示共享内存对象的大小已成功调整为 length 字节失败返回 -1同时设置全局变量 errno 指示错误原因 例如font stylecolor:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);EBADF/font无效的文件描述符或无写权限font stylecolor:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);EINVAL/fontfont stylecolor:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);length/font为负数或描述符对应对象不支持大小调整font stylecolor:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);ENOSPC/font系统无足够资源满足大小扩展需求删除共享内存对象intshm_unlink(constchar*name);name与 shm_open() 中一致的共享内存对象唯一标识名标识要删除的目标共享内存对象名称必须对应系统中已存在的共享内存返回值成功返回 0表示共享内存对象的「链接」已被成功移除失败返回 -1同时设置全局变量 errno 指示错误原因 例如 ENOENT指定名称的共享内存对象不存在EINVALname 命名格式非法使用流程shm_open() 创建/打开共享内存对象ftruncate() 设置大小mmap() 映射到进程地址空间读写操作munmap() 取消映射shm_unlink() 删除对象可选实现代码发送端#includefcntl.h#includesys/mman.h#includestdio.hintmain(){// 1. 创建共享内存对象intfdshm_open(/myshm,O_RDWR|O_CREAT,0666);// 2. 设置大小ftruncate(fd,4096);// 3. 映射到内存char*strmmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);close(fd);// 关闭文件描述符// 4. 写入数据sprintf(str,Hello from sender\n);sleep(10);// 保持映射// 5. 清理munmap(str,4096);return0;}接收端#includefcntl.h#includesys/mman.h#includestdio.hintmain(){// 1. 打开共享内存对象intfdshm_open(/myshm,O_RDONLY,0666);// 2. 映射到内存char*strmmap(NULL,4096,PROT_READ,MAP_SHARED,fd,0);close(fd);// 3. 读取数据printf(Received: %s,str);// 4. 清理munmap(str,4096);shm_unlink(/myshm);// 删除共享内存对象return0;}System V 共享内存进程通信POSIX 共享内存API创建 / 获取共享内存intshmget(key_tkey,size_tsize,intshmflg);key用于标识系统级唯一共享内存段的键值手动指定一个非负整数 不同进程使用相同 key 可访问同一个共享内存段。用 IPC_PRIVATE值为 0创建一个仅当前进程可见的私有共享内存段通常用于父子进程间通信可通过 ftok() 函数生成基于文件路径和项目 ID 的唯一 key避免手动指定冲突size共享内存段的目标字节大小非负整数。若为创建新共享内存段必须指定有效的 size例如 4096若为获取已存在的共享内存段仅用 key 匹配现有段size 可指定为 0无需匹配大小仅用于标识获取操作shmflg打开 / 创建标志可通过 按位或组合多个标志O_RDONLY、O_WRONLY、O_RDWR、O_CREAT、O_EXCL、O_TRUNC返回值成功返回一个有效的 共享内存段 IDshmid非负整数后续所有对该共享内存的操作shmat/shmdt/shmctl均依赖该 ID失败返回 -1同时设置全局变量 errno 指示错误原因例如 EEXISTIPC_CREAT|IPC_EXCL 时内存段已存在ENOENT无对应 key 的内存段且未指定 IPC_CREATENOMEM系统无足够内存创建新段EINVALsize 无效或超出系统限制附加到进程地址空间void*shmat(intshmid,constvoid*shmaddr,intshmflg);shmid 由font stylecolor:rgb(31, 35, 41);background-color:rgba(0, 0, 0, 0);shmget()/font成功返回的**共享内存段 ID**标识要附加的目标共享内存段需有效且当前进程有访问权限shmaddr 指定共享内存段映射到当前进程地址空间的起始地址。常用取值NULL推荐由系统自动选择合适的空闲地址进行映射避免手动指定地址导致冲突或无效若手动指定非 NULL 地址需保证地址对齐符合内存页要求shmflg 附加模式标志0默认以读写模式附加共享内存段需当前进程拥有该内存段的读写权限常传入 0 即可满足大部分需求SHM_RDONLY以只读模式附加共享内存段需当前进程拥有该内存段的读权限此时进程无法修改共享内存中的数据SHM_REMAP仅当 shmaddr 非 NULL 时有效替换当前进程 shmaddr 地址处已存在的映射覆盖原有映射返回值成功返回共享内存段映射到当前进程地址空间的起始虚拟地址void * 类型进程可通过该地址访问共享内存数据失败返回 -1同时设置全局变量 errno 指示错误原因分离共享内存intshmdt(constvoid*shmaddr);shmaddr 由 shmat() 成功返回的共享内存段映射起始地址标识要分离的内存映射区域。必须是 shmat() 返回的原始地址不能是偏移后的地址返回值成功返回 0表示共享内存段已成功与当前进程分离失败返回 -1同时设置全局变量 errno 指示错误原因控制操作 含删除、查询、修改 intshmctl(intshmid,intcmd,structshmid_ds*buf);shmaddr 由 shmget() 成功返回的共享内存段 ID标识要进行控制操作的目标共享内存段需有效且当前进程有对应操作权限cmd要执行的控制命令IPC_STAT查询共享内存段的属性信息将属性写入 buf 指向的 struct shmid_ds 结构体中buf 不能为 NULLIPC_SET修改共享内存段的属性仅能修改 struct shmid_ds 中的 shm_perm.uid、shm_perm.gid、shm_perm.mode 字段需进程拥有对应权限buf 不能为 NULLIPC_RMID删除共享内存段此时 buf 可指定为 NULL无意义无需传入buf指向 struct shmid_ds 结构体的指针用于存储或修改共享内存段的属性返回值成功返回 0失败返回 -1同时设置全局变量 errno 指示错误原因实现代码发送端#includesys/ipc.h#includesys/shm.h#includestring.hintmain(){// 1. 生成键值key_tkeyftok(shmfile,65);// 2. 创建共享内存intshmidshmget(key,1024,0666|IPC_CREAT);// 3. 附加到进程char*shmshmat(shmid,NULL,0);// 4. 写入数据strcpy(shm,Hello from System V);// 5. 分离shmdt(shm);return0;}接收端#includesys/ipc.h#includesys/shm.h#includestdio.hintmain(){key_tkeyftok(shmfile,65);// 获取现有共享内存intshmidshmget(key,1024,0666);// 附加到进程char*shmshmat(shmid,NULL,0);// 读取数据printf(Received: %s\n,shm);// 分离并删除shmdt(shm);shmctl(shmid,IPC_RMID,NULL);return0;}共享内存数据竞争问题多个进程同时读写共享内存会导致数据不一致// 进程1int*datashmat(shm_id,NULL,0);*data10;while(*data!0);// 等待进程2// 此时进程2可能也在修改data解决方案信号量 (Semaphore)互斥锁 (Mutex) - 配合共享内存使用条件变量 (Condition Variable)文件锁 (fcntl)#includesemaphore.h#includesys/mman.h// 在共享内存中定义同步结构structshared_data{sem_tsem;// 信号量intvalue;// 共享数据};// 初始化sem_init(data-sem,1,1);// 进程间共享初始值1// 使用sem_wait(data-sem);// 加锁data-value;// 临界区操作sem_post(data-sem);// 解锁对比特性文件映射POSIX共享内存System V共享内存匿名映射持久性文件系统持久系统重启消失系统重启消失进程结束消失进程关系任意进程任意进程任意进程相关进程使用难度中等简单中等简单同步需求需要需要需要需要可移植性高较高高高性能依赖文件系统高高最高