Linux系统编程之内存映射

概述

内存映射是操作系统提供的一种机制,使得文件或设备的内容可以直接映射到进程的虚拟地址空间中。这意味着,我们可以像访问数组一样读写文件内容,而不需要显式地调用I/O函数进行数据传输。内存映射适用于多种应用场景,包括但不限于:大文件处理、数据库和缓存、进程间通信、内存映射文件等。

内存映射的优势主要体现在如下三个方面。

1、提高性能:减少了从磁盘到用户空间的数据复制过程。

2、简化编程模型:可以通过指针直接访问文件内容,就像操作普通内存一样。

3、支持共享内存:多个进程可以共享同一块内存映射区域,实现高效的进程间通信。

mmap和munmap

mmap函数用于创建一个新的内存映射或扩展现有的映射,以将文件或设备映射到内存中。通过mmap,我们可以直接访问文件内容,就像操作内存一样简单和高效。这种机制特别适用于需要处理大文件、实现进程间通信、或需要频繁读写特定文件部分的应用场景。其函数原型如下。

cpp 复制代码
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

各个参数和返回值的含义如下。

addr:建议的映射起始地址,通常设置为NULL,让操作系统自动选择合适的地址。

length:映射区域的长度,以字节为单位。

prot:内存保护标志,指定映射区域的访问权限,取值如下。

(1)PROT_READ:映射区域可以被读取。

(2)PROT_WRITE:映射区域可以被写入。

(3)PROT_EXEC:映射区域可以被执行。

(4)PROT_NONE:映射区域不可访问。

flags:指定映射类型的标志,取值如下。

(1)MAP_SHARED:变更会被共享给其他进程,并且会同步更新到文件中。

(2)MAP_PRIVATE:创建一个私有副本,变更仅对当前进程可见,不会同步回文件。

(3)MAP_ANONYMOUS:映射匿名内存,不关联任何文件描述符。

fd:文件描述符。如果使用了MAP_ANONYMOUS标志,则此参数应设为-1。

offset:文件中的偏移量,指定了映射区域在文件中的起始位置。

返回值:成功时返回指向映射区域的指针,失败时返回MAP_FAILED,并设置errno为具体的错误原因。

munmap函数用于解除之前通过mmap建立的内存映射。它会将指定的地址范围从调用进程的地址空间中删除,并释放相应的资源。其函数原型如下。

cpp 复制代码
int munmap(void *addr, size_t length);

各个参数和返回值的含义如下。

addr:要解除映射的内存区域的起始地址。

length:内存区域的长度。

返回值:成功时返回0,失败时返回-1,并设置errno为具体的错误原因。

实战代码

下面的实战代码使用内存映射技术来读取并打印一个文件的内容。

首先,我们通过调用open函数以只读模式打开名为"hope_wisdom.txt"的文件。接着,使用fstat函数获取文件的统计信息,包括文件大小。然后,通过mmap函数将整个文件内容映射到进程的地址空间中,映射区域设置为只读且私有。这就意味着,对映射内容的任何修改都不会反映到原文件上。

成功映射后,我们直接将映射区域的内容作为字符串打印出来。最后,通过munmap函数解除了文件内容的内存映射,并关闭了文件描述符。

cpp 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    int fd = open("hope_wisdom.txt", O_RDONLY);
    if (fd == -1)
    {
        printf("open failed\n");
        return 1;
    }

    // 获取文件大小
    struct stat sb;
    if (fstat(fd, &sb) == -1)
    {
        printf("fstat failed\n");
        close(fd);
        return 1;
    }
    
    // 映射文件
    off_t length = sb.st_size;
    void *pAddr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
    if (pAddr == MAP_FAILED)
    {
        printf("mmap failed\n");
        close(fd);
        return 1;
    }

    // 打印内容
    printf("%s", (char *)pAddr);

    // 解除映射
    munmap(pAddr, length);
    close(fd);
    return 0;
}
相关推荐
groundhappy3 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕3 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫5 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生5 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.116 小时前
linux基础IO
linux·运维·服务器
BelongPanda8 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯8 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_777998349 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器
yunwei379 小时前
eBPF 教程:检查 exec 后真正安装的可执行镜像
linux·开源
笑锝没心没肺9 小时前
yum update 报错:GPG key retrieval failed: [Errno 14] HTTPS Error 404 - Not Found
linux·运维·服务器