应用视角的操作系统
本讲内容:指令序列和高级语言的状态机模型;回答以下问题:
- 什么是软件 (程序)?
- 如何在操作系统上构造最小/一般/图形界面应用程序?
- 什么是编译器?编译器把一段程序翻译成什么样的指令序列才算 “正确”?
构建一个最小的程序
1 |
|
gcc 编译出来的文件一点也不小
- objdump 工具可以查看对应的汇编代码
- –verbose 可以查看所有编译选项 (真不少)
- printf 变成了 puts@plt
- -Wl,–verbose 可以查看所有链接选项 (真不少)
原来链接了那么多东西
还解释了 end 符号的由来 - -static 会链接 libc (大量的代码)
gcc a.c 和 gcc a.c -static 有什么区别?
强行构造最小的 Hello, World?
1 |
|
1 |
|
执行a.out,会发生Segmentation Fault.
如果改为
1 |
|
再继续执行上面的编译命令,发现程序可以正常执行了(死循环)。
查看汇编语言,猜测程序返回时的 ret 出现了错误。
解决异常退出
解决办法:用一条特殊的指令请操作系统帮忙
1 |
|
- 把 “系统调用” 的参数放到寄存器中
- 执行 syscall,操作系统接管程序
- 程序把控制权完全交给操作系统
- 操作系统可以改变程序状态甚至终止程序
理解高级编程语言程序
编程语言也是一个状态机
- 非递归的汉诺塔什么叫函数调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26typedef struct {
int pc, n;
char from, to, via;
} Frame;
#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; })
#define ret() ({ top--; })
#define goto(loc) ({ f->pc = (loc) - 1; })
void hanoi(int n, char from, char to, char via) {
Frame stk[64], *top = stk - 1;
call(n, from, to, via);
for (Frame *f; (f = top) >= stk; f->pc++) {
n = f->n; from = f->from; to = f->to; via = f->via;
switch (f->pc) {
case 0: if (n == 1) { printf("%c -> %c\n", from, to); goto(4); }
// 为什么goto(4)只是设置pc为3,是因为循环会++
break;
case 1: call(n - 1, from, via, to); break;
case 2: call( 1, from, to, via); break;
case 3: call(n - 1, via, to, from); break;
case 4: ret(); break;
default: assert(0);
}
}
}函数是由很多个栈帧组成的,每一个栈帧都有一个PC
什么是函数调用?
函数调用就是在栈帧的顶部再加上一个栈帧,这个栈帧的PC是0,然后把参数放到栈上
什么是函数返回
把顶部的栈给抹掉
什么是执行一条语句
取最顶上栈帧PC上的语句执行
- 代码讲解
这是递归版的汉诺塔1
2
3
4
5
6
7
8
9
10void hanoi(int n, char from, char to, char via) {
if (n == 1) {
printf("%c -> %c\n", from, to);
} else {
hanoi(n - 1, from, via, to);
hanoi(1, from, to, via);
hanoi(n - 1, via, to, from);
}
// return 省略了
}根据上面的思想,把整个hanoi函数理解成一个栈帧,每一个栈帧有一个PC,还需要参数信息
可以理解成函数体每一条语句都是一条PC指针
每一次循环,都会取最顶上的栈帧来操作
这也就是下部分的逻辑。
1 |
|
操作系统上的软件 (应用程序)
任何程序 = minimal.S = 调用 syscall 的状态机
可执行文件是操作系统中的对象
与大家日常使用的文件 (a.c, README.txt) 没有本质区别
操作系统提供 API 打开、读取、改写 (都需要相应的权限)
查看可执行文件
- vim, cat, xxd 都可以直接 “查看” 可执行文件
- vim 中二进制的部分无法 “阅读”,但可以看到字符串常量
- 使用 xxd 可以看到文件以 “\x7f” “ELF” 开头
- Vscode 有 binary editor 插件
在 Vim 中输入 %!xxd 命令会将当前编辑的文件转换成十六进制表示,并在 Vim 中显示。这个命令的作用是将当前编辑的文件作为输入传递给 xxd 命令(%表示对整个文件执行操作),xxd 命令会将其转换成十六进制格式,并将结果输出到标准输出流,此时 Vim 会将其读取并显示在编辑器中。
动手实验:观察程序的执行
工具程序代表:编译器 (gcc)
- 主要的系统调用:execve, read, write
- strace -f gcc a.c (gcc 会启动其他进程, -f选项会追踪所有子进程)
- 可以管道给编辑器 vim -
- 编辑器里还可以 %!grep (细节/技巧)
grep 命令的 -e 选项用于指定一个或多个匹配模式,这些模式可以是正则表达式或普通字符串。
1 |
|
图形界面程序代表:编辑器 (xedit)
- 主要的系统调用:poll, recvmsg, writev
- strace xedit
- 图形界面程序和 X-Window 服务器按照 X11 协议通信
- 虚拟机中的 xedit 将 X11 命令通过 ssh (X11 forwarding) 转发到 Host
应用视角的操作系统
http://example.com/2023/08/02/操作系统/jyy操作系统/应用视角的操作系统/