1. void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
addr: 建立映射区的首地址,由Linux内核指定,使用时直接传递NULL;
length:欲创建映射区的大小。
prot: 映射区权限 PROT_READ, PROT_WRITE, PROT_READ | PROT_WRITE
flags:标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区) MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上;MAP_PRIVATE: 映射区所做的修改不会映射到物理设备;
当用于父子进程通信时,MAP_SHARED表示映射区由父子进程共享,MAP_PRIVATE表示映射区由进程独享,所以IPC时需要SHARED。
fd: 用来建立映射区的文件描述符
offset: 映射文件的偏移(4K的整数倍)
返回值: 成功返回创建的映射区首地址;失败返回: MAP_FAILED((void*)-1)
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int var = 100;
int main(void)
{
int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
{
perror("open error");
exit(1);
}
if (ftruncate(fd, 4) < 0)
{
perror("ftruncate error");
exit(1);
}
unlink("test.txt");
int* p = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
// mmap建立完成后即可close fd了
close(fd);
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
exit(1);
}
if (pid == 0)
{
// 子进程
*p = 1234;
var = 1000;
printf("child process:*p = %d, var = %d\n", *p, var);
}
else
{
sleep(1);
// var属于进程独享,所以不受影响
// 当mmap是MAP_PRIVATE时,*p就不是子进程所更改的了而是0,map区域属于进程独享
printf("parent process: *p = %d, var = %d\n", *p, var);
wait(NULL);
}
if (munmap(p, 4) < 0)
{
perror("munmap error");
exit(1);
}
return 0;
}
2.mmap参数注意事项
length不能为0,也就是说文件大小不能为0;
munmap的地址必须和mmap出来的地址相同。
文件open时的权限和mmap的prot的选项:映射区的权限 <= 文件的打开权限; 建立映射区时隐含一次读操作。
offset必须是4K的整数倍,因为映射区是MMU建立的,MMU划分物理内存的最小单位是4K(一页的大小)。
文件描述符先关闭对mmap没影响,因为fd相当于文件句柄,操作文件的方式,而现在是在用映射区操作文件,不再需要fd,所以一旦mmap建立成功,即可关闭fd。
3. 结束时要调用munmap
int munmap(void* addr, size_t length);
返回值:成功返回0, 失败返回-1.
4. mmap父子进程间通信
父子进程共享:打开的文件;mmap建立的映射区(但必须指定MAP_SHARED).
5. 匿名映射:只支持Linux系统,类Unix需要另一种方法。
flag位加上MAP_ANONYMOUS(或者MAP_ANNO), 即MAP_SHARED | MAO_ANONYMOUS, 文件描述符用-1, 文件大小任意。
例如:int* p = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); --> 注意文件大小任意
类Unix系统需要下面两步来完成匿名映射区:
fd = open("/dev/zero", O_RDWR);
p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
6. 在无血缘关系进程间通信:进程间打开的文件路径一样即可