MIT6828 学习笔记 001 (lec 1,2)
1. 入门
看看 MIT 的计分标准: 70% 实验、 20% 提问实验内容、 10% 作业&讨论,太散漫辣!学生就应该每天坐在教室/自习室里学习来应付期末考试,缺课=作业都不是自己写的(数据库何蕾老师教我的),然后写论文时先不说内容,首先一定要引用大量文献(肥工毕业设计的老师教我的),这一点肥工做得就很好,赢。
示例代码都用 exit(0) 而不是 return 0; 不知道有什么原因,难道为了强调系统调用?
答:因为 Xv6 并不“标准”,一般 Linux 程序的起点不是
main而是像这样的:
1
2
3
4
5 void start(void) {
/* ... */
int r = main(argc, argv, envp);
exit(r);
}起初 Xv6 下程序的起点就是
main,然后return 0;时程序想pop发现没得pop就 segmentation fault 了。但在 commit 76ad8e974fefb4588b0a318f6502fde6bd730097 之后就不会 segfault 了,方法也差不多(git diff可以看到):
1
2
3
4
5
6 // user/ulib.c 添加了这个函数
void _main() {
extern int main();
main();
exit(0);
}
1
2 # Makefile ld -e 指定了 _main 而不是 main
$(LD) $(LDFLAGS) -N -e _main -Ttext 0 -o $@ $^
1.1. 软件调用了系统调用(如 open )时发生了什么
- 硬件保存一些用户的寄存器
- 硬件提升权限级别
- 硬件跳转到内核里一个已知的入口点
- 执行内核里的代码
- 内核调用这个系统调用的实现
sys_open()在文件系统中寻找这个文件名- 可能会 wait 磁盘
- 更新内核数据结构(文件块缓存, fd 表)
- 恢复用户的寄存器
- 降低权限级别
- 在用户代码系统调用处返回
大体的描述,后面会深入
1.2. Unix Shell
- 虽然现在 Unix 有 GUI 、 Web 服务器之类的来进行工作,但 Unix 最初专注于通过 shell 实现分时系统。
- shell 在接收指令后会
fork,exec,wait然后等待下一个指令,而在一个指令后面加上&表示 shell 在fork,exec后不用wait - 后面的 lab “copy-on-write” 会解决
fork复制,exec又覆盖看起来可能产生的浪费 - 程序只需要知道 fd
0(stdin), fd1(stdout) ,不用关心具体从哪里输入,往哪里输出, shell 会管理这些
1.3. 有趣的问题
不得不说这个思维真的让我挺震惊的,下面找几个我觉得精彩的,然而肥工老师的学生不需要这些花里胡哨,只要背作业答案过期末、引用各种国内牛人的文献写论文,肥又赢!
- 为什么有时这些 IO 是抽象的、而有时又不是
- 为什么内核规范了文件系统,而不允许程序自行定义磁盘的使用方式
- 为什么文件是字节流,而不是磁盘块或格式化的记录
2. Xv6 的 C 语言
2.1. Xv6 C 程序的内存分布
- text: 代码、只读数据
- data: 全局变量
- stack: 函数的局部变量
- heap: 用
sbrk,malloc/free动态分配的内存
局部变量没有默认值,
clang-tidy也会提醒
2.2. 编译
Makefile 定义编译过程、 gcc 把代码编译成 .o 、 ld 把 .o 文件链接成为一个可执行文件。
ulibc.o是 Xv6 的最小 C 库。
可执行文件的格式分为几个部分,包括:文本(代码)、初始化了的数据、符号表、调试信息等等。
2.3. 反编译
1 | objdump -S a.out |
目前没弄明白怎么在 Xv6 下编译代码,所以上面的是我的 Linux 上的反编译方法
答:在我的物理机器 (X86-64 Linux) 上交叉编译出 RISC-V 的程序 (RISC-V Linux) ,放进 Xv6 里运行
2.4. 查看符号表
1 | nm -n a.out |
这也是我的 Linux 上查看符号的方法,
-n表示按数字顺序排序
2.5. C
static使一个变量只能在所在文件里可视,但在这个文件里它是全局变量