zoukankan      html  css  js  c++  java
  • mmap

    存储映射

    存储映射I/O(Memory-mapped I/O)使一个磁盘文件与存储空间中的一个缓冲区相映射. 于是当从缓冲区中取数据, 就相当于读文件中的相应字节. 与此类似, 将数据存入缓冲区, 则相应的字节就自动写入文件. 这样, 就可在不适用read和write函数的情况下, 使用地址(指针)完成I/O操作

    使用这种方法, 首先应通知内核, 将一个指定文件映射到存储区域中. 这个映射工作可以通过mmap函数来实现

    作用: 将磁盘文件的数据映射到内存, 用户通过修改内存就能修改磁盘文件

    mmap创建内存映射
      

    匿名映射

    通过使用我们发现, 使用映射区来完成文件读写操作十分方便, 父子进程间通信也较容易. 但缺陷是, 每次创建映射区一定要依赖一个文件才能实现. 通常为了建立映射区要open一个temp文件, 创建好了再unlink, close掉, 比较麻烦. 可以直接使用匿名映射来代替. 其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定

    使用MAP_ANONYMOUS (或MAP_ANON), 如: int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

    需注意的是, MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏. 在类Unix系统中如无该宏定义, 可使用如下两步来完成匿名映射区的建立

    fd = open("/dev/zero", O_RDWR);
    p = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);
    

    mmap无血缘关系进程间通信

    实质上mmap是内核借助文件帮我们创建了一个映射区, 多个进程之间利用该映射区(需借助文件)完成数据传递. 由于内核空间多进程共享, 因此无血缘关系的进程间也可以使用mmap来完成通信. 只要设置相应的标志位参数flags即可. 若想实现共享, 当然应该使用MAP_SHARED了

    基础API

    mmap

    void *mmap(
    	void *addr,		// 建立映射区首地址, 由linux内核指定, 传NULL 
    	size_t length, 	// 映射区的大小, 4k的整数倍, 不能为0, 一般文件有多大length就为多大
    	int prot, 		// 映射区的权限, PROT_READ(映射区  必须  要有读权限), PROT_WRITE
    	int flags,		// 标志位参数, 	MAP_SHARED(数据同步到磁盘), MAP_PRIVATE(数据不会同步到磁盘), 有血缘关系通信需是MAP_SHARED
    	int fd, 		// 文件描述符, 	要映射文件对应的fd(open得来的)
    	off_t offset	// 映射文件的偏移量, 	映射时文件指针的偏移量, 必须是4k的整数倍, 一般为0
    );
    

    返回值: 成功, 映射区首地址; 失败, MAP_FAILED

    思考问题
    如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功?
      不能对ptr做操作, 可以复制一份, 对复制的指针进行操作
    如果open是O_RDONLY, mmap时prot参数指定PROT_READ|PROT_WRITE会怎样
      mmap调用失败, open文件指定权限应该大于等于mmap第三个参数prot指定的权限
    如果文件偏移量为1000会怎样
      必须是4096的整数倍
    如果不检测mmap的返回值会怎么样
      没什么影响
    mmap什么情况下会调用失败
      第二个参数length=0; 第三个参数没有指定PROT_READ; 第五个参数fd对应的open权限必须大于port权限; offset必须是4096的整数倍
    可以open的时候O_CREAT一个新文件来创建映射区吗
      可以, 需要做文件拓展(lseek, truncate)
    mmap后关闭文件描述符, 对mmap映射有无影响
      没有影响
    对ptr越界操作会怎样
      段错误

    munmap

    int munmap(void *addr, size_t length);

    参数:
      addr: mmap的返回值
      length: mmap的第二个参数

    进程间通信, 不阻塞, 数据直接才内存中处理,
      有血缘关系,
        父子进程共享内存映射区, 可以创建匿名映射区, 可以不需要磁盘文件进行通信
      没有血缘关系
        不能使用匿名映射的方式, 只能借助磁盘文件创建映射区
        A(a.c) B(b.c)
        a.c: int fd1 = open("XXX"); void *ptr = mmap(,,,fd1, 0); 对映射区(ptr)进行读写操作
        b.c: int fd2 = open("hello"); void ptr* = mmap(,,,fd2, 0); 对映射区(ptr)进行写操作

    示例程序

    利用内存映射区读文件

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    int main(int argc, const char* argv[]) {
        // 打开一个文件
        int fd = open("english.txt", O_RDWR);
        int len = lseek(fd, 0, SEEK_END);
        // 创建内存映射区
        void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
            perror("error");
            exit(1);
        }
    
        printf("%s", (char*)ptr);
    
        // 释放内存映射区
        munmap(ptr, len);
        close(fd);
    
        return 0;
    }
    

    MAP_PRIVATE与MAP_SHARED测试

    父子进程共享:

    1. 打开的文件
    2. mmap建立的映射区(但必须要使用MAP_SHARED)
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    
    int main(int argc, const char* argv[]) {
        // 打开一个文件
        int fd = open("english.txt", O_RDWR);
        int len = lseek(fd, 0, SEEK_END);
        
        // 通信测试
        //void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);    // MAP_PRIVATE, 父子进程不可通信
        void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   // MAP_SHARED, 父子进程可通信
        if (ptr == MAP_FAILED) {
            perror("error");
            exit(1);
        }
        close(fd);
    
        //printf("%s", (char*)ptr);
    
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork error");
            exit(1);
        }
        if (pid > 0) {
            // 父进程写数据
            strcpy((char*)ptr, "haha, hehe");
            // 回收
            wait(NULL);
        }
        else if (pid == 0) {
            // 读数据
            printf("%s
    ", (char*)ptr);
        }
    
        // 释放内存映射区
        //ptr++;
        int ret = munmap(ptr, len);
        if (ret == -1) {
            perror("mmap error");
            exit(1);
        }
    
        return 0;
    }
    
    

    有血缘关系匿名映射区通信

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    
    int main(int argc, const char* argv[]) {
        // 创建匿名内存映射区
        int len = 4096;
    
        void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);      // MAP_SHARED可通信
        //void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);   // MAP_PRIVATE, 不可通信
        if (ptr == MAP_FAILED) {
            perror("error");
            exit(1);
        }
    
        //printf("%s", (char*)ptr);
    
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork error");
            exit(1);
        }
        if (pid > 0) {
            // 父进程写数据
            strcpy((char*)ptr, "haha");
            // 回收
            wait(NULL);
        }
        else if (pid == 0) {
            // 读数据
            printf("%s
    ", (char*)ptr);
        }
    
        // 释放内存映射区
        //ptr++;
        int ret = munmap(ptr, len);
        if (ret == -1) {
            perror("mmap error");
            exit(1);
        }
    
        return 0;
    }
    
    

    无血缘关系利用内存映射区通信

    读端

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    int main(int argc, const char *argv[]) {
        int fd = open("temp", O_RDWR | O_CREAT, 0664);
        ftruncate(fd, 4096);
        int len = lseek(fd, 0, SEEK_END);
    
        void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
            perror("mmap");
            exit(1);
        }
    
        while (1) {
            sleep(1);
            printf("%s
    ", (char*)ptr+1024);
        }
    
        // 释放
        int ret = munmap(ptr, len);
        if (ret == -1) {
            perror("munmap");
            exit(1);
        }
    
        return 0;
    }
    

    写端

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    
    int main(int argc, const char *argv[]) {
        int fd = open("temp", O_RDWR | O_CREAT, 0664);
    
        void *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
            perror("mmap");
            exit(1);
        }
    
        while (1) {
            char *p = (char *)ptr;
            p += 1024;
            strcpy(p, "haha, I'm fine ~
    ");
            sleep(2);
        }
    
        int ret = munmap(ptr, 4096);
        if (ret == -1) {
            perror("munmap");
            exit(1);
        }
    
        return 0;
    }
    
  • 相关阅读:
    C++11:02decltype关键字
    git 源操作,分支操作
    git操作命令以及优点
    drf--序列化组件
    项目相关 --知识点
    vue框架:
    drf --解析器,异常模块,响应模块 序列化组件
    drf框架相关
    中间键 csrf跨站请求伪造 装饰器相关 auth模块
    多对多表的创建方式 forms组件 session与cookie
  • 原文地址:https://www.cnblogs.com/hesper/p/10738880.html
Copyright © 2011-2022 走看看