Files
2026-06-25 00:09:09 +08:00

5.8 KiB
Raw Permalink Blame History

实验九 内存与文件——内存映射文件

一、实验内容

理解内存映射文件机制,掌握 mmap/munmap/msync提高文件访问效率。

将文本文件映射内存,小写转大写,反序写入目标文件。

二、实验设计

2.1 内存映射文件原理

传统文件访问:                    内存映射文件:
应用程序                        应用程序
    ↓                              ↓
read() → 内核缓冲区 → 复制 → 用户缓冲区    mmap() → 直接访问
    ↓                              ↓
write() ← 内核缓冲区 ← 复制 ← 用户缓冲区    munmap() → 写回

2.2 函数设计

函数 功能
open() 打开源文件和目标文件
lseek() 获取文件长度
mmap() 将文件映射到内存
munmap() 解除映射
toupper() 字符转大写

2.3 调用关系

main()
├── open(argv[1]) 打开源文件
├── open(argv[2]) 打开目标文件
├── lseek(fd1, 0, SEEK_END) 获取长度
├── write(fd1, "\0", 1) 扩展文件
├── mmap() 映射源文件到内存
├── 遍历映射内存: 小写转大写
├── 从映射内存末尾向开始: 逐字节写入目标文件
├── munmap() 解除映射
└── close() 关闭文件

三、编码实现

3.1 打开文件并获取长度

int fd1 = open(argv[1], O_RDONLY);
int fd2 = open(argv[2], O_RDWR | O_CREAT, 0666);
int flength = lseek(fd1, 0, SEEK_END);

// 扩展目标文件以便写入
write(fd1, "\0", 1);  // 在源文件末尾添加结束符
lseek(fd1, 0, SEEK_SET);

要点:

  • 目标文件需要 O_RDWR | O_CREAT
  • 添加'\0'确保文件有足够空间

3.2 内存映射

char *mapped_mem = mmap(NULL, flength,
                         PROT_READ | PROT_WRITE,
                         MAP_PRIVATE, fd1, 0);

参数说明:

  • NULL: 让内核选择映射地址
  • flength: 映射长度
  • PROT_READ | PROT_WRITE: 可读可写
  • MAP_PRIVATE: 写时复制,不影响原文件

3.3 小写转大写

char *p = mapped_mem;
for (int i = 0; i <= flength; i++) {
    if (*p >= 'a' && *p <= 'z')
        *p = toupper(*p);
    p++;
}

要点: toupper() 将小写字母转为大写

3.4 反序写入目标文件

p = mapped_mem + flength;
for (int i = flength; i >= 0; i--) {
    write(fd2, p, 1);
    p--;
}

要点:

  • 从映射内存末尾向前遍历
  • 逐字节写入目标文件,实现内容反转

3.5 清理资源

munmap(mapped_mem, flength + 1);
close(fd1);
close(fd2);

3.6 编译与运行

g++ exp09_source.cpp -o mmapfile

# 创建测试文件
echo "Hello World" > source.txt

# 运行
./mmapfile source.txt target.txt

# 查看结果
cat target.txt  # 输出: DLROW OLLEH (反序且大写)

四、实验结果

4.0 测试命令

# 编译
g++ exp09_source.cpp -o mmapfile

# 创建测试文件
echo "hello world" > source.txt

# 运行(源文件小写转大写,目标文件反序)
./mmapfile source.txt target.txt

# 查看结果
cat source.txt   # 原文件(已转大写)
cat target.txt   # 反序文件

4.1 示例运行

$ echo "hello world" > source.txt
$ ./mmapfile source.txt target.txt
HELLO WORLD
$ cat target.txt
DLROW OLLEh

说明:

  • 源文件 "hello world\n" 经过内存映射后转为 "HELLO WORLD\n"
  • 目标文件为反序: "DLROW OLLEh\n"

4.2 内存映射优势

特性 传统read/write 内存映射
系统调用 多次read/write 一次mmap
数据复制 两次(磁盘→内核→用户) 零次(直接内存访问)
随机访问 lseek + read 指针直接访问
多进程共享 每进程独立缓冲区 映射到同一内存区域

4.3 处理流程图

source.txt 内容: "abc\n"

mmap 后 mapped_mem 指向文件内容: ['a','b','c','\n','\0']

遍历转换: ['A','B','C','\n','\0']

反序写入 target.txt:
1. write('A')
2. write('B')
3. write('C')
4. write('\n')
5. write('\0')

target.txt 内容: "CBA\n"

五、实验结果思考与体会

5.1 内存映射文件适用场景

场景 优势
大文件处理 无需整体加载,减少内存占用
随机访问 直接指针访问无需seek
多进程共享 多进程映射同一文件,共享数据
高效修改 就地修改,无需读写循环

5.2 思考问题解答

问题1用数组下标访问映射内存

char *mapped_mem = mmap(NULL, flength, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd1, 0);

for (int i = 0; i < flength; i++) {
    if (mapped_mem[i] >= 'a' && mapped_mem[i] <= 'z')
        mapped_mem[i] = toupper(mapped_mem[i]);
}

问题2内存映射文件优势场景

  1. 编译器/链接器:处理大型源文件,快速定位代码
  2. 数据库:映射数据库文件到内存,提高查询速度
  3. 图像处理:处理大型图片,就地修改像素
  4. 配置文件:映射配置文件,修改即时生效

5.3 实验体会

  1. mmap 核心思想:建立文件与内存地址空间的映射,直接用指针操作文件内容

  2. 与传统文件操作对比

    • 传统方式read → 用户缓冲区 → write
    • 内存映射:直接通过指针访问,零复制
  3. MAP_PRIVATE vs MAP_SHARED

    • PRIVATE写时复制不影响原文件
    • SHARED直接写回文件多进程共享
  4. munmap 后数据写回MAP_SHARED 的修改会在 munmap 时自动写回磁盘

  5. 反序写入原理:从 mapped_mem + flength 开始向前移动指针,逐字节写入目标文件,实现字符串反转

  6. 实际应用内存映射文件是操作系统优化IO的重要机制理解其原理有助于编写高效程序