Linux高阶——1026—验证内存映射&&mmap函数使用

时间:2024-11-01 19:59:08

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缓冲区内