南京大学OS笔记(1)-应用眼中的操作系统
南京大学OS笔记(1)-应用眼中的操作系统
早就想刷一刷南大JYY老师的os课。之前稍微看过几节,果然讲的风趣幽默,而且现场写代码展示水平确实很高,这次准备认真刷一刷然后好好记一下笔记。当然lab就不做了,因为已经做过mit的lab了。刷课主要是为了复习基础知识和学的更深入一点。第一节操作系统历史就不记笔记了,直接从第二节开始
1. 什么是应用程序
1. (应用)程序
这里学到和csapp里的一致的。下面看一系列代码演示
当我gcc -c a.c
的时候会生成一个a.o。注意-c表示编译。这里的a.o
是可重定位目标文件。而不是可执行目标文件。
而在执行gcc a.o
就会把它变成一个可执行目标文件。这里我在我自己的Linux机器上报错了。
总结一下,程序就是可执行的二进制文件,无论什么程序在Linux
系统下都是如此的。
2. ELF二进制文件
正因如此,我们可以用vim直接打开/bin/ls
这是一个二进制文件,所以这里直接显示乱码。
xxd
可以用来读二进制文件
这里的Entry point address
表示它第一条指令的起始地址。
3. 最简单的" Hello World"
1. 如果我们有一个下面这样的代码
int main () {
printf("Hello World\n");
}
可以直接运行吗。看起来没什么问题,但我们试试
-
这里会提示我们没有定义
puts
,明明是调用了printf
为什么会提示puts
这是因为printf在底层实际上调用了puts
这是因为gcc即便在没有设置优化的情况下。也就是
- o0
的情况下还是会做优化,把printf简化成了puts -
第二个问题是这里提示了warninng是我们没有制定代码的起始位置
2. 如果我们再尝试一次代码
int main() {
}
这是一个完全空的代码。但是它还是会报错
我们需要用gdb调试一下,看看到底为什么出错了
我们需要单步执行,执行到retrun这里。return指令就是调用main函数的地方。
因此在这里我们触发了段错误。这里我们不能访问地址为1的地方
3. 正确的尝试
这里jyy老师引入了一段汇编代码。让hello程序变得正确
这里单步执行到了系统调用
%eax 传递系统调用号
%rdi 传递第一个参数 ,以此类推
2. 应用程序怎么调用操作系统
1.首先看一下syscall的代码在哪里
objdump
指令解释
Displays information about one or more object files.
这里可以发现我们所有的系统调用都是callq syscall@plt
-动态链接来自于libc
的代码
2. Main()之前发生了什么?
(面试题)一个普通的C程序第一条指令子啊哪里?
- ❌ main的第一条指令
- ❌ libc的_start
可以用gdb调试一下会发现。它的第一条指令会在lib64/ld-linux-x86-64.so
这是操作系统自带的加载器
下面输入info inferiors
看一下有什么问题
- 我们发现我们现在运行的这个程序进程号是12305。
- 我们使用
pmap 12305
输出这个进程的信息。
会发现os已经帮我们做了很多事情所以整个过程是
os自带的加载器---> 加载libc------> 加载a.out
看下面这个程序。
虽然main是空的。,但是这里的
Hello World
Goodbye, Cruel OS World
还是可以正常输出
3. Trace的使用
使用strace可以追踪系统调用
这里跟随课上jyy老师的脚步分析一下a.out
的系统调用
- 可以发现第一条系统调用是
execve
- 然后libc执行了一堆系统调用
- 最后才会到我们自己写的程序
3. 应用眼中的操作系统
可以说所有的程序都是类似的,不断的调用系统调用。从开始到关闭
1. gcc的系统调用过程
-
这里的gcc确实是先利用as来做编译
-
然后用collect2来做链接
collect2主要用来做合成,会把构造器和析构器的代码生成出来
- 在gcc的最后会调用ld。