Linux操作系统提供的IPC方法有很多,如文件,管道,信号,共享内存,消息队列,套接字,命名管道等,常用的进程间通信方法有:
- 管道 (使用最简单)
- 信号 (开销最小)
- 共享映射区 (无血缘关系)
- 本地套接字 (最稳定)
mmap函数基本用法
上述IPC方法中的共享映射区就是使用mmap函数实现的,该函数的功能是将磁盘文件的内容映射到内存中,在不适用read和write函数的情况下,使用地址(指针)完成I/O操作
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length); /* 删除共享映射区 */
参数解析:
- addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
- length: 欲创建映射区的大小
- prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
- flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备
- fd: 用来建立映射区的文件描述符
- offset: 映射文件的偏移(4k的整数倍)
返回值:
- 成功返回共享内存的首地址
- 失败返回 MAP_FAILED((void *) -1)
mmap函数注意事项
先看一个示例程序:
#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int fd_test;
unsigned char *buf = NULL;
int ret;
fd_test = open("temp", O_RDWR | O_CREAT, 0644);
if (fd_test == -1)
{
perror("open err");
exit(0);
}
/*
* 删除文件的目录项,使之具备被删除的条件,没有真正被删除
* 在所有占有该文件描述符的进程结束后该文件被删除
*/
//unlink("temp"); /* 程序结束后删除temp */
int n;
printf("请输入文件的大小\n");
scanf("%d",&n);
ret = ftruncate(fd_test, n); /* 设置文件的大小 */
if (ret == -1)
{
perror("ftruncate err:");
exit(0);
}
struct stat fd_stat;
ret = fstat(fd_test, &fd_stat); /* 获取文件的大小 */
if (ret == -1)
{
perror("fstat err:");
exit(0);
}
int fd_len = fd_stat.st_size;
printf("fd_len = %d \n",fd_len);
/* 映射一个与文件大小相同的共享内存区域 */
buf = (unsigned char *)mmap(NULL, fd_len, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_test, 0);
if (buf == MAP_FAILED)
{
perror("mmap failed");
exit(0);
}
int buf_len = strlen((char *)buf);
printf("buf len = %d \n", buf_len);
printf("修改前文件的内容:\n%s\n", buf);
char rcv_buf[1024];
printf("输入写入文件的数据:\n");
while ( fgets(rcv_buf, sizeof(rcv_buf), stdin) != NULL )
{
if (strlen(rcv_buf) <= 1)
continue;
break;
}
memcpy(buf, rcv_buf, strlen(rcv_buf));
printf("修改后文件的内容:\n%s\n", buf);
close(fd_test);
munmap(buf, 10);
return 0;
}
代码中使用了几个系统调用,
- unlink,删除文件的目录项,使之具备被删除的条件,没有真正被删除,在所有占有该文件描述符的进程结束后该文件被删除,如果在程序结束后就无需使用这个文件,则调用它
- ftruncate,设置文件的大小
- fstat, 获取文件的大小
mmap函数的核心在注意事项,主要有以下几个
- 新创建的文件无法mmap,必须要有实际的大小,可以使用ftruncate函数设置文件实际大小
- 对映射区的权限要小于等于打开文件的权限,映射的过程中隐藏了一次读操作,因此文件权限至少是可读
- 最后一个参数offset必须是4K的整数倍
- 映射的内存超出了文件的大小,不报错,但无法写入超出的部分
- 一定要检查返回值
运行结果:
可以看到,设置文件的大小为10,但写入的数据超过10,虽然写入了共享内存区域,但是超出文件大小的部分并没有写入文件中
mmap函数用于进程间通信
实质上mmap是内核借助文件帮我们创建了一个映射区,多个进程之间利用该映射区完成数据传递。mmap可以用于父子间通信,也可以用于无血缘关系进程间通信,前提是必须要使用MAP_SHARED选项
匿名映射
使用映射区必须要使用一个文件来创建共享内存,比较麻烦,也可以使用匿名映射来创建共享映射区,方法是使用MAP_ANONYMOUS (或MAP_ANON)
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
该方法只可能在Linux操作系统使用,无法在类Unix操作系统使用。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!