操作系统学习历程
操作系统概述
什么是操作系统
计算机硬件:极简的公理系统(导线,逻辑门,时钟,触发器)就能够支持非常复杂的数字系统设计
硬件和软件的中间层,需要了解全部但不要细究全部
- 对单机作出抽象
- 支撑多个程序执行
从应用角度看操作系统


可以对如图所示的文件进行gcc的编译得到想要的exe可执行文件,也可以使用gcc -c来先进行编译得到.o文件在进行链接

在计算机中,我们所有的指令都是计算和move指令,并没有关闭程序和关闭计算机的指令,那么这个过程是谁在发挥作用?
答案是操作系统。
借助操作系统来实现
1 | movq $SYS_exit , %rax #exit( |
- 将系统调用的参数放到寄存器中
- 执行sys call,操作系统接管程序,操作系统可以任意改变程序状态(甚至终止程序)
应用程序=计算+操作系统API
举例说明:
窗口管理器:能直接管理屏幕设备
能够和其他进程通信
任务管理器:能访问操作系统提供的进程对象
杀毒软件:文件静态扫描(read),主动防御(ptrace)
操作系统的职责:提供令应用程序舒适的抽象(对象+API)
理解高级语言程序
C语言代码经过编译之后得到二进制文件,执行二进制文件就可以依次执行指令,每次执行一条“语句”
在使用gdb调试代码的时候可以发现,c语言文件的执行也是一种状态机,那我们可以试着使用c语言的源代码来模拟状态机的运行,也就是将代码写成“simpleC”
状态机是拥有严格数学定义的对象,这意味着可以用定义的方式写出来
- 状态就是各种栈帧和全局变量的组合。
- 初始状态下仅有一个栈帧(main函数栈帧),且全局变量均为初始值(PC=0)
- 状态迁移:执行栈帧PC
试图把c代码改写成simpleC:
- 每一条语句至多一次运算
- 条件语句中不包含运算
编译器与编译优化
- 编译器的输入:高级c语言代码
- 编译器的输出:汇编代码(指令序列)
- 编译器就是状态机之间的翻译器
simpleC翻译:
- 运算:把操作数load到寄存器,执行运算,store写回结果
- 分支/循环:使用条件跳转分别执行代码
- 函数调用:专门留一个寄存器给栈(SP,stackPointer),将stackframe的信息保存到内存中
编译优化三板斧:
- 函数内联: 将函数调用替换为函数体本身的内容
- 常量传播:在编译时计算常量表达式的值并替换
- 死代码消除:删除永远不会执行到的代码
那么给定两个程序A和B,编译器到底允不允许把A编译成B?
- 首先要看在syscall调用后两个程序的输出序列是否一致,如果一致则可以编译。
- 系统调用是使程序计算结果可见的唯一方法,不改变语义=不改变可见结果,即如果两个状态机生成的所有syscall序列是完全一致的,则该优化就是允许的。
C语言代码中不可优化的部分:
External function calls外部函数调用(链接时才能确定到底是什么代码)
- 未知的代码可能包含系统调用
- 因此不可删除,移除循环等,且要保证参数传递完全一致
编译器提供的不可优化标注
从硬件视角理解操作系统
计算机系统的状态机模型
- 状态:内存,寄存器的数值
寄存器,内存
1 | struct CPUstate{ |
还有外部世界的态:
- 设备上的寄存器
- 中断指令Interrput
- 客观存在但计算机系统不能直接访问,进程只能通过syscall访问外部输入
- 初始状态:由系统设计者规定(CPU Reset)
- 状态迁移:从PC取指令执行
计算机系统中的固件
reset是计算机硬件和系统程序员的第一个接口,如果想要在裸机上面进行编程,只需要做一个电路并在适当的内存放上适当的代码,只要cpu在reset状态,就执行相应的某些代码,此时就可以对cpu进行控制。
什么是固件?
固件就是厂商在出厂时放置到计算机系统里的代码
- 之所以称之为固件是因为早期时rom,如果想要更新升级则需要换芯片
固件的功能:
- 运行程序前的计算机系统配置
- CPU电压,内存时序,接口开关等
不严格的说,加载操作系统
- 加载存储设备上的引导程序
可以成为一个小的操作系统,在CPU reset后初始化硬件,对接操作系统Boot Loader
又称为BIOS(basic io system)
现在有了UEFI(undefined extensible firmware interface)固件提供更丰富的支持如指纹锁蓝牙键盘等