mmap
void *mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) // 创建共享内存映射
参数:
addr: 指定映射区的首地址。通常传NULL,表示让系统自动分配;
length:共享内存映射区的大小;(<= 文件的实际大小)
prot:共享内存映射区的读写属性; PROT_READ、PROT_WRITE、PROT_READ | PROT_WRITE
flags:标注共享内存的共享属性;MAP_SHARED、MAP_PRIVATE
fd:用于创建共享内存映射区的那个文件的 文件描述符;
offset:偏移位置,默认0,表示文件全部。必须是4K的整数倍返回值:
成功:映射区的首地址;
失败:MAP_FAILED((void*)-1),errno
munmap
int munmap(void* addr, size_t length); // 释放映射区
参数:
addr:mmap 的返回值;
length:大小
返回值:
成功:0;
失败:-1。
代码
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/mman.h>
int main(int argc, char* argv[])
{
int fd;
char* p=NULL;
fd=open("testmap", O_RDWR|O_CREAT|O_TRUNC, 0664);
if(fd==-1){
perror("open error");
}
/*
lseek(fd, 10, SEEK_END); // 扩展文件大小
write(fd, "\0", 1);
*/
ftruncate(fd, 20); // 需要写权限才能扩展文件大小
int len=lseek(fd, 0, SEEK_END);
p=mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(p == MAP_FAILED){
perror("mmap error");
}
// 使用p对文件进行读写操作
strcpy(p, "hello mmap");
printf("------------%s\n",p);
int ret=munmap(p, len); // 释放映射区
if(ret==-1){
perror("munmap error");
}
close(fd);
return 0;
}
使用注意
1、用于创建映射区的文件大小为0,实际指定非0大小,创建映射区,出现总线错误;
2、用于创建映射区的文件大小为0,实际指定0大小,创建映射区,出现无效参数错误;
3、创建映射区,需要read权限,当访问权限为MAP_SHARED时,mmap的读写权限,应该 <= 文件的open权限, 只写是不行的。
4、文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用 地址访问。
5、 offset必须是4096的整数倍.(MMU映射的最小单位是 4K)。
6、对申请的内存不能越界访问。
7、映射区访问权限为MAP_PRIVATE,对内存所做的所有修改,只在内存有效,不会反映到物理磁盘上。
8、映射区访问权限为MAP_PRIVATE,只需要open文件时,有读权限,用于创建映射区即可。
保险调用方式
1、fd = open(“文件名”, O_RDWR);
2、mmap(NULL, 有效文件大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
mmap父子进程间通信
1、父进程 先 创建映射区,open(O_RDWR), mmap(MAP_SHARED);
2、指定 MAP_SHARED权限;
3、fork()创建子进程;
4、一个进程读,一个进程写。
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>
int var = 100;
int main(void)
{
int *p;
int pid;
int fd;
fd=open("temp", O_RDWR|O_CREAT|O_TRUNC, 0644);
if(fd<0){
perror("open error");
exit(1);
}
ftruncate(fd, 4);
p=(int*)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); // 创建映射区
if(p == MAP_FAILED){
perror("mmap error");
exit(1);
}
close(fd);
pid = fork();
if(pid == 0){
*p=2000;
var=1000;
printf("child, *p = %d, var = %d\n",*p, var);
}else if(pid > 0){
sleep(1);
printf("parent, *p = %d, var = %d\n",*p, var);
wait(NULL);
int ret = munmap(p, 4); // 释放映射区
if(ret == -1){
perror("munmap error");
exit(1);
}
}
return 0;
}
非血缘关系进程间通信
1、两个进程 打开同一个文件,创建映射区;
2、指定flags为MAP_SHARED;
3、一个写入、一个读出。
【注意】:无血缘关系进程通信,
mmap:数据可以重复读取;
fifo:数据只能读取一次。
写进程
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/mman.h>
#include<fcntl.h>
struct student{
int id;
char name[256];
int age;
};
int main(int argc, char* argv[])
{
struct student stu = {10, "xiaoming", 18};
struct student *p;
int fd;
fd = open("test_map", O_RDWR|O_CREAT|O_TRUNC, 0664);
if(fd == -1){
perror("open error");
exit(1);
}
ftruncate(fd, sizeof(stu));
p = mmap(NULL, sizeof(stu), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(p == MAP_FAILED){
perror("mmap error");
exit(1);
}
while(1){
memcpy(p, &stu, sizeof(stu));
stu.id++;
sleep(1);
}
munmap(p, sizeof(stu));
return 0;
}
读进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/mman.h>
struct student{
int id;
char name[256];
int age;
};
int main(int argc, char* argv[])
{
struct student stu;
struct student *p;
int fd;
fd = open("test_map", O_RDONLY);
if(fd == -1){
perror("open error");
exit(1);
}
p = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);
if(p == MAP_FAILED){
perror("mmap error");
exit(1);
}
close(fd);
while(1){
printf("id = %d, name = %s, age = %d\n", p->id, p->name, p->age);
sleep(1);
}
munmap(p, sizeof(stu));
return 0;
}
匿名映射
只能用于血缘关系间的进程通信。
p = (int*) mmap(NULL, 所需大小长度, PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS, -1, 0);