首页 > 系统相关 >Linux多进程11-内存映射

Linux多进程11-内存映射

时间:2023-05-17 18:45:32浏览次数:37  
标签:11 映射 mmap PROT 内存 Linux include ptr

内存映射(Memory-mapped I/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。

image

mmap

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
    功能: 将一个文件或者设备的数据映射到内存中
    参数:
        - void* addr: NULL 由内核指定
        - length: 要映射的数据的长度, 这个值不能为0, 建议使用文件的长度
            (获取文件的长度: stat lseek)
        - prot: 对申请的内存映射区的操作权限
            - PROT_EXEC: 可执行的权限
            - PROT_READ: 读权限
            - PROT_WRITE: 写权限
            - PROT_NONE: 没有权限
            要操作映射内存, 必须要有读权限, 如: PROT_READ|PROT_WRITE
        - flags:
            - MAP_SHARED: 映射区的数据会自动和磁盘文件进行同步, 进程间通信,必须要设置这个选项
            - MAP_PRIVATE: 不同步, 内存映射区的数据改变了, 对原来的文件不会修改, 会重新创建一个新的文件(copy on write)
        - fd: 需要映射的那个文件的文件描述符
            - 通过open得到, open的是一个磁盘文件
            - 注意: 文件的大小不能为0, open指定的权限不能和prot参数有冲突(prot参数要小于等于open参数)
        - offset: 偏移量(一般不用), 必须指定的是4K的整数倍, 0表示不偏移
    返回:
        返回创建的内存的首地址
        失败返回 MAP_FAILED, (void*) -1

int munmap(void *addr, size_t length);
    功能: 释放内存映射
    参数:
        - addr: 要释放的内存的首地址
        - length: 要释放的内存大小, 要和mmap函数中的length参数值一样

//使用内存映射实现进程间通信

    1. 有关系的进程(父子进程)
        - 还没有子进程的时候, 通过唯一的父进程, 先创建内存映射区
        - 有了内存映射区以后, 创建子进程
        - 父子进程共享创建的内存映射区

    2. 没有关系的进程间通信
        - 准备一个大小不是0的磁盘文件
        - 进程1 通过磁盘文件创建内存映射区, 得到一个操作这块内存的指针
        - 进程2 通过磁盘文件创建内存映射区, 得到一个操作这块内存的指针
        - 使用内存映射区通信

    注意: 内存映射区通信是非阻塞的

实例: 内存映射实现父子进程通信

test.txt

helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld,helloworld.

mmap-parent-child-ipc.c

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

int main(int argc, char const *argv[])
{
    //打开一个文件
    int fd = open("test.txt", O_RDWR);
    //获取文件大小
    int size = lseek(fd, 0, SEEK_END);

    //创建内存映射区
    void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap err");
        exit(0);
    }

    //创建子进程
    pid_t pid = fork();
    if (pid > 0)
    {
        //回收完子进程的资源之后再读数据
        wait(NULL);
        //父进程 读数据  
        char buf[64];
        strcpy(buf, (char *)ptr);
        printf("read data: %s\n", buf);
    }
    else if (pid == 0)
    {
        //子进程  写数据
        strcpy((char *)ptr, "nihao a, son!"); 
    }

    //关闭内存映射区
    munmap(ptr, size);

    return 0;
}

运行程序

$./mpci
read data: nihao a, son!

内存映射的注意事项

◼ 如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功?
void * ptr = mmap(...); ptr++; 可以对其进行++操作
但是 munmap(ptr, len);释放内存映射操作会产生错误, 要提前保存地址

◼ 如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
错误, 会返回MAP_FAILED; 建议open函数权限建议和prot参数权限保持一致

◼ 如果文件偏移量为1000会怎样?
偏移量必须是4K的整数倍, 返回MAP_FAILED

◼ mmap什么情况下会调用失败?

  • 第二个参数: length=0
  • 第三个参数: prot
    • 只指定了写权限
    • prot函数 PROT_READ | PROT_WRITE
  • 第五个参数 fd 通过open函数时指定的 O_RDONLY / O_WRONLY
  • 第六个参数偏移量必须是4K整数倍

◼ 可以open的时候O_CREAT一个新文件来创建映射区吗?
可以的, 但创建的文件大学不能为0; 可以使用 lseek() / truncate() 函数对新文件扩展

◼ mmap后关闭文件描述符,对mmap映射有没有影响?

没有任何影响, close(fd); 后映射区还存在, 内存还是没有释放(munmap)

◼ 对ptr越界操作会怎样?
void* ptr = mmap(NULL, 100, ,,,,);
越界操作是操作非法的内存 -> 段错误

内存映射完成文件复制

