操作系统-第六讲-进程的地址空间

进程的地址空间

jyy 老师的讲义


内容回顾

在操作系统的视角,进程就是运行的状态机,可以通过系统调用来复制、重置和销毁状态机。状态机有自己的地址空间,但是进程的地址空间从哪里来呢?

进程的初始状态

进程 execve 后的初始状态需要满足 ABI 中规定的 initial state (System V ABI),里面只规定了部分寄存器和栈(argvenvp 中的字符串保存在栈中)。

进程的地址空间管理

进程的地址空间: 配合 gdb 调试器,我们甚至可以 "随时查看程序的地址空间"。这一切代码都是人工智能生成的!因为 AI 知晓几乎所有的细节,它是帮助我们理解复杂细节的利器。

Memory Map 一系列系统调用可以改变进程的地址空间。

C memory_mapping.h
1// 映射
2void *mmap(void *addr, size_t length, int prot, int flags,
3 int fd, off_t offset);
4int munmap(void *addr, size_t length);
5
6// 修改映射权限
7int mprotect(void *addr, size_t length, int prot);

例如,调用 mmap 分配 1GiB 内存前后的进程地址空间如下图:

mmap前 - 初始状态
0xFFFFFFFF
FF601000
[vsyscall]
系统调用页面
0x7FFFFFFF
FFF00000
[stack]
用户栈
~140KB
0x7FFFF7FF
D000
大量空白区域
可用于mmap分配

[vdso] 虚拟DSO
[vvar] 虚拟变量
0x4F2000
[heap]
堆区域
~152KB
0x4CB000
.data/.bss
全局变量
~12KB
0x4C8000
.rodata
只读数据
~164KB
0x49A000
.text
代码段
~612KB
0x401000
ELF头
程序头表
4KB
0x400000
程序入口
mmap后 - 分配1GiB
0xFFFFFFFF
FF601000
[vsyscall]
系统调用页面
0x7FFFFFFF
FFF00000
[stack]
用户栈
~140KB
0x7F000000
mmap区域
1 GiB
物理内存未使用
0x6F000000
剩余空白区域
[vdso] 虚拟DSO
[vvar] 虚拟变量
0x4F2000
[heap]
堆区域
~152KB
0x4CB000
.data/.bss
全局变量
~12KB
0x4C8000
.rodata
只读数据
~164KB
0x49A000
.text
代码段
~612KB
0x401000
ELF头
程序头表
4KB
0x400000
程序入口
栈区域
mmap区域
堆区域
数据段
代码段
空白区域

mmap 前栈和堆之间有大量空白区域可供分配,mmap 在空白区域中分配了 1GiB 的虚拟地址空间。mmap 只是虚拟分配,实际物理内存在写入时才分配,当进程访问 mmap 区域时触发缺页异常,然后分配物理页面。

使用 mmap 系统调用可以申请大量内存,当 malloc 申请较大内存时,会调用一次 mmap 实现。或者将一个大文件直接搬到进程的地址空间中。

Python mmap_example.py
1with open('/dev/sda', 'rb') as fp:
2 mm = mmap.mmap(fp.fileno(),
3 prot=mmap.PROT_READ, length=128 << 30)
4 hexdump.hexdump(mm[:512])

入侵进程的地址空间

进程对应的状态机在”无情执行指令机器”上执行,状态机是一个封闭的世界,但是有些系统调用允许一个进程对其他进程的地址空间有访问权,可以任意改变另一个程序的行为。

gdb 可以任意观测和修改程序的状态。

但是并不是任意一个进程都可以访问和修改另一个进程的地址空间,如果进程 A 能访问和修改进程 B 的地址空间,需满足:

  1. A 是 root 用户
  2. A 和 B 是同用户且 ptrace_scope = 0
  3. A 和 B 是父子进程且 ptrace_scope <= 1
  4. A 具有 CAP_SYS_PTRACE 能力

其中,ptrace_scope 是 Linux 内核中 Yama 安全模块提供的一个安全特性,用来限制 ptrace 系统调用的使用,防止进程被恶意调试和内存访问。

金山游侠:通过操作系统赋予我们实现调试器的机制,我们可以窥探甚至修改任何进程的代码和数据。这个能力使我们可以绕过游戏为我们设置的 "人为障碍",取得更多的金钱、经验,或是锁定生命值等。

总结

Take-away Messages 操作系统通过虚拟内存为每个进程提供独立的地址空间,实现了进程间的隔离和保护。操作系统通过 mmap/munmap 实现地址空间的管理,并且还提供特定机制 (如 procfsptraceprocess_vm_writev、共享内存等) 访问彼此的地址空间。