Linux下mmap共享内存应用详解
linux mmap 共享内存

作者:IIS7AI 时间:2025-01-07 23:56



Linux mmap与共享内存:高效进程间通信的利器 在Linux操作系统中,进程间通信(IPC)是一个核心而复杂的话题

    传统的通信方式如管道、消息队列和套接字,尽管各有其优点,但在效率和灵活性上总存在一定的局限性

    而mmap(memory map)系统调用及其支持的共享内存机制,则为进程间通信提供了一种高效、灵活的新选择

    本文将深入探讨Linux中的mmap系统调用及其如何用于实现共享内存,揭示其背后的原理、应用场景及优势

     mmap的基本原理 mmap,即内存映射文件(Memory Map File),是一种将文件或设备映射到进程地址空间的方法

    通过mmap,文件的磁盘地址与进程虚拟地址空间中的一段虚拟地址建立了一一对应关系

    这种映射关系一旦建立,进程就可以像访问普通内存一样,通过指针直接对文件进行读写操作,而无需再调用read、write等系统调用函数

    系统自动处理脏页面的回写,即完成对文件的操作

     mmap的核心在于其减少了数据拷贝的次数

    传统的文件操作需要从磁盘到页缓存(内核空间,不能被用户进程直接寻址),再到用户主存的两次数据拷贝

    而mmap操控文件时,只需从磁盘到用户主存的一次数据拷贝过程

    具体来说,mmap创建新的虚拟内存区域,将文件磁盘地址与虚拟内存区域映射,实现了一次性拷贝

     mmap不仅限于普通文件,还可以映射设备和其他对象

    映射成功后,进程可以直接通过指针访问这段内存区域,进行读写操作

    不过,值得注意的是,直接对该段内存写时,不会写入超过当前文件大小的内容

    如果文件大小不是所有页大小之和,最后一个页不被使用的空间将会清零

     mmap与共享内存 共享内存是mmap应用的一个重要方面

    通过mmap,进程可以映射同一个普通文件,从而实现共享内存

    这种共享内存机制使得不同进程间的数据传递不再涉及内核,也就是说,进程不再需要通过执行进入内核的系统调用来传递数据

     mmap系统调用并不是完全为了共享内存而设计的,但它为共享内存提供了一种高效的方式

    共享内存有两种主要方式:shm(Shared Memory)和mmap方式

    shm直接共享物理内存,而mmap通过一个中间文件间接共享内存

     - mmap方式:实际存储并没有反映到主存上,但储存量可以很大(多于主存)

    进程间读取和写入速度相对较慢,因为涉及到磁盘操作

     - shm方式:实际储存量直接反映到主存上,储存量受限于物理内存大小

    进程间访问速度(读写)比磁盘要快

     mmap系统调用使得进程可以像读写内存一样对普通文件进行操作,这种特性不仅提高了文件操作的效率,还为进程间通信提供了新的可能

    通过mmap实现共享内存,进程间可以直接读写内存,而不需要任何数据的拷贝,从而大大提高了通信效率

     mmap的使用与示例 mmap函数的基本原型如下: include void mmap(void start, size_t length, int prot, int flags, int fd,off_t offset); int munmap(void start, size_t length); - `start`:映射区的开始地址,通常传NULL,让内核自己选择

     - `length`:映射区的长度

     - `prot`:期望的内存保护标志,不能与文件的打开模式冲突,可以是以下值的组合: -PROT_EXEC:页内容可以被执行

     -PROT_READ:页内容可以被读取

     -PROT_WRITE:页可以被写入

     - `flags`:指定映射对象的类型、映射选项和映射页是否可以共享,可以是以下值的组合: -MAP_SHARED:与其他所有映射该对象的进程共享映射空间

    对共享区的写入相当于输出到文件

     -MAP_PRIVATE:建立一个写入时拷贝的私有映射

    内存区域的写入不会影响到原文件

     -MAP_ANONYMOUS:建立匿名映射,忽略参数fd,不涉及文件,映射区域不和其他进程共享

     - `fd`:有效的文件描述词

    如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1

     - `offset`:被映射对象内容的起点

     成功执行时,mmap()返回被映射区的指针,munmap()返回0

    失败时,mmap()返回MAP_FAILED(其值为(void)-1),munmap()返回-1,并设置errno为相应的错误代码

     以下是一个简单的使用mmap进行进程间通信的示例: 程序1(写进程): include include include include include typedef struct{ int age; charname【4】; int flag; } STU; int main() { int fd; STUp; // 生成一个5倍STU结构大小的空文件 fd = open(a.txt, O_CREAT | O_TRUNC | O_RDWR, 0777); lseek(fd, sizeof(STU) 5 - 1, SEEK_SET); write(fd, , 1); // 映射文件到内存 p= ( - STU ) mmap(NULL, sizeof(STU) 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); // 写入10倍STU结构大小的内存 for(int i = 0; i < 10; i++) { (p +i)->age = i + 20; (p +i)->flag = 1; memcpy(&((p +i)->name), hh, 2); printf(name: %s, age: %d, flag: %dn,p【i】.name,p【i】.age,p【i】.flag); sleep(1); } munmap(p,sizeof(STU)10); printf(映射结束n); return 0; } 程序2(读进程): include include include include typedef struct{ int age; charname【4】; int flag; } STU; int main() { int fd; STUp_map, p; fd = open(a.txt, O_CREAT | O_RDWR, 0777); p_map= ( - STU ) mmap(NULL, sizeof(STU) 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); printf(开始读取n); p = p_map; while(p < p_map + 10) { if(p->flag == { printf(name: %s, age: %d , p->name, p->age); p++; }else { sleep(1); } } munmap(p_map,sizeof(STU)10); printf(读取结束n); return 0; } 在这个示例中,程序1创建一个文件并将其映射到内存,然后写入一些数据

    程序2打开同一个文件并将其映射到内存,然后读取程序1写入的数据

    通过这种方式,两个进程实现了高效的通信

     mmap的优势与挑战 mmap及其支持的共享内存机制具有显著的优势: 1.高效性:进程可以直接读写内存,而不需要任何数据的拷贝,从而大大提高了通信效率

     2.灵活性:mmap不仅限于普通文件,还可以映射设备和其他对象,为进程间通信提供了更多的可能性

     3.易用性:mmap提供了丰富的标志和选项,使得用户可以根据具体需求进行灵活的配置

     然而,mmap也面临一些挑战: 1.同步问题:多个进程同时读写共享内存时,需要解决同步问题,以避免数据不一致

     2.安全性:直接操作内存可能导致地址越界等安全问题,需要谨慎处理

     3.兼容性:不同操作系统和平台对mmap的支持可能存在差异,需要关注兼容性问题

     结论 mmap系统调用及其支持的共享内存机制为Linux中的进程间通信提供了一种高效、灵活的新选择

    通过mmap,进程可以像读写内存一样对文件进行操作,从而实现了高效的通信

    尽管mmap面临一些挑战,但其显著的优势使得它在许多应用场景中成为不可或缺的工具

    掌握mmap的使用和原理,对于提高Linux程序性能和优化进程间通信具有重要意义