【Linux系统编程】25.mmap、munmap

本文详细介绍了Linux系统中的mmap函数用法,包括参数解释、映射过程、映射区的使用注意事项,以及munmap用于释放映射区。同时展示了如何通过mmap实现父子进程间的内存共享和匿名映射通信示例。
摘要由CSDN通过智能技术生成

目录

mmap

参数addr

参数length

参数prot

参数flags

参数fd

参数offset

返回值

保险使用方式

munmap

参数addr

参数length

返回值

测试代码1

测试结果

使用注意事项

测试代码2

测试结果

测试代码3

测试结果

匿名映射

测试代码4

测试结果

mmap

将一个指定文件映射到存储区域中。

man 2 mmap

参数addr

指定映射区的首地址。通常传NULL,让系统自动分配。

参数length

共享内存映射区的大小,小于等于文件的实际大小。

参数prot

共享内存映射区的读写属性,映射区权限。

可传参数:

  • PROT_READ

  • PROT_WRITE

  • PROT_READ|PROT_WRITE

参数flags

标注共享内存的共享属性,标志位参数,常用于设定更新物理区域、设置共享、创建匿名映射区。

可传参数:

  • MAP_SHARED:会将映射区所做的操作反映到物理设备(磁盘)上。

  • MAP_PRIVATE:映射区所做的修改不会反映到物理设备。

参数fd

用于创建共享内容映射区的文件的文件描述符。

参数offset

偏移位置,必须是4k的整数倍

0:默认值,表示映射文件全部。

返回值

成功:映射区的首地址。

失败:MAP_FAILED。

保险使用方式

