mmap函数
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
参数:
void *addr
建立映射区的首地址,由Linux内核指定,所以我们直接传递NULL。也就是说虽然这是一个参宿但是并不需要我们传递,当建立好映射区以后映射区的首地址将以返回值返回。size_t length
建立映射区的大小,一般由创建映射区的文件的大小int prot
用来表示映射区的权限(读,写,读写,执行,对于执行一般是操作系统调用)。PROT_READ
PORT_WRITE
PROT_READ | PROT_WRITE
int flags
标志位参数,可以通过设置标志位来决定对映射区的修改是否反应到磁盘上。MAP_SHEARD
会将映射区所做的修改反映到物理设备上MAP_PRIVATE
映射区所做的修改不会反映到物理设备
int fd
用来建立映射区的文件描述符off_t offset
映射文件的偏移,用于截取文件的一部分建立映射区(4K的整数倍)
返回值:
成功返回创建映射区的首地址。失败返回MAP_FAILED
ftruncate
用来扩展文件大小
关闭映射区:
int munmap(void *addr, size_t length);
第一个参数必须是映射区的首地址,长度可以变化
成功返回0,失败返回-1
例如:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>int main()
{int fd = open("mmap1file",O_CREAT | O_RDWR | O_TRUNC ,0644);if(-1 == fd){perror("open error");exit(1);}if(-1 == ftruncate(fd, 128)) {perror("ftruncate error:");exit(1);}char *p = mmap(NULL, lseek(fd,0,SEEK_END), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if(MAP_FAILED == p){perror("mmap error:");exit(1);}strcpy(p,"Hello mmap!");close(fd); //关闭文件if(-1 == munmap(p,128)){perror("munmap error:");exit(1);}return 0;
}
注意事项
- 可以用
malloc
创建大小为0 的堆空间,并且可以free, 不可以创建大小为0 的映射区 - 如果文件是以只读方式打开的就不能改变文件大小
- 如果文件是以只读方式打开,那么不能将映射区设置为
MAP_SHARED
,但是可以将映射区设置为MAP_PRIVATE
- 如果文件以只写方式打开,将无法建立映射区,错误信息为
Permission denied
,因为创建映射区的过程中其实有一次隐含的读操作 - 创建映射区的权限要小于等与文件的权限
- 偏移量必须是页(4K)的整数倍
- 因为
mmap
容易出错,所以一定要保留mmap的返回值,并通过perror
输出错误信息 - 关闭映射区的时候
munmap
的第一个参数必须是映射区的首地址 - 建立映射区以后文件即使关闭也不影响,如果是
MAP_SHARED
的话仍旧会修改磁盘文件
利用共享内存在父子进程之间通信
用于通信的时候我们创建一个临时文件,成功创建映射区以后将文件关闭不再使用。
具体的方法是:
- 创建一个文件
- 使用
ftruncate
函数改变文件大小 - 使用
unlink
函数删除目录项(创建临时文件) - 建立映射区
- 关闭文件
在父子进程间通信时MAP_SHARED
指的是共享同一个映射区,MAP_PRIVATE
指的是不共享映射区,父子进程分别占用
匿名映射
因为正常mmap
函数必须依赖一个文件,虽然这个文件没有存在的必要,因此我们需要open
、ftruncate
、unlink
、close
比较麻烦。因此我们可以使用匿名映射较为方便地创建映射区。
int *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
len
可以根据我们的需要修改大小。
MAP_ANONYMOUS
表示匿名通信,可以简写为MAP_ANON
需要注意的是MAP_ANONYMOUS
只能够在Linux
系统中使用,在其他类Unix
系统中不可以使用,在其他系统中使用字符设备文件/dev/zero
int fd = open("/dev/zero", O_RDWR);
p = (int*)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
mmap无血缘关系进程通信
同一个文件创建的是一个映射区,因此如果我们想要在没有血缘关系,就需要通过同一个文件来建立映射区
strace 可执行文件
追踪程序里面所使用的系统调用有哪些
其实Linux系统对文件的操作是通过mmap进行的