程序 = 状态机
进程 (状态机) 管理的 API
学习虚拟存储是如何实现的
进程作为状态机,$(M, R)$ 中的 $M$ 到底是什么?
个 void *p
可以指向任意整数
NULL
; 导致 segmentation fault)main
, %rip
会从此处取出待执行的指令)static int x
)int y
)它们停留在概念中,但实际呢?
pmap (1) - report memory of a process:
演示:调试一个最小的程序 (静态/动态链接)
思考题
pmap 是基于 procfs 中的 maps 实现的
$ cat /proc/[pid]/maps
00400000-004b6000 r-xp /tmp/a.out (代码)
006b6000-006bc000 rw-p /tmp/a.out (数据)
006bc000-006bd000 rw-p (bss)
0131f000-01342000 rw-p [heap]
7fff993c9000-7fff993ea000 rw-p [stack]
7fff993f4000-7fff993f7000 r--p [vvar] <- WTF?
7fff993f7000-7fff993f9000 r-xp [vdso] <- WTF?
ffffffffff600000-ffffffffff601000 r-xp [vsyscall] <- WTF? 这地址?
试试:是不是 bss? bss.c
vdso (7): Virtual system calls: 只读的系统调用也许可以不陷入内核执行。
vsyscall 的例子
让内核和进程共享数据 (内核可写,进程只读)
ffffffffff600000
这么诡异的地址)计算机系统里没有魔法!
“执行系统调用时,进程陷入内核态执行”——不,不是的。
系统调用就是一组接口的约定,谁说一定要 int
指令?
SYSCALL — Fast System Call
RCX <- RIP; (* 下条指令执行的地址 *)
RIP <- IA32_LSTAR;
R11 <- RFLAGS;
RFLAGS <- RFLAGS & ~(IA32_FMASK);
CPL <- 0; (* 进入 Ring 0 执行 *)
CS.Selector <- IA32_STAR[47:32] & 0xFFFC
SS.Selector <- IA32_STAR[47:32] + 8;
能不能让其他系统调用也不要陷入内核?
使用共享内存和内核通信!
进程只有少量内存映射
地址空间里剩下的部分是怎么创建的?
进程的地址空间 = 内存里若干连续的 “段”
管理进程地址空间的系统调用
// 映射
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
// 修改映射权限
int mprotect(void *addr, size_t length, int prot);
它们的确好像没有什么区别
实现 “游戏外挂程序” (mem-probe.c)
/proc/[pid]/mem
访问,原理类似实现函数 $f: [0,M) \mapsto [0, M)$
我们需要在硬件上实现数据结构 $f$
把地址空间切成大小是 $p$ “页面”
映射是页面到页面的,也就意味着
“什么也不做,只是标记一下”。
mmap 并不需要真的为进程分配任何页面
缺页时操作系统会得到缺页的地址 (%cr2
)
Example 1:
Example 2:
但我们好像带来了一些问题……
请查阅手册,看看操作系统是如何规定这些操作的行为的
msync (2)
本次课内容与目标
Takeaway messages