当前位置: 首页 > news >正文

[CSAPP] 9.8 内存映射 | 虚拟内存 | 页表 | 物理内存 | 写时拷贝机制

目录

进程地址空间的管理

虚拟地址与父子进程

Linux下的进程地址空间管理

原书笔记存档

习题

mmapcopy.c


去年暑假的笔记可以看这里,里面有进行过详细的代码验证【Linux详解】进程地址空间,习题答案可以直接看文末


进程地址空间的管理

当我们讨论进程地址空间时,一个关键点在于我们操作的并不是真实的物理地址,而是虚拟地址。

那么,如何在使用虚拟地址的同时能够对物理内存进行数据存储呢?

  • 答案是操作系统中存在一种叫做 页表 的机制。页表维护了虚拟地址与物理地址之间的映射关系。
  • 当进程试图通过虚拟地址在自己的地址空间中查找数据时,实际上是拿着这个虚拟地址去查询页表,找到对应的物理地址,从而实现对实际数据的访问。

虚拟地址与父子进程

在探讨虚拟地址时,有一个问题浮现出来:为什么同一个虚拟地址在父进程和子进程中可以存储不同的数据?

当创建子进程时,它会继承父进程中的大量数据,包括页表。

因此,在初始状态下,子进程的页表包含了与父进程相同的虚拟地址到物理地址的映射关系。

例如,如果父进程和子进程都对变量val进行了定义,它们在开始时对val的虚拟地址是一样的。

  • 但是,当子进程尝试修改val(比如将val设为5)时,写时拷贝(Copy-On-Write, COW)机制就会被触发。
  • 此时,系统会把原先父子进程共享的val值复制一份给子进程专用,并允许子进程单独对其进行修改。

  • 这一过程中,val的虚拟地址保持不变,但其对应的物理地址发生了变化。
  • 也就是说,虽然父子进程对于val的虚拟地址相同,但由于它们的页表不同,导致最终访问到的物理地址不同

这就是为何我们可以看到同一虚拟地址对应两个不同值的现象。

Linux下的进程地址空间管理

Linux操作系统采用了一套复杂的机制来管理系统中的进程地址空间。

其中,页表 扮演了至关重要的角色,不仅解决了虚拟地址到物理地址的转换问题,还通过如写时拷贝等机制优化了内存使用效率,使得多个进程(特别是父子进程之间)能够高效、安全地共享和独立使用内存资源。

  • 这个地方涉及到了一个 可以中间再加一层的 映射转化 思想
  • 就是例如,我们有很多的数据要对其进行存储,我们可以通过其进行标识,然后对其的标识位进行管理
  • 要用的时候再通过标识去找到该数据,这样就不用统一管理非常多/大的数据了
  • (类似于昨天protobuf对于传输效率提升,所使用到的Varint编码用到的MSB最高位 标记 机制)

原书笔记存档

习题

mmapcopy.c

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

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "使用方法: %s <文件名>\n", argv[0]);
        exit(1);
    }

    // 打开输入文件
    int fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("无法打开文件");
        exit(1);
    }

    // 获取文件大小
    struct stat sb;
    if (fstat(fd, &sb) == -1) {
        perror("无法获取文件状态");
        close(fd);
        exit(1);
    }

    // 如果文件大小为0,直接退出
    if (sb.st_size == 0) {
        printf("文件为空\n");
        close(fd);
        exit(0);
    }

    // 映射文件到内存
    char *mapped_file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped_file == MAP_FAILED) {
        perror("mmap失败");
        close(fd);
        exit(1);
    }

    // 将映射的内容写入stdout
    size_t bytes_written = write(STDOUT_FILENO, mapped_file, sb.st_size);
    if (bytes_written != sb.st_size) {
        perror("写入失败");
        munmap(mapped_file, sb.st_size);
        close(fd);
        exit(1);
    }

    // 清理
    if (munmap(mapped_file, sb.st_size) == -1) {
        perror("munmap失败");
        close(fd);
        exit(1);
    }

    close(fd);
    return 0;
} 

测试

我们可以用 man 再来回看一下这些接口,就会感觉到很清晰了

相关文章:

  • 使用 Frida Stalker 反 OLLVM 算法还原
  • 代码随想录算法训练营第三十二天 | 509.斐波那契数 70.爬楼梯 746.使用最小花费爬楼梯
  • 基于YOLO11的违禁物品检测分析系统
  • 【sgThumbPreviewTip】自定义组件:缩略图预览组件,移入缩略图等待1秒后出现浮动气泡框显示更大的缩略图或预览播放视频
  • HTTP协议手写服务器
  • 设计模式之适配器模式(二):STL适配器
  • RISC-V AIA学习3---APLIC 第二部分(APLIC 中断域的内存映射控制区域)
  • 第五章.图论
  • iOS GCD
  • C++ STL常用算法之常用排序算法
  • Vue3+Vite获取原始文版并展示在页面内
  • 030-gprof
  • 群体智能优化算法-算术优化算法(Arithmetic Optimization Algorithm, AOA,含Matlab源代码)
  • 清晰易懂的Trae实现为AI编程从安装到实战开发ToDoList
  • Redis:持久化 RDB快照 AOF日志
  • leetcode每日一题:使所有字符相等的最小成本
  • Java面试黄金宝典24
  • 软考《信息系统运行管理员》- 6.2 信息系统硬件的安全运维
  • Oracle数据库数据编程SQL<2.1 DDL、DCL表、列及约束>
  • 数据仓库 - 转转 - 一面凉经
  • 做报名链接的网站/郑州seo联系搜点网络效果好
  • 怎样做网站平台赚钱/网络营销品牌案例
  • 武汉网架加工厂/seo公司重庆
  • 深圳网站建设制作订做/制作网站要花多少钱
  • 汕头市作风建设的网站/百度浏览器在线打开
  • 个人网站的搭建/我赢seo