1、验证共享映射后修改文件内容,是否能够同步
先创建一个映射文件,写入数据
分为四个步骤
1、打开映射文件
设文件描述符,使用open函数
int fd;
if((fd=open("mapfile",O_RDWR))==-1)
{
perror("open failed");
exit(0);
}
获取文件大小,作为映射大小
int fsize;
fsize=lseek(fd,0,SEEK_END);
2、进行共享映射
int * ptr=NULL;
if((ptr=mmap(NULL,fsize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0))==MAP_FAILED)
{
perror("mmap failed");
exit(0);
}
3、映射成功,关闭描述符
close(fd);
4、修改映射内存
munmap释放映射的函数
ptr[0]=0x34333231;
munmap(ptr,fsize);
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<sys/mman.h>
int main()
{
int fd;
if((fd=open("mapfile",O_RDWR))==-1)
{
perror("open failed");
exit(0);
}
int fsize;
fsize=lseek(fd,0,SEEK_END);
int * ptr=NULL;
if((ptr=mmap(NULL,fsize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0))==MAP_FAILED)
{
perror("mmap failed");
exit(0);
}
close(fd);
ptr[0]=0x34333231;
munmap(ptr,fsize);
return 0;
}
成功截图
2、验证两个进程是否能够建立共享通信的方式
前提:必须保证映射文件是空的(保证不会出现还没有开始通信,但读端以及读到了数据的情况),但是需要有大小(否则无法映射)
使用ftruncate函数阶段,可以将文件大小变小或拓展空文件
写端
1、按照给对方的需求,定义一个结构体
typedef struct
{
int id;
char name[1024];
}shared_t;
2、创建文件
int fd=open("MAPIC",O_RDWR|O_CREAT,0664);
3、将文件截断
ftruncate(fd,sizeof(shared_t));
4、使用结构体指针进行映射
shared_t* ptr;
ptr=mmap(NULL,sizeof(shared_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
5、将结构体里的数据初始化
ptr->id=0;
bzero(ptr->name,1024);
6、循环写入数据
while(1)
{
++ptr->id;
sprintf(ptr->name,"test message id %d",ptr->id);
sleep(1);
}
7、 释放映射,关闭文件描述符
munmap(ptr,sizeof(shared_t));
close(fd);
总代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<sys/mman.h>
#include<sys/types.h>
typedef struct
{
int id;
char name[1024];
}shared_t;
int main()
{
int fd=open("MAPIC",O_RDWR|O_CREAT,0664);
ftruncate(fd,sizeof(shared_t));
shared_t* ptr;
ptr=mmap(NULL,sizeof(shared_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
ptr->id=0;
bzero(ptr->name,1024);
while(1)
{
++ptr->id;
sprintf(ptr->name,"test message id %d",ptr->id);
sleep(1);
}
munmap(ptr,sizeof(shared_t));
close(fd);
return 0;
}
读端
读端只需要读即可
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/fcntl.h>
#include<sys/mman.h>
#include<sys/types.h>
typedef struct
{
int id;
char name[1024];
}shared_t;
int main()
{
int fd=open("MAPIC",O_RDWR,0664);
shared_t* ptr;
ptr=mmap(NULL,sizeof(shared_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
while(1)
{
printf("%s\n",ptr->name);
sleep(1);
}
munmap(ptr,sizeof(shared_t));
close(fd);
return 0;
}
3、mmap使用时容易出现的错误
1、映射权限错误
如果其他对某文件没有权限的人也通过映射的方式将文件修改了,则意味着无论是否有权限,任何人都能修改任何文件,为了避免此类现象,有两种措施
1、每次映射前先出进程查看用户权限,然后再决定映射方式,但这种方式效率低
2、mmap映射时,映射的权限要小于等于open的权限,因为如果用户对文件只有读权限,就无法以读写模式打开
2、映射大小错误
如果映射文件是一个空文件,则映射时会出现错误
如果将mmap函数的第二个参数改为4096,即指定比映射文件大的映射大小,虽然不会报错,但mmap返回的映射内存地址的指针无法使用
如果使用,则会访问越界,抛出总线错误,进程被杀死
如果申请的内存为1024,但权限比映射文件小,则系统给的读写权限为0
因此,能够获得的映射大小一般小于等于映射文件大小
4、MMAP一般使用的领域
1、大数据处理(大文件处理)
切割处理,可以采用mmap偏移分段映射
可以将1TB的文件使用mmap分割成为4K大小的文件分段处理
offset偏移量,只能为4K或4K的整数倍(按页偏移)
2、文件加载到进程内存,采用映射
相比传统的read,映射拷贝开销更小,效率更高,因为read,可能要读取多次
3、零拷贝
DMA,高速缓冲区,可以避免cpu拷贝,不占用CPU
内核缓冲区,是服务器的缓冲区,存在于电脑
标准发文件流程
当调用read时,系统将磁盘中的文件通过DMA拷贝到内核缓冲区,又通过CPU拷贝将文件拷贝到用户缓冲区buffer中------------2次上下文切换------1次CPU拷贝
当调用send函数时,会把用户缓冲区内的数据通过CPU拷贝到SOCKET缓冲区内,通过DMA拷贝到网卡设备中,通过网卡发出去,用户端等待接收下载------------2次上下文切换------1次CPU拷贝
标准发文件流程是4次切换开销,2次cpu拷贝开销
零拷贝的宗旨是减少拷贝,减少开销
mmap函数优化
4次切换,1次拷贝
SENDFILE
2次切换,1次拷贝
sendfile可以将内核缓冲区的数据使用CPU拷贝到SOCKET缓冲区内