image-20221104145946378
//使用内存映射实现文件拷贝功能
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    // 1.对原始文件进行内存映射
    int fd = open("english.txt", O_RDWR);
    if (fd == -1)
    {
        perror("open err");
        exit(0);
    }
    //获取原始文件大小
    int len = lseek(fd, 0, SEEK_END);

    // 2.创建一个新文件
    int fd1 = open("cpy.txt", O_RDWR | O_CREAT, 0664);
    if (fd1 == -1)
    {
        perror("open err");
        exit(0);
    }
    //拓展该文件
    truncate("cpy.txt", len);
    write(fd1, " ", 1);

    // 3.把新文件数据映射到内存中
    void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    void *ptr1 = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);

    if (ptr == MAP_FAILED)
    {
        perror("mmap err");
        exit(0);
    }

    // 4.通过内存拷贝将一个新的文件内存数据拷贝到新的文件内存中
    memcpy(ptr1, ptr, len);

    // 5.释放资源 (后打开先释放)
    munmap(ptr1, len);
    munmap(ptr, len);
    close(fd1);
    close(fd);

    return 0;
}

匿名映射

/*
    匿名映射: 不需要文件实体进行一个内存映射, 因此也只能做父子进程通信
*/

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

int main(int argc, char const *argv[])
{
    // 1.创建匿名内存映射区
    int len = 4096;
    void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (ptr == MAP_FAILED)
    {
        perror("mmap err");
        exit(0);
    }

    //父子进程间通信
    pid_t pid = fork();
    if (pid > 0)
    {
        //父进程
        strcpy((char *)ptr, "hello, world");
        wait(NULL);
    }
    else if (pid == 0)
    {
        //子进程
        sleep(1);
        printf("%s\n", (char *)ptr);
    }

    //释放内存映射区
    int ret = munmap(ptr, len);
    if (ret == -1)
    {
        perror("munmap err");
        exit(0);
    }

    return 0;
}

运行程序

$./mmap-anon 
hello, world  #停1s后打印

标签:11,映射,mmap,PROT,内存,Linux,include,ptr
From: https://www.cnblogs.com/anqwjoe/p/17409736.html

相关文章

  • Linux多进程10-有名管道实现简单版聊天功能
    chatA.c//有名管道实现简单版聊天功能#include<unistd.h>#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>#include<string.h>intmain(intargc,charconst*argv[]){......
  • Linux多进程13-kill,raise,abort函数
    #include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsig);-功能:给某个进程pid,发送某个信号sig-参数:-pid:>0:将信号发送给指定的进程=0:将信号发送给当前的进程组=-1:将信号发送给每一个......
  • Linux多进程12-信号
    信号概念信号是Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。发往进程的诸多信......
  • 电子秤实物量产资料 原理图和PCB文件及BOM,源码HEX 量产HX711
    电子秤实物量产资料原理图和PCB文件及BOM,源码HEX量产HX711电子秤采集模块全套资料1.串口波特率19200;2.上电后直接串口打印称重数据;3.可以发指令校零传感器,读取称重数值;4.默认使用的是40Kg的传感器,其它传感器需要重新修正参数;5.有看门狗功能,减少程序异常;6.STM8F103驱动HX711包含原......
  • Linux多进程01-进程概述
    程序与进程程序是包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程进程是正在运行的程序的实例。是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。......
  • 图数据库 NebulaGraph 的内存管理实践之 Memory Tracker
    数据库的内存管理是数据库内核设计中的重要模块,内存的可度量、可管控是数据库稳定性的重要保障。同样的,内存管理对图数据库NebulaGraph也至关重要。图数据库的多度关联查询特性,往往使图数据库执行层对内存的需求量巨大。本文主要介绍NebulaGraphv3.4版本中引入的新特性Mem......
  • linux-ubuntu安装 lrzsz
    ubuntu安装lrzsz方法1:二进制包自动安装sudoapt-getinstalllrzsz方法2:源码包手动安装2.1下载cd/server/toolswgethttps://www.ohse.de/uwe/releases/lrzsz-0.12.20.tar.gztar-xzflrzsz-0.12.20.tar.gzcdlrzsz-0.12.20./configure--prefix=/usr/local/lrzszs......
  • Linux 内存管理 pt.3
    哈喽大家好,我是咸鱼在《Linux内存管理pt.2》中我们学习了多级页表和大页,我们知道了由于历史遗留的问题,Linux的页通常为4KB这样就会导致一个页表里面会有特别多页,为了解决这个问题,Linux提供了两种解决方案——多级页表和大页那么今天继续我们的Linux内存管理学习,我们今天......
  • linux-安装系统Ubuntu Server 14.04,16.04和17.10
    安装系统UbuntuServer14.04,16.04和17.10 本文以14.04位案例进行安装,其他版本相关问题会做注解1.选择要安装的系统语言本界面建议选择English,之后再选择中文安装对于14.x版本选择中文没有问题,但是对于16.04和17.10版本,该步骤选择中文后面会报错:2.选择安装过程中需要的语......
  • 在Linux/Windows/Mac上刷新DNS缓存的方法
    在Linux/Windows/Mac上刷新DNS缓存的方法刷新dns缓存让你可以得到新的域名解析。当你无法正确访问一个新注册的域名时就可以刷新dns缓存试试。但是不同的系统,Windows、MacOS和Linux上的方法是不一样的。1.Windows系统刷新DNS缓存开始-->运行-->输入cmd并回车在打开的命......