Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

8.1 操作系统架构

操作系统是计算机系统中最核心的系统软件,它管理着计算机的所有硬件和软件资源,为上层应用提供抽象的接口和运行环境。理解操作系统的架构,能帮助我们更好地理解程序的运行机制。

操作系统的地位和作用

操作系统位于硬件和应用程序之间,承上启下:

┌─────────────────────────┐
│       应用程序           │  浏览器、办公软件、服务等
├─────────────────────────┤
│       操作系统           │  内核、系统库、驱动程序
├─────────────────────────┤
│       硬件层             │  CPU、内存、磁盘、外设等
└─────────────────────────┘

操作系统的核心作用:

  1. 资源管理:管理CPU、内存、磁盘、外设等所有硬件资源,公平合理地分配给各个进程使用
  2. 抽象接口:为上层应用提供统一的抽象接口,屏蔽底层硬件的细节,让应用程序不需要关心硬件的具体实现
  3. 隔离保护:隔离不同的进程,防止进程之间互相干扰,保证系统安全稳定
  4. 提升效率:通过调度、缓存、并发等技术提升系统的整体效率和资源利用率

内核态与用户态

为了保护操作系统的安全,现代CPU都提供了不同的特权级别,操作系统将运行环境分为内核态和用户态:

内核态(Kernel Mode)

  • 拥有最高特权,可以直接访问所有硬件资源,执行任意指令
  • 操作系统的核心代码运行在内核态,包括进程调度、内存管理、驱动程序等
  • 内核态的错误是致命的,可能导致整个系统崩溃

用户态(User Mode)

  • 特权级别较低,不能直接访问硬件资源,也不能直接访问内核地址空间
  • 所有的应用程序都运行在用户态
  • 用户态的程序要访问硬件资源,必须通过系统调用接口进入内核态,由操作系统代为访问
  • 用户态的错误通常只会影响当前进程,不会导致整个系统崩溃

状态切换

当用户态程序需要执行特权操作(比如读写文件、分配内存、发送网络请求)时,需要通过系统调用从用户态切换到内核态,操作系统完成操作后再切换回用户态。

  • 状态切换有一定的开销,需要保存和恢复上下文
  • 频繁的系统调用会降低程序性能

操作系统内核架构

内核是操作系统的核心部分,负责管理系统资源和提供核心功能,常见的内核架构有三种:宏内核、微内核、混合内核。

1. 宏内核(Monolithic Kernel)

宏内核是最传统的内核架构,所有的操作系统功能(进程管理、内存管理、文件系统、驱动程序、网络协议栈等)都运行在内核态,是一个单独的大程序。

  • 优点
    • 性能高,所有模块都在内核态运行,模块之间直接调用,没有通信开销
    • 实现简单,成熟稳定
  • 缺点
    • 内核庞大复杂,维护难度大
    • 一个模块的bug可能导致整个内核崩溃
    • 扩展性差,添加新功能需要重新编译内核
  • 代表系统:Linux、Unix、早期的Windows

Linux是最典型的宏内核实现,但Linux也吸收了微内核的优点,支持动态加载内核模块,需要的功能可以动态加载,不需要的时候卸载,兼顾了性能和扩展性。

2. 微内核(Micro Kernel)

微内核架构的设计思想是尽可能把内核的功能放到用户态运行,内核只保留最核心的功能:

  • 内核只负责最基本的功能:进程调度、进程间通信、基本的内存管理
  • 文件系统、驱动程序、网络协议栈等都作为独立的用户态服务运行
  • 服务之间通过消息传递进行通信
  • 优点
    • 内核小巧,稳定可靠,安全性高
    • 扩展性好,添加功能只需要修改对应的用户态服务,不需要修改内核
    • 一个服务崩溃不会影响整个系统,稳定性高
  • 缺点
    • 性能较低,服务之间通过消息传递通信,需要频繁的用户态和内核态切换,开销大
    • 实现复杂
  • 代表系统:Minix、QNX、L4、HarmonyOS微内核

微内核适合对稳定性、安全性要求高的场景,比如嵌入式系统、航空航天、自动驾驶等领域。

3. 混合内核(Hybrid Kernel)

混合内核结合了宏内核和微内核的优点,内核核心部分运行在内核态,同时把一些非核心的功能放到内核态或者用户态运行,兼顾性能和扩展性。

  • 核心功能在内核态运行,保证性能
  • 驱动程序、文件系统等模块可以动态加载,运行在内核态
  • 吸收了微内核的消息传递机制,同时保留了宏内核的高性能
  • 代表系统:Windows NT内核、macOS XNU内核、Android内核

Windows是典型的混合内核,大部分功能运行在内核态,同时支持动态加载驱动,兼顾性能和扩展性。

操作系统的核心模块

无论是哪种架构的操作系统,通常都包含以下核心模块:

1. 进程管理模块

  • 负责进程和线程的创建、调度、销毁
  • 实现CPU调度算法,分配CPU时间
  • 进程间通信机制
  • 进程同步与互斥支持

2. 内存管理模块

  • 管理物理内存和虚拟地址空间
  • 实现分页、分段、虚拟内存机制
  • 负责内存的分配、回收、置换
  • 内存保护,隔离不同进程的地址空间

3. 文件系统模块

  • 管理磁盘上的文件和目录
  • 实现文件系统的逻辑,处理文件的读写、权限管理
  • 磁盘空间管理
  • 缓存管理,提升文件访问性能

4. 设备驱动模块

  • 管理和控制各种硬件设备(磁盘、网卡、显卡、键盘等)
  • 提供统一的设备访问接口,屏蔽硬件差异
  • 处理硬件中断

5. 网络协议栈

  • 实现各种网络协议(TCP/IP、UDP、HTTP等)
  • 处理网络数据的发送和接收
  • 提供网络编程接口

6. 系统调用接口

  • 是用户态程序和内核之间的接口
  • 提供标准化的系统调用函数,比如open、read、write、fork等
  • 处理用户态到内核态的切换

系统调用与标准库

应用程序通常不会直接调用系统调用,而是通过标准库封装的接口:

  • 系统调用:操作系统提供的最底层接口,和平台相关,每个操作系统的系统调用不同
  • 标准库:对系统调用进行封装,提供跨平台的统一接口,比如C标准库的fopen、fread等函数,内部封装了不同操作系统的系统调用
  • 标准库会根据不同的操作系统调用对应的系统调用,实现跨平台兼容性

比如C语言的fopen()函数:

  • 在Linux上内部调用open()系统调用
  • 在Windows上内部调用CreateFile()系统调用
  • 应用程序只需要调用标准库函数,不需要关心底层操作系统的差异

思考问题

  1. 为什么操作系统要区分内核态和用户态?如果所有程序都运行在内核态会有什么问题?
  2. 宏内核和微内核各有什么优缺点?分别适合什么场景?
  3. 为什么应用程序通常不直接调用系统调用,而是通过标准库?
  4. 上下文切换和系统调用切换有什么区别?哪个开销更大?