内存映射
内存映射区是将文件或设备的内容映射到进程的虚拟内存地址空间中。进程可以像操作普通内存一样读写这些映射的区域,而这些操作会直接反映到文件或设备上。对于进程间通信,可以使用匿名内存映射区(不对应具体的文件),进程通过共享这个匿名内存区域来进行数据交换。
主要函数
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
功能:将一个文件或者设备的数据映射到内存中。指令 man 2 mmap 查看详情
参数:
void * addr:要映射到的内存地址,一般填NULL,表示由内核指定
length:要映射的数据的长度,这个值不能为0。建议使用文件的长度。
获取文件的长度: stat、lseek
prot:对申请的内存映射区的操作权限
PROT_EXEC:可执行的权限
PROT_READ:读权限
PROT_WRITE:写权限
PROT_NONE:没有权限
要操作映射内存,必须要有读的权限,可以设置成PROT_READ、PROT_READ | PROT_WRITE,一般是后者
flags:
MAP_SHARED :映射区的数据会自动和磁盘文件进行同步,进程间通信,必须要设置这个选项
MAP_PRIVATE:不同步,内存映射区的数据改变了,对原来的文件不会修改,会重新创建一个新的文件。(copy on write)
fd:需要映射的那个文件的文件描述符
通过open得到,open的是一个磁盘文件
注意:文件的大小不能为0,open指定的权限不能和prot参数有冲突。
prot设置为:PROT_ READ open可以设置为:只读/读写
prot设置为:PROT_ READ| PROT WRITE open可以设置为:读写
也就是说prot的权限不能大于open的权限
offset:偏移量。一般不用,必须指定的是4k的整数倍。一般用0,表示不偏移。
返回值:返回创建的内存的首地址
失败返回MAP_FAILED,是一个宏,实质是(void *)-1
*/
#include <sys/mman.h>
int munmap(void *addr, size_t length);
功能:释放内存映射,指令 man 2 munmap 查看详情
参数:
addr:要释放的内存的首地址,mmap函数得到
length:要释放的内存的大小,要和mmap函数中的length参数的值一样。
返回值:
成功返回 0
失败返回-1
使用内存映射实现进程间通信:
有关系的进程(父子进程)
- 在还没有子进程的时候,通过唯一的父进程,先创建内存映射区,有了内存映射区以后,创建子进程,父子进程共享创建的内存映射区
没有关系的进程间通信
- 准备一个大小不是0的磁盘文件,进程1通过磁盘文件创建内存映射区,得到一个操作这块内存的指针。然后进程2也通过磁盘文件创建内存映射区,得到一个操作这块内存的指针,使用内存映射区通信
注意:内存映射区通信,是非阻塞。
下面是一个使用内存映射(Memory-Mapped File)实现进程间通信(IPC)的完整例子。这个例子包含两个程序:写入进程(writer)和读取进程(reader),它们通过共享的内存映射文件交换数据。
- 写入进程 (writer.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#define SHM_NAME "/my_shm" // 共享内存文件名
#define SHM_SIZE 4096 // 共享内存大小
int main() {
// 1. 创建/打开共享内存文件
int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
// 2. 调整文件大小
if (ftruncate(fd, SHM_SIZE) == -1) {
perror("ftruncate");
exit(EXIT_FAILURE);
}
// 3. 将文件映射到内存
char *ptr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
// 4. 写入数据到共享内存
const char *message = "Hello from Writer Process!";
sprintf(ptr, "%s", message);
printf("Writer: Sent message: '%s'\n", message);
// 5. 等待用户输入后清理
printf("Press Enter to exit...");
getchar();
// 6. 解除映射并删除共享内存
munmap(ptr, SHM_SIZE);
shm_unlink(SHM_NAME);
close(fd);
return 0;
}
- 读取进程 (reader.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define SHM_NAME "/my_shm" // 必须与writer中的名称相同
#define SHM_SIZE 4096 // 必须与writer中的大小相同
int main() {
// 1. 打开共享内存文件
int fd = shm_open(SHM_NAME, O_RDONLY, 0666);
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
// 2. 将文件映射到内存(只读)
char *ptr = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
// 3. 从共享内存读取数据
printf("Reader: Received message: '%s'\n", ptr);
// 4. 清理资源
munmap(ptr, SHM_SIZE);
close(fd);
return 0;
}
思考问题
-
如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能够成功?
可以对其进行++操作,但是再用此时的ptr调用munmap会出错,如果想++还要munmap,那需要把原始的ptr拷贝一份,调用munmap时用拷贝的ptr。 -
如果open时O_RDONLY,mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
会出现错误,并返回MAP_FAILED。open()函数中的权限建议和prot参数的权限保持致。 -
如果文件偏移量为1000会怎样?
会出错并返回MAP_FAILED,文件偏移量必须为4k(4096)的整数倍。 -
mmap什么情况下会调用失败?
第二个参数length = 0;
第三个参数prot的权限没有读的权限或比fd的flags权限大;
文件偏移量不是4k的整数倍 -
可以open的时候O_CREAT一个新文件来创建映射区吗?
可以,但是新创建的文件大小不能为0,可以通过lseek函数、truncate函数扩展大小 -
mmap后关闭文件描述符,对mmap映射有没有影响?
没有影响,因为mmap函数内部实际上对传入的文件描述符做了拷贝 -
对ptr越界操作会怎样?
越界操作所操作的是非法的内存,会导致段错误

2530

被折叠的 条评论
为什么被折叠?