fd=open("文件名",O_RDWR);
data=mmap(NULL,有效文件大小,PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

munmap

释放映射区。

参数addr

映射区首地址。

参数length

映射区大小。

返回值

成功:0

失败:-1

测试代码1

建立文件映射,读取并写入其他内容。

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

int main(int argc, char *argv[])
{
    int fd, flag;
    char *data = NULL;
    char data1[1024];
    int leng;
    printf("程序开始运行。\n");

    printf("开始打开文件。\n");
    fd = open("temp.txt", O_RDWR);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");

    printf("开始文件映射。\n");
    leng = lseek(fd, 0, SEEK_END);
    data = mmap(NULL, leng, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
    if (data == MAP_FAILED)
    {
        perror("文件映射错误");
        exit(1);
    }
    printf("文件映射完成。\n");
    printf("地址是%d。\n", *data);
    printf("内容是:%s", data);

    printf("开始写入文件。\n");
    printf("写入的内容是:hello,world.\n");
    strcpy(data, "hello,world.\n");
    printf("写入文件完成。\n");

    close(fd);
    return 0;
}

测试结果

映射多大的内存空间,只能写入多大的内存空间。映射后的返回值是文件里的内容。

 

使用注意事项

  1. 用于创建映射区的文件大小为0时,实际指定非0大小创建映射区,报“总线错误”。

  2. 用于创建映射区的文件大小为0时,实际指定0大小创建映射区,报“无效参数”。

  3. 用于创建映射区的文件读写属性为只读时,映射区属性为读、写,报“无效参数”。

  4. 创建映射区需要read权限,当访问权限指定为“共享/MAP_SHARED”时,mmap的读写权限应该小于等于文件的open权限。

  5. 文件描述符fd,在mmap创建映射区完成后即可关闭。后续可以使用地址访问文件。

  6. offset必须是4096的整数倍,因为MMU映射的最小单位为4k。

  7. 对申请的映射区内存,不能越界访问。

  8. munmap用于释放地址,必须是mmap申请返回的地址。

  9. 映射区访问权限为“私有/MAP_PRIVATE”时,对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。

  10. 映射区访问权限为“私有/MAP_PRIVATE”时,只需要open文件时,有读权限用于创建映射区即可。

测试代码2

父子进程间通过映射区进行通信。

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

int main(int argc, char *argv[])
{
    int fd, flag;
    int *di_zhi;
    pid_t JinCheng_ID;
    int temp = 100;
    printf("程序开始运行。\n");

    printf("开始打开文件。\n");
    fd = open("temp2.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");

    printf("开始文件拓展。\n");
    ftruncate(fd, 10);
    printf("文件拓展完成。\n");

    printf("开始文件映射。\n");
    di_zhi = (int *)mmap(NULL, 10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
    if (di_zhi == MAP_FAILED)
    {
        perror("文件映射错误");
        exit(1);
    }
    close(fd);
    printf("文件映射完成。\n");

    printf("开始创建进程。\n");
    JinCheng_ID = fork();
    if (JinCheng_ID == 0) //子进程
    {
        *di_zhi = 1000;
        temp = 200;
        printf("这是子进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
    }
    else if (JinCheng_ID > 0) //父进程
    {
        sleep(1);
        printf("这是父进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
        wait(NULL);
    }

    printf("开始释放映射区。\n");
    flag = munmap(di_zhi, 10);
    printf("释放映射区完成。\n");

    return 0;
}

测试结果

测试代码3

两个毫无关系的进程通过映射区进行通信。

/*
CeShi3_1.c
发送数据的进程
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

struct XueSheng
{
    int id;
    char ming_zi[256];
    int nian_ling;
};

int main(int argc, char *argv[])
{
    int fd;
    struct XueSheng xue_sheng = {1, "张三", 20};
    struct XueSheng *di_zhi;

    printf("程序开始运行。\n");

    printf("开始打开文件。\n");
    fd = open("temp3.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");

    printf("开始文件拓展。\n");
    ftruncate(fd, sizeof(xue_sheng));
    printf("文件拓展完成。\n");

    printf("开始文件映射。\n");
    di_zhi = mmap(NULL, sizeof(xue_sheng), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
    if (di_zhi == MAP_FAILED)
    {
        perror("文件映射错误");
        exit(1);
    }
    close(fd);
    printf("文件映射完成。\n");

    printf("开始向映射区发送数据。\n");
    while(1){
        memcpy(di_zhi,&xue_sheng,sizeof(xue_sheng));
        printf("发送的数据:id=%d,ming_zi=%s,nian_ling=%d。\n", di_zhi->id, di_zhi->ming_zi, di_zhi->nian_ling);
        xue_sheng.id++;
        sleep(1);
    }

    printf("开始释放映射区。\n");
    munmap(di_zhi, sizeof(xue_sheng));
    printf("释放映射区完成。\n");

    return 0;
}
/*
CeShi3_2.c
接收数据的进程
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

struct XueSheng
{
    int id;
    char ming_zi[256];
    int nian_ling;
};

int main(int argc, char *argv[])
{
    int fd;
    struct XueSheng xue_sheng;
    struct XueSheng *di_zhi;

    printf("程序开始运行。\n");

    printf("开始打开文件。\n");
    fd = open("temp3.txt", O_RDWR);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");



    printf("开始文件映射。\n");
    di_zhi = mmap(NULL, sizeof(xue_sheng), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //全部映射
    if (di_zhi == MAP_FAILED)
    {
        perror("文件映射错误");
        exit(1);
    }
    close(fd);
    printf("文件映射完成。\n");

    printf("开始向映射区接收数据。\n");
    while (1)
    {
        printf("接收的的数据:id=%d,ming_zi=%s,nian_ling=%d。\n", di_zhi->id, di_zhi->ming_zi, di_zhi->nian_ling);
        sleep(1);
    }

    printf("开始释放映射区。\n");
    munmap(di_zhi, sizeof(xue_sheng));
    printf("释放映射区完成。\n");

    return 0;
}

测试结果

匿名映射

只能用于血缘关系进程间的通信。

测试代码4

父子进程间使用匿名映射通信。

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

int main(int argc, char *argv[])
{
    int flag;
    int *di_zhi;
    pid_t JinCheng_ID;
    int temp = 100;
    printf("程序开始运行。\n");

    printf("开始文件映射。\n");
    di_zhi = (int *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); //全部映射
    if (di_zhi == MAP_FAILED)
    {
        perror("文件映射错误");
        exit(1);
    }
    printf("文件映射完成。\n");

    printf("开始创建进程。\n");
    JinCheng_ID = fork();
    if (JinCheng_ID == 0) //子进程
    {
        *di_zhi = 1000;
        temp = 200;
        printf("这是子进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
    }
    else if (JinCheng_ID > 0) //父进程
    {
        sleep(1);
        printf("这是父进程,*di_zhi=%d,temp=%d。\n", *di_zhi, temp);
        wait(NULL);
    }

    printf("开始释放映射区。\n");
    flag = munmap(di_zhi, 10);
    printf("释放映射区完成。\n");

    return 0;
}

测试结果

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

因心,三人水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值