内存映射I/O

来源:互联网 发布:乐视mac码怎么查 编辑:程序博客网 时间:2024/04/27 19:06

1.概念

将一个文件与内存中的某一个缓冲区建立一种映射关系。用户对缓冲区进行读写就相当于对相应的文件进行读写。

 

2.创建内存映射

#include <sys/mman.h>
定义函数 void *mmap(void *start,size_t length,int prot,int flags,intfd,off_t offsize);
函数说明mmap()用来将某个文件内容映射到内存中,对该内存区域的存取即是直接对该文件内容的读写。参数start指向欲对应的内存起始地址,通常设为NULL,代表让系统自动选定地址,对应成功后该地址会返回。参数length代表将文件中多大的部分对应到内存。
参数 prot代表映射区域的保护方式有下列组合
PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE 映射区域不能存取
参数 flags会影响映射区域的各种特性
MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。参数fd为open()返回的文件描述词,代表欲映射到内存的文件。参数offset为文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值 若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
错误代码 EBADF 参数fd 不是有效的文件描述词EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。
EINVAL 参数start、length 或offset有一个不合法。
EAGAIN 文件被锁住,或是有太多内存被锁住。
ENOMEM 内存不足。

 

//nmap.c

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

int main(void)
{
    int fd;
    char *buf;
    int i;
    struct stat statbuf;

    if(stat("test.txt", &statbuf) == -1){ /* 得到一个文件的状态信息,得到文件的大小 */
        perror("fail to get stat");
        exit(1);
    }

    fd = open("test.txt", O_RDONLY); /* 以只读方式打开文件 */
    if(fd == -1){
        perror("fail to open");
        exit(1);
    }

    /* 建立一个内存映射,起始地址由系统为用户选择,并作为返回值返回
    * 建立的映射区的大小为打开的文件的大小
    * 访问权限为只读,属性为不会写到磁盘,防止对其进行写的误操作
    */
    buf = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(buf == MAP_FAILED){
        perror("fail to mmap");
        exit(1);
    }

    i = 0;
    while(i < statbuf.st_size){ /* 输出每一个字符,注意mmap函数不会添加’/0’结束符 */
        printf("%c", buf[i]);
        i++;
    }
    printf("/n");
   
    if(munmap(buf, statbuf.st_size) == -1){ /* 撤销内存映射 */
        perror("fail to munmap");
        exit(1);
    }

    close(fd); /* 关闭文件 */
   
    return 0;
}

 

 

3.撤销一个内存映射

 

#include<unistd.h>
#include<sys/mman.h>
定义函数 int munmap(void *start,size_t length);
函数说明 munmap()用来取消参数start所指的映射内存起始地址,参数length则是欲取消的内存大小。当进程结束或利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述词时不会解除映射。
返回值 如果解除映射成功则返回0,否则返回-1,错误原因存于errno中。

错误代码EINVAL参数 start或length 不合法。

 

4.内存映射同步

保证对缓冲区进行的修改回反映到磁盘中。

 

#include <sys/mman.h>

int msync(void * addr,sizt_t len,int flags);

 

addr表示要写回磁盘的内存映射区,flags表示回写的标志。

msync                       意义

MS_ASYNC                不等待页面写回磁盘,只是将修改过的页面放入内核的写出队列中,msync函数立即返回

MS_SYNC                  等待页面写回磁盘,知道页面写磁盘完毕,再返回

MS_INVALIDATE        丢弃指定范围的所有页面

 

 

5.更改内存映射的权限

 

#include <sys/mman.h>

int mprotect(void * addr,size_t len,int PROT);

 

addr是内存映射区的首地址,len是映射区的大小,PROT表示修改后的新权限。

 

//protect.c

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

/* SIGBUS信号的处理函数,输出一行提示信息 */
void sigbus_handler(int signo)
{
    printf("permission denied/n");
}

int main(void)
{
    int fd;
    char *buf;
    struct stat statbuf;

    if(signal(SIGBUS, sigbus_handler) == SIG_ERR){ /* 设置SIGBUS的信号处理程序 */
        perror("can’t set handler for SIGALRM");
        exit(0);
    }

    if(stat("test.txt", &statbuf) == -1){ /* 得到一个文件的状态信息,得到文件的大小 */
        perror("fail to get stat");
        exit(1);
    }

    fd = open("test.txt", O_WRONLY); /* 以可写方式打开文件 */
    if(fd == -1){
        perror("fail to open");
        exit(1);
    }

    /* 建立一个内存映射区,其访问权限为只读 */
    buf = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if(buf == MAP_FAILED){
        perror("fail to mmap");
        exit(1);
    }
   
    printf("try to write/n");
    strcpy(buf, "China/n"); /* 尝试写一个只读的映射区 */

    /* 将映射区的访问权限改变为可读可写 */
    if(mprotect(buf, statbuf.st_size, PROT_READ | PROT_WRITE) == -1){
        perror("fail to alter permission");
        exit(1);
    }

    printf("write again/n");
    strcpy(buf, "China/n"); /* 再次写一个只读的映射区 */

    if(munmap(buf, statbuf.st_size) == -1){ /* 撤销内存映射 */
        perror("fail to munmap");
        exit(1);
    }

    close(fd); /* 关闭文件 */
   
    return 0;
}

原创粉丝点击