mmap的使用

来源:互联网 发布:淘宝押金怎么交 编辑:程序博客网 时间:2024/05/01 06:25

UNIX提供了一个非常有用的功能,它允许程序共享内存,Linux内核从2.0版本开始已经把这一功能包括进来。mmap(内存映射,memory map)函数的作用是建立一段内存,使它能够被两个或更多个程序读写。一个程序对它所做出的修改可以被其他程序看见。

这一功能还可以用在文件的处理上。你可以使某个磁盘文件的全部内容看起来就像是内存中的一个数组。如果文件由记录组成,而这些记录又能够用C语言中的结构来描述的话,你就可以通过访问结构数组对文件的内容进行修改。

这要通过使用带特殊权限集的虚拟内存段才能实现。对这类虚拟内存段的读写会使操作系统去读写磁盘文件中与之对应的部分。

mmap函数创建一个指向一段内存区域的指针,该指针将与文件的内容相关联,而文件则是通过一个打开的文件描述符来访问。

#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

你可以通过传递off参数来改变经共享内存段访问的文件中数据的起始偏移值。打开的文件描述符由fildes参数给出。可以访问的数据量(即内存段的长度)由len参数设置。

你可以通过addr参数请求使用某个特定的内存地址。如果它的取值是零,结果指针将将自动分配。这是推荐的做法,否则会降低程序的可移植性,因为不同系统上的可用地址范围是不一样的。

prot参数用来设置内存段的访问权限。它是下列常数值的按位OR结果:

l PORT_READ:允许读该内存段。

l PORT_WRITE:允许写该内存段。

l PORT_EXEC:允许执行该内存段。

l PORT_NONE:该内存段不能被访问。

flags参数控制程序对该内存段的改变所造成的影响,如表3-7所示。

表  3-7

--------------------------+----------------------------------------------
    MAP_PRIVATE     |内存段是私用的,对它的修改只在此局部范围内有效
--------------------------|----------------------------------------------
    MAP_SHARED    |把对该内存段的修改保存到磁盘文件中
--------------------------|----------------------------------------------
    MAP_FIXED         |该内存段必须位于addr指定的地址处
-------------------------+----------------------------------------------

msync函数的作用是把在该内存段的某个部分或整段中的修改写回到被映射的文件中(或者从被映射文件里读出)。

#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
内存段需要修改的部分由作为参数传递过来的起始地址addr和长度len确定。flags参数控制着执行修改的具体方式,如表3-8所示。

表3-8

--------------------+-----------------------------------------------
    MS_ASYNC        |  采用异步写方式
--------------------|-----------------------------------------------
    MS_SYNC         |  采用同步写方式
--------------------|-----------------------------------------------
    MS_INVALIDATE   |  从文件中读回数据
--------------------+-----------------------------------------------

函数成功返回0, 失败返回-1.

munmap函数的作用是释放内存段
#include <sys/mman.h>
int munmap(void *addr, size_t len);

下面的程序mmap_eg.c演示了如何利用mmap和数组方式的存取操作对结构化数据文件进行修改。注意,2.0版本之前的Linux内核不完全支持mmap的这种用法。这个程序在Sun Solaris和其他操作系统上也能够正确运行。

例程:
 #include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <fcntl.h>

typedef struct {
    int integer;
    char string[24];
}RECORD;

#define NRECORDS 100

int main()
{
    RECORD record, *mapped;
    int i, f;
    FILE *fp;
    
    //创建、初始化文件
    fp = fopen("Records.dat", "w+");
    if(fp == 0)
    {
        printf("Error : open Records.dat fail /n");
        return -1;
    }

    for(i=0; i<NRECORDS; i++)
    {
        record.integer = i;
        sprintf(record.string, "RECORD--%d", i);
        fwrite(&record, sizeof(record), 1, fp);
    }
    fclose(fp);
    
    //把第43条记录的值改为143
    fp = fopen("Records.dat", "r+");
    if(fp == 0)
    {
        printf("Error : open Records.dat fail -- 2 /n");
        return -1;
    }
    fseek(fp, 43*sizeof(record), SEEK_SET);
    fread(&record, sizeof(record), 1, fp);
    printf("Record 43: %d, %s /n", record.integer, record.string);
    record.integer = 143;
    sprintf(record.string, "RECORD--%d", record.integer);
    fseek(fp, 43*sizeof(record), SEEK_SET);
    fwrite(&record, sizeof(record), 1, fp);
    fclose(fp);
    
    //映射到内存,操作内存数组
    f = open("Records.dat", O_RDWR);
    mapped = (RECORD *)mmap(0, NRECORDS*sizeof(record), PROT_READ | PROT_WRITE,
                            MAP_SHARED, f, 0);
    if(mapped == MAP_FAILED)
    {
        printf("MAP_FAILED /n");
        return -1;
    }

    printf("mapped[43]: %d, %s /n", mapped[43].integer, mapped[43].string);
    mapped[43].integer = 243;
    sprintf(mapped[43].string, "RECORD--%d", mapped[43].integer);
    msync((void *)mapped, NRECORDS*sizeof(record), MS_ASYNC);
    munmap((void *)mapped, NRECORDS*sizeof(record));
    close(f);
    
    //测试,打开文件查看内容是否更新
    fp = fopen("Records.dat", "r");
    fseek(fp, 43*sizeof(record), SEEK_SET);
    if(fp == 0)
    {
        printf("open Records.dat fail--3/n");
        return -1;
    }
    fread(&record, sizeof(record), 1, fp);
    printf("Record[43] : %d, %s/n", record.integer, record.string);
    fclose(fp);
    return 0;
}

原创粉丝点击