操作系统结构与组成
操作系统结构
操作系统启动
- 计算机上电
- 处理器Reset
- 设置到初始状态
- 跳转到ROM代码(BIOS)
- 初始化启动所需最少的设备
- 从持久存储中加载BootLoader
- 跳转到BootLoader继续执行
- 加载OS其余的部分
- 初始化OS并运行
程序的执行
- GCC编译、汇编、链接各模块
- 编译器将程序编译成汇编文件
- 汇编器将汇编代码编程为可重定位的对象文件
- 链接器将多个对象文件链接成一个可执行文件
ELF 文件格式
- ELF(Executable and Linking Format)
- 可重定位的对象文件(.o)
- 可执行的对象文件
- 可被共享的对象文件(.so)
- 链接视图
- 由多个section组成
- 执行视图
- 由多个segments组成
四个段
- 代码段:指令序列(text segment)
- 数据段:全局数据(data segment)
- 栈(stack)
- 堆(heap)
布局特点
- 为了将代码段和数据段分离
- 栈和堆相向生长
- 栈自顶向底生长
- 堆自底向顶生长
全局数据和代码段
- 编译器静态分配,产生名字和符号索引
- 链接器翻译索引和重定位地址
- 加载器最终完成在内存的布局
栈
- 由编译器布局
- 进程创建的时候分配、进程结束的时候释放
- 相对于栈顶(栈指针)寻址
堆
- 链接器和加载器确定起始地址
- 通过库函数
malloc和free进行分配和释放 - 应用程序通过库函数进行管理
加载器
在Unix下,由加载器Loader完成如下工作
- 读取一个可执行文件
- 放置代码、数据、堆和栈
- 动态链接到共享库
- 运行应用程序
典型的UNIX操作系统
- 用户层
- 应用:程序员编写并编译后的应用程序
- 库:
- 精心设计的代码
- 预编译好的对象
- 通过头文件定义
- 通过链接器引入
- 类似函数调用
- 程序加载时必须定位
- 核心层
- 可移植层:系统调用功能的集合
- 机器相关层
- 启动
- 初始化
- 中断和例外
- I/O设备驱动
- 内存管理
- 处理器调度
- 模式切换
程序运行保护
CPU保护
- 保护CPU不被某个用户程序长期占用
- 保护用户程序间的尽可能隔离
- 维护CPU资源的抢占
内存保护
- 避免用户程序修改OS的代码和数据结构
I/O保护
- 防止用户程序随意的写入磁盘的任何位置
内核态的保护机制
需要满足处理器支持特权态,可以在特权模式和用户模式下切换。
graph LR
A([用户模式])---->|中断或异常|B([特权模式])
B---->|特殊的返回指令|A
在用户模式下只能执行常规指令和访问用户内存。
在特权模式下可以执行特权指令和访问核心内存。
- 特权指令包括
- 读写系统状态寄存器(CSR寄存器)
- 执行I/O操作
- 无效快表(TLB)项
- 特权指令包括
中断
硬件中断
- 由外部事件(外部设备)触发
- 例如:时钟中断,硬盘读写请求完成,移动鼠标,键盘输入
- 硬件中断产生与当前正在执行的进程无关
- 硬件中断可以被关闭(CSR寄存器)
可编程中断(软件中断)
- 由编程者用相应指令触发(
syscall)
中断处理程序
触发中断后,系统内核跳转到中断处理程序中,处理完毕后恢复被中断的进程。
异常
- 由当前正在执行的进程产生
- 类型:
- 出错:处理后,重新执行触发异常的指令
- 陷入:处理后,执行触发异常指令的下一条指令
- 中止:不再执行指令
系统调用机制
关键和核心功能由内核完成,用户仅仅进行调用,而不修改内核代码
过程
- 系统调用参数传递
- 寄存器传参
- 可用寄存器个数
- 系统调用参数个数
- 内存向量(数组)传参
- 一个寄存器传递起始地址
- 向量位于用户地址空间
- 堆栈传参
- 类似内存向量
- 遵循过程调用的约定
- 寄存器传参
- 系统模式从用户态切换到内核态
- 执行系统调用功能
- 返回结果,切换到用户态
- 通过寄存器返回结果
- 将错误返回给调用者
系统调用 对比 库函数调用
- 系统调用
- 陷入内核,在内核态执行具体功能
- 依赖于操作系统
- 内核态和用户态切换
- 库函数调用
- 在进程用户态空间执行和执行功能
- 标准C库函数相同
- 过程调用
内存管理
- 内核
- 分配带硬件保护的页面
- 分配一大块(多个页面)给库
- 不关心小粒度的分配
- 通过
sbrk/brk/mmap等函数分配页面,改变数据段长度
- 库
- 提供
malloc/free等函数用于分配和释放内存 - 应用通过这些函数细粒度管理内存
- 当页面用完后,库函数会向内核批量请求更多的页面
- 提供
内核结构
宏内核(Linux,Windows,BSD Unix)
- 在内核态实现操作系统所有功能
- 应用通过调用系统调用(
syscall)使用操作系统提供的功能 - 好处
- 内核所有函数共享地址空间
- 内核模块通信性能高
- 缺点
- 不稳定:模块的崩溃会造成系统的崩溃
- 不灵活:新增模块需要对内核进行编程
微内核(Mach,MINIX)
- 操作系统功能服务作为用户态的常规进程
- 应用通过消息获取服务进程的服务
- 好处
- 开发灵活
- 故障隔离
- 缺点
- 进程间的通信效率低
- 保护机制不完整
库操作系统(Exokernel,ExOS)
- 应用程序直接通过库与底层硬件交互
- 好处
- 效率高
- 缺点
- 通用性差
操作系统组成
- 处理器管理
- 内存管理
- I/O设备管理
- 文件系统
- GUI