《unix高级环境编程》高级 I/O—— 存储映射 I/O

        存储映射 I/O 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是从缓冲区中取数据,就相当于读文件的相应字节,同理,将数据写入缓冲区,则相应字节就会自动写入文件。这样可以不使用 read 和 write 函数的情况下执行 I/O。

        将一个给定的文件映射到缓冲区可以使用 mmap 函数;

/* 存储映射IO */
/*
 * 函数功能:将一个给定文件映射到存储区域中;
 * 返回值:若成功则返回缓冲区的起始地址,若出错则返回MAP_FAILED;
 * 函数原型:
 */
#include <sys/mman.h>
void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
/*
 * 说明:
 * addr参数用于指定映射存储区的起始地址,通常将其设置为0,这样由系统自动分配起始地址;
 * filedes指定要被映射文件的描述符,在映射之前,先要打开该文件;
 * len是映射的字节数;
 * off是要映射字节在文件中的起始偏移量;
 * prot是对映射存储区的保护要求,具体参数是以下的按位"或"组合:
 * PROT_READ    映射区可读
 * PROT_WRITE   映射区可写
 * PROT_EXEC    映射区可执行
 * PROT_NONE    映射区不可访问
 *
 * flag参数影响映射存储区的多重属性:
 * MAP_FIXED    返回值必须等于addr;
 * MAP_SHARED   说明本进程对映射区所进行的存储操作的配置;指定存储操作修改映射文件;
 * MAP_PRIVATE  对映射区的存储操作导致创建该映射文件的一个私有副本,所有后来对映射区的引用都是该副本;
 */

         存储映射文件的存储空间如下图所示:


         调用 mprotect 可以更改一个现有映射存储区的权限:

/*
 * 函数功能:更改一个现有映射存储区的权限;
 * 返回值:若成功则返回0,若出错则返回-1;
 * 函数原型:
 */
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
/*
 * 说明:
 * 参数prot和mmap函数的参数一样;
 * 起始地址addr必须是系统页长的整数倍;
 */

         如果在共享存储映射区中的页已被修改,那么我们可以调用 msync 将该页冲洗到被映射的文件中:

/*
 * 函数功能:将页冲洗到被映射的文件中;
 * 返回值:若成功则返回0,若出错则返回-1;
 * 函数原型:
 */
#include <sys/mman.h>
int msync(void *addr, size_t len, int flags);
/*
 * 说明:
 * 若映射是私有的,则不修改被映射的文件,地址必须与页边界对齐;
 * 参数flags取值如下:
 * MS_ASYNC         执行异步写
 * MS_SYNC          执行同步写
 * MS_INVALIDATE    使高速缓存的数据失效
 */

       当我们使用 mmap 函数成功映射存储区之后,我们可以关闭文件描述符 filedes,此操作并不会解除映射区,想要解除映射区必须调用 munmap 函数:

/*
 * 函数功能:解除映射存储区;
 * 返回值:若成功则返回0,若出错则返回-1;
 * 函数原型:
 */
#include <sys/mman.h>
int munmap(void *addr, size_t len);
        munmap 不会影响被映射的对象,也就是说,调用 munmap 不会使映射区的内容写到磁盘文件上。对于 MAP_SHARED 区磁盘文件的更新,在写到存储映射区时按内核虚拟内存算法自动进行,在解除了映射后,对于 MAP_PRIVATE 存储区的修改被丢弃。

测试程序:

#include "apue.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd;
    void *dst;
    struct stat f_stat;
    char buf[MAXLINE];
    memset(buf, 0, MAXLINE);
    char *src = "mmap munmap msync 12";
    if(argc != 2)
        err_quit("usage: a.out <pathname>");
    if((fd = open(argv[1], O_RDWR)) < 0)
        err_sys("open argv[1] file error");

    if(fstat(fd, &f_stat) == -1 )
        err_sys("fstat error");
    if((dst = mmap(NULL, f_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
        err_sys("mmap error");

    close(fd);
    memcpy(dst, src, 20);
    if((msync(dst, f_stat.st_size, MS_SYNC)) == -1)
        err_sys("msync error");

    if((munmap(dst, f_stat.st_size)) == -1)
        err_sys("munmap error");
    exit(0);
}

原始文件mmap.txt 里面的内容为:

 cat mmap.txt 
cccccccccccccccccc
ffffffffffffffffff
eeeeeeeeeeeeeeeeee
yyyyyyyyyyyyyyyyyy
qqqqqqqqqqqqqqqqqq
aaaaaaaaaaaaaaaaaa
pppppppppppppppppp
vvvvvvvvvvvvvvvvvv
zzzzzzzzzzzzzzzzzz
xxxxxxxxxxxxxxxxxx

映射存储之后的内容为:

mmap munmap msync 12
fffffffffffffffff
eeeeeeeeeeeeeeeeee
yyyyyyyyyyyyyyyyyy
qqqqqqqqqqqqqqqqqq
aaaaaaaaaaaaaaaaaa
pppppppppppppppppp
vvvvvvvvvvvvvvvvvv
zzzzzzzzzzzzzzzzzz
xxxxxxxxxxxxxxxxxx

输出结果:注意:以下是mmap以共享模式的即MAP_SHARED,若是以私有模式的并不会修改源文件

$cat mmap.txt 
cccccccccccccccccc
ffffffffffffffffff
eeeeeeeeeeeeeeeeee
yyyyyyyyyyyyyyyyyy
qqqqqqqqqqqqqqqqqq
aaaaaaaaaaaaaaaaaa
pppppppppppppppppp
vvvvvvvvvvvvvvvvvv
zzzzzzzzzzzzzzzzzz
xxxxxxxxxxxxxxxxxx
$ ./mmap mmap.txt 
$ cat mmap.txt 
mmap munmap msync 12
fffffffffffffffff
eeeeeeeeeeeeeeeeee
yyyyyyyyyyyyyyyyyy
qqqqqqqqqqqqqqqqqq
aaaaaaaaaaaaaaaaaa
pppppppppppppppppp
vvvvvvvvvvvvvvvvvv
zzzzzzzzzzzzzzzzzz
xxxxxxxxxxxxxxxxxx

参考资料:

《UNIX高级环境编程》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值