进程间数据共享--文件与内存的映射

进程间数据共享--文件与内存的映射

前言

    在运行strace  -c nginx的时候,我们可以看出mmap、mprotect和munmap使用的比例是很高的。因此可以看出nginx运行的时候有很大一部分时间在处理文件与内存之间的映射。



接口说明

mmap

    mmap在调用进程的虚拟地址空间中创建一个新的映射。

    #include <sys/mman.h>

    void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);


    addr:新映射的起始地址。如果addr为NULL,那么内核会选择创建映射的地址;这是创建新映射的最方便的

    方法。如果addr不是NULL,那么内核将它作为提示映射位置的提示;在Linux上,映射将在附近的页面边界

    创建。作为调用的结果,新映射的地址被返回。


    length:映射的长度。文件映射的内容使用文件描述符fd引用的文件中的偏移量offset开始的长度字节进

    行初始化。


    prot:参数描述映射所需的内存保护(并且不得与文件的打开模式冲突)。 它是PROT_NONE或一个或多个

    的按位或,具体标志如下所示:

        PROT_EXEC页可能会被执行。

        PROT_READ页可能被读取。

        PROT_WRITE页可以写入。

        PROT_NONE页可能无法访问。


    offset:必须是由sysconf(_SC_PAGE_SIZE)返回的页面大小的倍数。


    flags:参数确定对映射的更新对映射同一区域的其他进程是否可见,以及是否将更新传递到底层文件。常用

    的flags如下:

        MAP_SHARED

        和其他进程共享这个映射。 映射的更新对映射此文件的其他进程可见,并传递到底层文件。 在调用

        msync(2)或munmap()之前,文件可能不会实际更新。


        MAP_PRIVATE

        创建一个私人写时拷贝映射。 对映射的更新对映射相同文件的其他进程不可见,并且不会传递到底层

        文件。 没有指定在映射区域中是否可见mmap()调用后文件所做的更改。


munmap

    int munmap(void *addr, size_t length);


    munmap()系统调用会删除指定地址范围的映射。 该进程终止时,该区域也会自动取消映射。 另一方面,关闭文件描述

    符不会取消映射该区域。


    地址addr必须是页面大小的倍数。 包含部分指定范围的所有页面都未映射,随后对这些页面的引用将生成SIGSEGV。 如

    果指定的范围不包含任何映射页面,则不是错误。

mprotect

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


    mprotect为包含区间[addr,addr + len-1]中地址范围的任何部分的调用进程的内存页面更改保护。 

    addr必须与页面边界对齐。


    如果调用进程试图以违反保护的方式访问内存,那么内核会为该进程生成一个SIGSEGV信号。


        prot是PROT_NONE或按位或以下列表中的其他值:

        PROT_NONE内存根本无法访问。

        PROT_READ可以读取内存。

        PROT_WRITE可以修改内存。

        PROT_EXEC可以执行内存。

举例说明

测试代码

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

#define FILE_MODE       (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define DIR_MODE        (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)

int main(int argc, char *argv[])
{
        int                     fdin, fdout;
        void            *src, *dst;
        size_t          copysz = 4096;
        struct stat     sbuf;
        off_t           fsz = 0;

        if (argc != 3){
                printf("usage: %s <fromfile> <tofile> \n", argv[0]);
                exit(-1);
        }
        if ((fdin = open(argv[1], O_RDONLY)) < 0){
                printf("can't open %s for reading", argv[1]);
                exit(-1);
        }
        if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0){
                printf("can't creat %s for writing", argv[2]);
                exit(-1);
        }       

        if (fstat(fdin, &sbuf) < 0){
                /* need size of input file */
                printf("fstat error");
                exit(-1);
        }

        //设置目标文件的长度
        if (ftruncate(fdout, sbuf.st_size) < 0){
                /* set output file size */
                printf("ftruncate error");
                exit(-1);
        }

        //循环从原文件读取文件内容,并复制到目标内容
        while (fsz < sbuf.st_size) {

                //设置原文件文件映射,
                if ((src = mmap(0, copysz, PROT_READ, MAP_SHARED,fdin, fsz)) == MAP_FAILED){
                        printf("mmap error for input");
                        exit(-1);
                }

                //设置目标文件映射
                if ((dst = mmap(0, copysz, PROT_READ,MAP_SHARED, fdout, fsz)) == MAP_FAILED){
                        printf("mmap error for output");
                        exit(-1);
                }

                //修改目标文件映射的权限,否则程序报SIGSEGV中断
                if(mprotect(dst, copysz, PROT_WRITE)!=0){
                        printf("mprotect error for output");
                        exit(-1);
                }
                //拷贝文件
                memcpy(dst, src, copysz);       
                munmap(src, copysz);
                munmap(dst, copysz);
                fsz += copysz;

        }
        exit(0);

}

参考资料

[1]. http://www.man7.org/linux/man-pages/man2/mmap.2.html 

[2]. http://www.man7.org/linux/man-pages/man2/mprotect.2.html 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值