目录
mmap
将一个指定文件映射到存储区域中。
man 2 mmap
参数addr
指定映射区的首地址。通常传NULL,让系统自动分配。
参数length
共享内存映射区的大小,小于等于文件的实际大小。
参数prot
共享内存映射区的读写属性,映射区权限。
可传参数:
PROT_READ
PROT_WRITE
PROT_READ|PROT_WRITE
参数flags
标注共享内存的共享属性,标志位参数,常用于设定更新物理区域、设置共享、创建匿名映射区。
可传参数:
MAP_SHARED:会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE:映射区所做的修改不会反映到物理设备。
参数fd
用于创建共享内容映射区的文件的文件描述符。
参数offset
偏移位置,必须是4k的整数倍。
0:默认值,表示映射文件全部。
返回值
成功:映射区的首地址。
失败:MAP_FAILED。
保险使用方式
fd=open("文件名",O_RDWR);
data=mmap(NULL,有效文件大小,PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
munmap
释放映射区。
参数addr
映射区首地址。
参数length
映射区大小。
返回值
成功:0
失败:-1
测试代码1
建立文件映射,读取并写入其他内容。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
int main(int argc, char *argv[])
{
int fd, flag;
char *data = NULL;
char data1[1024];
int leng;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp.txt", O_RDWR);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件映射。\n");
leng = lseek(fd, 0, SEEK_END);
data = mmap(NULL, leng, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
if (data == MAP_FAILED)
{
perror("文件映射错误");
exit(1);
}
printf("文件映射完成。\n");
printf("地址是%d。\n", *data);
printf("内容是:%s", data);
printf("开始写入文件。\n");
printf("写入的内容是:hello,world.\n");
strcpy(data, "hello,world.\n");
printf("写入文件完成。\n");
close(fd);
return 0;
}
测试结果
映射多大的内存空间,只能写入多大的内存空间。映射后的返回值是文件里的内容。
使用注意事项
用于创建映射区的文件大小为0时,实际指定非0大小创建映射区,报“总线错误”。
用于创建映射区的文件大小为0时,实际指定0大小创建映射区,报“无效参数”。
用于创建映射区的文件读写属性为只读时,映射区属性为读、写,报“无效参数”。
创建映射区需要read权限,当访问权限指定为“共享/MAP_SHARED”时,mmap的读写权限应该小于等于文件的open权限。
文件描述符fd,在mmap创建映射区完成后即可关闭。后续可以使用地址访问文件。
offset必须是4096的整数倍,因为MMU映射的最小单位为4k。
对申请的映射区内存,不能越界访问。
munmap用于释放地址,必须是mmap申请返回的地址。
映射区访问权限为“私有/MAP_PRIVATE”时,对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。
映射区访问权限为“私有/MAP_PRIVATE”时,只需要open文件时,有读权限用于创建映射区即可。
测试代码2
父子进程间通过映射区进行通信。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int fd, flag;
int *di_zhi;
pid_t JinCheng_ID;
int temp = 100;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp2.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件拓展。\n");
ftruncate(fd, 10);
printf("文件拓展完成。\n");
printf("开始文件映射。\n");
di_zhi = (int *)mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
if (di_zhi == MAP_FAILED)
{
perror("文件映射错误");
exit(1);
}
close(fd);
printf("文件映射完成。\n");
printf("开始创建进程。\n");
JinCheng_ID = fork();
if (JinCheng_ID == 0) //子进程
{
*di_zhi = 1000;
temp = 200;
printf("这是子进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
}
else if (JinCheng_ID > 0) //父进程
{
sleep(1);
printf("这是父进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
wait(NULL);
}
printf("开始释放映射区。\n");
flag = munmap(di_zhi, 10);
printf("释放映射区完成。\n");
return 0;
}
测试结果
测试代码3
两个毫无关系的进程通过映射区进行通信。
/*
CeShi3_1.c
发送数据的进程
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
struct XueSheng
{
int id;
char ming_zi[256];
int nian_ling;
};
int main(int argc, char *argv[])
{
int fd;
struct XueSheng xue_sheng = {1, "张三", 20};
struct XueSheng *di_zhi;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp3.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件拓展。\n");
ftruncate(fd, sizeof(xue_sheng));
printf("文件拓展完成。\n");
printf("开始文件映射。\n");
di_zhi = mmap(NULL, sizeof(xue_sheng), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
if (di_zhi == MAP_FAILED)
{
perror("文件映射错误");
exit(1);
}
close(fd);
printf("文件映射完成。\n");
printf("开始向映射区发送数据。\n");
while(1){
memcpy(di_zhi,&xue_sheng,sizeof(xue_sheng));
printf("发送的数据:id=%d,ming_zi=%s,nian_ling=%d。\n", di_zhi->id, di_zhi->ming_zi, di_zhi->nian_ling);
xue_sheng.id++;
sleep(1);
}
printf("开始释放映射区。\n");
munmap(di_zhi, sizeof(xue_sheng));
printf("释放映射区完成。\n");
return 0;
}
/*
CeShi3_2.c
接收数据的进程
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
struct XueSheng
{
int id;
char ming_zi[256];
int nian_ling;
};
int main(int argc, char *argv[])
{
int fd;
struct XueSheng xue_sheng;
struct XueSheng *di_zhi;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp3.txt", O_RDWR);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件映射。\n");
di_zhi = mmap(NULL, sizeof(xue_sheng), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
if (di_zhi == MAP_FAILED)
{
perror("文件映射错误");
exit(1);
}
close(fd);
printf("文件映射完成。\n");
printf("开始向映射区接收数据。\n");
while (1)
{
printf("接收的的数据:id=%d,ming_zi=%s,nian_ling=%d。\n", di_zhi->id, di_zhi->ming_zi, di_zhi->nian_ling);
sleep(1);
}
printf("开始释放映射区。\n");
munmap(di_zhi, sizeof(xue_sheng));
printf("释放映射区完成。\n");
return 0;
}
测试结果
匿名映射
只能用于血缘关系进程间的通信。
测试代码4
父子进程间使用匿名映射通信。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int flag;
int *di_zhi;
pid_t JinCheng_ID;
int temp = 100;
printf("程序开始运行。\n");
printf("开始文件映射。\n");
di_zhi = (int *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); //全部映射
if (di_zhi == MAP_FAILED)
{
perror("文件映射错误");
exit(1);
}
printf("文件映射完成。\n");
printf("开始创建进程。\n");
JinCheng_ID = fork();
if (JinCheng_ID == 0) //子进程
{
*di_zhi = 1000;
temp = 200;
printf("这是子进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
}
else if (JinCheng_ID > 0) //父进程
{
sleep(1);
printf("这是父进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
wait(NULL);
}
printf("开始释放映射区。\n");
flag = munmap(di_zhi, 10);
printf("释放映射区完成。\n");
return 0;
}