学习了一下Linux下内存映射文件的用法,在这里共享一下自己的收获,希望大家提出宝贵意见,进行交流。
    
        简介:

        内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。

Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明:

  • 头文件:
    • <unistd.h>
    • <sys/mman.h>
  • 原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
  • 返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
  • 参数:
    • addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
    • length: 将文件的多大长度映射到内存.
    • prot: 映射区的保护方式, 可以是:
      • PROT_EXEC: 映射区可被执行.
      • PROT_READ: 映射区可被读取.
      • PROT_WRITE: 映射区可被写入.
      • PROT_NONE: 映射区不能存取.
    • flags: 映射区的特性, 可以是:
      • MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
      • MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
      • 此外还有其他几个flags不很常用, 具体查看linux C函数说明.
    • fd: 由open返回的文件描述符, 代表要映射的文件.
    • offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.

 

    下面说一下内存映射的步骤:

  • 用open系统调用打开文件, 并返回描述符fd.
  • 用mmap建立内存映射, 并返回映射首地址指针start.
  • 对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).
  • 用munmap(void *start, size_t lenght)关闭内存映射.
  • 用close系统调用关闭文件fd.

 

注意事项:

在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的.

 

        在网上找了一些代码,自己加工之后在Linux下编译通过,可以运行实现相应功能。

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>

int main()
{
    int outfile;
    char*mapped;
    char*ptr;

    if((outfile=open("test.dat",O_RDWR|O_CREAT|O_TRUNC,0640))==-1)
    {
        printf("couldn't open this file!\n");
        exit(254);
    }
    lseek(outfile,1000,SEEK_SET);//把文件指针outfile移到从文件头开始到1000字节处

    if(write(outfile,"\0",1)==-1)//在第1001字节处写入数据"\0"

    {
        printf("error,write failed!\n");
        exit(254);
    }
 //创建内存映像文件,将文件从文件头开始到1000字节处映射到内存,mapped指向该内存的首地址

    mapped=mmap(NULL,1000,PROT_READ|PROT_WRITE,MAP_SHARED,outfile,0);

    if(!mapped)
    {
         printf("error ,write failed!\n");
         exit(254);
    }
    ptr=mapped;
    printf("please enter a number!\n");
    fgets(mapped,80,stdin);//从终端输入一个数字

    printf("your number times two is:%d\n",atoi(mapped)*2);
    //将该数字与2相乘结果打印出来

    sprintf(ptr,"your number timews two is :%d\n",atoi(mapped)*2);//把数据保存到内存中

    msync(ptr,1000,MS_SYNC);//将内存中数据保存到文件中

    munmap(ptr,1000); //撤销内存映像文件


    if(close(outfile))
    {
        printf("possibly serious error,close file failed!\n");
        exit(254);
    }

    return 0;
}

 

Logo

更多推荐