1 内存映射区
1.1mmap
函数
函数功能
- 创建内存映射
- 将磁盘文件的数据映射到内存中,通过修改内存就能修改磁盘文件
函数原型
length
:大小为4K的倍数,且不能为0。- 一般文件多大,映射区的大小就指定为多大
prot
:PROT_READ
:映射区必须要有读权限PROT_WRITE
- 读写权限是:
PROT_READ|PROT_WRITE
flags
:MAP_SHARED
:修改了内存数据会同步到磁盘MAP_PRIVATE
:修改了内存数据不会同步到磁盘
fd
:要映射的文件的fd
。通过open()
函数得到offset
:映射的时候文件指针的偏移量- 必须是4K的整数倍
- 一般指定为0
返回值
- 调用成功返回映射区的首地址
- 调用失败返回
MAP_FAILED
宏(-1)
1.2 munmap
函数
函数作用
- 释放内存映射区
函数原型
int munmap(void* addr,size_t length);
-
参数
addr
:mmap
的返回值,映射区的首地址length
:mmap
的第二个参数,映射区的大小 -
返回值
失败返回-1
2 有血缘关系进程之间的通信
有名内存映射区(借助文件)
通过在内存中创建文件的映射区实现两个进程间通过该映射区通信
A进程写B进程读,当A进程写的东西比较多时可能会出现A没写完,B已经读完了
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/mman.h>
int main(){
int fd = open("hello",O_RDWR);
if(fd == -1){
perror("文件打开失败");
exit(1);
}
//计算文件大小
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("文件映射区创建失败");
exit(2);
}
//创建子进程,进行进程间通信
pid_t pid = fork();
if(pid == -1){
perror("子进程创建失败");
exit(3);
}else if(pid == 0){
//子进程写,父进程读
strcpy((char*)ptr,"写的内容\n");
}else{
//父进程读
printf("%s",(char*)ptr);
wait(NULL);
}
//释放内存映射区,关闭文件
munmap(ptr,len);
close(fd);
return 0;
}
上段代码文件”hello“的作用很小,所以有了下段创建匿名内存映射区的方法
匿名内存映射区
与上段代码的差别
- 没有“hello"文件,内存映射区的大小也不是文件的大小
- 直接指定创建的内存映射区的大小
- 修改
mmap
函数的个别参数
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/mman.h>
int main(){
//直接指定内存映射区的长度
int len = 4096;
//创建内存映射区
void* ptr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
if(ptr == MAP_FAILED){
perror("文件映射区创建失败");
exit(2);
}
//创建子进程,进行进程间通信
pid_t pid = fork();
if(pid == -1){
perror("子进程创建失败");
exit(3);
}else if(pid == 0){
//子进程写,父进程读
strcpy((char*)ptr,"写的内容\n");
}else{
//父进程读
printf("%s",(char*)ptr);
wait(NULL);
}
//释放内存映射区,关闭文件
munmap(ptr,len);
return 0;
}
3 没有血缘关系进程之间的通信
- 不能使用匿名映射区
- 只能借助磁盘文件创建映射区
- 不阻塞
- 因为数据在内存映射区,所以数据是可以重复读的
读
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/mman.h>
int main(){
int fd = open("hello",O_RDWR|O_CREAT,0666);
if(fd == -1){
perror("打开文件失败");
exit(1);
}
ftruncate(fd,4096);
//计算出文件长度,用来指定开辟的映射区大小
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("映射区创建失败");
exit(2);
}
while(1){
printf("%s",(char*)ptr);
sleep(1);
}
//释放内存映射区
uunmap(ptr,len);
//关闭文件
close(fd);
return 0;
}
写
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<sys/mman.h>
#include<sys/types.h>
int main(){
int fd = open("hello",O_RDWR|O_CREAT,0666);
if(fd == -1){
perror("打开文件失败");
exit(1);
}
ftruncate(fd,4096);
//计算出文件长度,用来指定开辟的映射区大小
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("映射区创建失败");
exit(2);
}
char *buf = "没有血缘关系的进程间通信\n";
while(1){
strcpy(ptr,buf);
sleep(4);
}
//释放内存映射区
uunmap(ptr,len);
//关闭文件
close(fd);
return 0;
}