内存映射区的作用:将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。
#include
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数:
addr : 映射区首地址,一般传NULL
length :映射区的大小,不能为0,一般写文件大小。
prot : 映射区权限,可以在man文档中查看。常用的主要有两个:
PROT_READ--->必须指定的权限,因为内存映射区必须要有读权限
PROT_WRITW---> 写权限
flags : 常用的主要有两个
MAP_SHARED--->修改了内存数据会同步到磁盘
MAP_PRIVATE--->修改了内存数据不会同步到磁盘
fd : 要映射的文件的文件描述符
offset : 映射时候文件指针的偏移量,但必须是4k的整数倍,如0,4096,8192....
返回值
创建成功:返回映射区的首地址
创建失败:返回一个MAP_FAILED的宏,相当于 void*(-1), 同时errno被设置为一个合适的值
int munmap(void* addr, size_t length);
参数:
addr:映射区首地址,mmap()函数的返回值。
length:映射区长度
返回值:成功返回 0 ,失败返回 -1,错误原因存于errno中错误代码 EINVAL
本例首先创建一个文件,往其中写入一些数据,为防止文件中没有数据导致在程序中计算文件大小时为 0 ,而 0 不能作为 mmap 的第二个参数,这点要注意。
该代码实现父子进程间通过内存映射区进行通信。
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int fd = open("test.txt",O_RDWR);
int len = lseek(fd, 0, SEEK_END);
void *ptr = mmap(NULL,len,PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(ptr == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
close(fd);
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
if(pid > 0) //父进程写数据
{
strcpy(ptr, "this is a parent\n");
wait(NULL);
}
else if(pid == 0)
{
printf("%s\n",(char*)ptr); //子进程把父进程写入的数据打印出来
}
int ret = munmap(ptr, len);
if(ret == -1 )
{
perror("munmap error");
exit(1);
}
exit(0);
}
从上面的例子可以察觉到,test.txt这个文件并不重要,即我们对用来进行映射的原文件的内容并不关心,所以我们有创建匿名映射区的方式,不需要具体文件来进行映射,即可实现父子进程间的通信。
此时由于我们不需要打开某个文件,所以只需要对原来的代码做以下事情:
具体代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
int len = 4096; //需要的内存映射区大小
void *ptr = mmap(NULL,len,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if(ptr == MAP_FAILED)
{
perror("mmap error");
exit(1);
}
pid_t pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
if(pid > 0) //父进程写数据
{
strcpy(ptr, "this is a parent\n");
wait(NULL);
}
else if(pid == 0)
{
printf("%s\n",(char*)ptr); //子进程把父进程写入的数据打印出来
}
int ret = munmap(ptr, len);
if(ret == -1 )
{
perror("munmap error");
exit(1);
}
exit(0);
}
通过匿名映射区的方式,我们也可以进行父子进程间通信。
那么我们能否通过内存映射区进行两个无关联的进程间通信呢?当然是可以的。
不过有以下需要注意的地方: