第二章 操作系统结构
2.1 系统调用与接口
系统调用是操作系统提供给应用程序的编程接口,允许程序员通过编程方式请求操作系统服务。
2.1.1 系统调用类型
系统调用可以分为六个主要类别。
一、进程控制
- 进程创建与终止:
create_process()
、terminate_process()
、end()
、abort()
。这些调用用于创建新进程和终止现有进程。 - 程序加载与执行:
load()
、execute()
。用于将程序加载到内存并开始执行。 - 进程属性操作:
get_process_attributes()
、set_process_attributes()
。用于获取和设置进程的属性。 - 等待时间和事件:
wait_time()
、wait_event()
、signal_event()
。用于进程同步和事件等待。 - 内存分配和释放:
allocate_memory()
、free_memory()
。用于动态内存管理。 - 锁机制:
acquire_lock()
、release_lock()
。用于进程间的互斥控制。
二、文件管理
- 基本文件操作:
create()
、delete()
、open()
、close()
、read()
、write()
、reposition()
。用于文件的创建、删除、打开、关闭、读写和重定位。 - 文件属性:
get_file_attributes()
、set_file_attributes()
。用于获取和设置文件的属性。 - 文件操作:
move()
、copy()
等。用于文件的移动和复制。
三、设备管理
- 设备请求与释放:
request_device()
、release_device()
。用于请求和释放I/O设备。 - 读写设备:
read_device()
、write_device()
、reposition_device()
。用于设备的读写操作。 - 特殊操作:设备的特定功能控制。用于执行设备特定的操作。
四、信息
- 时间和日期:
get_time()
、get_date()
。用于获取系统时间和日期。 - 系统数据:
get_system_data()
(版本、内存使用率等)。用于获取系统的各种信息。 - 进程信息:
get_process_info()
、get_process_id()
。用于获取进程的相关信息。 - 调试支持:
dump()
内存、单步执行等。用于调试和诊断。
五、通信
- 消息传递模型:
create_connection()
、delete_connection()
、send_message()
、receive_message()
。用于进程间的消息传递。 - 共享内存模型:
shared_memory_create()
、shared_memory_attach()
、shared_memory_control()
。用于进程间的共享内存。 - 网络通信:
socket()
、bind()
等网络相关调用。用于网络通信。
六、保护
- 权限控制:
set_permission()
、get_permission()
。用于设置和获取资源的访问权限。 - 用户控制:
allow_user()
、deny_user()
。用于控制用户的访问权限。
2.1.2 用户接口
大多数程序员通过应用程序编程接口(API)设计程序,而不直接使用系统调用。API提供了更高层次的抽象,简化了程序设计。
常见API
- Windows API:Windows系统的应用程序接口,提供对Windows操作系统功能的访问。
- POSIX API:基于POSIX标准的系统(UNIX/Linux)的应用程序接口,提供跨平台的兼容性。
- Java API:Java虚拟机上的程序接口,提供Java程序的标准库。
库与运行时环境
- 程序员通过操作系统提供的代码库访问API(如UNIX/Linux中的libc)。
- 运行时环境(RTE)提供系统调用接口,连接到操作系统的系统调用。
参数传递方法
- 通过寄存器传递参数。参数直接存储在CPU寄存器中。
- 将参数存储在内存块中,并传递地址(Linux采用这两种方法的组合)。
- 将参数压入栈中,由操作系统从栈中获取。
2.1.3 命令解释器实现
命令解释器(Shell)是用户与操作系统交互的主要方式。Shell提供命令行界面,允许用户输入命令并执行。
实现方式
- 内置执行方式:解释器包含执行命令的代码,每个命令在解释器中有自己的代码实现。
- 系统程序执行方式:解释器仅识别要执行的命令文件,通过加载相应程序执行命令。
启动方式
- 系统启动时自动运行。Shell在系统启动时自动加载并运行。
- 用户登录时启动。用户登录后,Shell作为用户会话的一部分启动。
- 可以选择不同的命令解释器(Shell)。用户可以选择使用不同的Shell,如bash、zsh等。
2.2 操作系统设计
2.2.1 分层结构
操作系统可以采用模块化方法设计,创建松耦合系统。分层结构将操作系统分为多个层次,每层提供特定的功能。
分层设计特点
- 每层是抽象对象的实现,包含数据和操作。每层提供特定的服务,并隐藏实现细节。
- 高层可以调用低层功能,低层对高层不可见。高层通过接口调用低层服务。
- 每层只依赖较低层,简化构建和调试。分层设计提高了系统的可维护性和可扩展性。
优缺点
- 优点:模块化、易于调试、维护和扩展。分层设计使得系统更易于理解和修改。
- 缺点:定义每层功能的挑战、多层导致性能下降。分层设计可能导致额外的开销。
实际应用
- 网络协议(如TCP/IP)和Web应用采用分层设计。分层设计在网络协议中广泛应用。
- 操作系统中很少纯粹采用分层结构。大多数操作系统采用混合设计。
- 现代操作系统通常采用功能更强的少量层次结构。现代操作系统结合了分层和模块化设计。
2.2.2 微内核架构
微内核方法通过最小化内核功能,将系统服务移至用户空间。微内核架构将内核功能最小化,增强系统的灵活性和安全性。
微内核特点
- 内核仅包含基本功能(进程和内存管理、通信机制)。微内核只保留最基本的功能。
- 大多数服务(如文件系统、设备驱动等)在用户空间运行。服务在用户空间实现,减少内核复杂性。
- 通过消息传递实现组件间通信。微内核通过消息传递实现进程间通信。
优缺点
- 优点:系统扩展简单(无需修改内核)、增强安全性和可靠性。微内核架构提高了系统的可扩展性和安全性。
- 缺点:由于消息传递和进程切换导致性能开销。微内核架构可能导致性能下降。
实际应用
- Darwin(macOS/iOS的内核)。Darwin采用微内核架构。
- QNX(嵌入式系统)。QNX是一个实时操作系统,采用微内核设计。
- Windows NT 4.0(后来的Windows版本转向更单体化设计以提高性能)。Windows NT最初采用微内核设计,后来的版本转向单体内核。
2.2.3 模块化设计
现代操作系统通常使用可加载内核模块(LKM),实现动态功能扩展。模块化设计允许操作系统在运行时加载和卸载模块。
模块化特点
- 内核提供核心服务。内核提供基本的系统功能。
- 在启动或运行时动态链接额外模块。模块可以在系统运行时动态加载。
- 无需重新编译内核即可添加新功能。模块化设计提高了系统的灵活性。
实际应用
- Linux、macOS、Solaris等现代UNIX实现。现代UNIX系统广泛采用模块化设计。
- Windows系统。Windows支持可加载内核模块。
- 主要用于设备驱动程序和文件系统支持。模块化设计常用于设备驱动和文件系统。
2.3 虚拟机
2.3.1 虚拟机概念
虚拟化允许在单一物理硬件上抽象出多个不同的执行环境。虚拟机技术提供了在单一硬件上运行多个操作系统的能力。
核心概念
- 虚拟机(VM)是对计算机系统的软件实现。虚拟机提供与物理机相同的功能。
- 提供与物理机相同的功能。虚拟机可以运行完整的操作系统和应用程序。
- 允许操作系统作为应用程序在其他操作系统内运行。虚拟机可以在宿主操作系统上运行。
虚拟机管理器(VMM)
- 运行和管理客户操作系统。VMM负责管理虚拟机的生命周期。
- 控制虚拟机对物理资源的访问。VMM分配和管理虚拟机的资源。
- 保障各虚拟机之间的隔离性。VMM确保虚拟机之间的隔离和安全。
2.3.2 实现原理
一、模拟方式
- 在软件中模拟整个硬件环境。模拟提供完整的硬件抽象。
- 源CPU类型与目标CPU类型不同时使用。模拟可以在不同架构之间运行。
- 提供完整的硬件抽象。模拟提供完整的硬件功能。
二、硬件支持
- 现代CPU提供对虚拟化的硬件支持。硬件支持提高了虚拟化性能。
- 降低虚拟化开销,提高性能。硬件支持减少了虚拟化的性能损失。
2.3.3 虚拟机的优势与局限
优势
- 隔离性:每个虚拟机独立运行,互不干扰。虚拟机提供强隔离性。
- 资源管理:更有效地分配和使用计算资源。虚拟化提高了资源利用率。
- 灵活性:在同一硬件上运行不同操作系统。虚拟化提供了灵活的操作系统选择。
- 安全性:虚拟机之间相互隔离,提高系统安全性。虚拟化提高了系统的安全性。
局限性
- 性能开销:虚拟化层会导致性能损失。虚拟化可能导致性能下降。
- 资源需求:需要更多的总体资源。虚拟化需要额外的资源支持。
- 硬件依赖:某些虚拟化功能需要特定硬件支持。虚拟化依赖于硬件支持。
2.4 系统启动
2.4.1 引导流程
启动计算机通过加载内核的过程称为系统引导。系统引导是计算机启动的关键步骤。
引导过程
- 引导程序(Bootstrap/Boot Loader)定位内核。引导程序负责加载操作系统内核。
- 内核被加载到内存并启动。内核加载后开始初始化系统。
- 内核初始化硬件。内核识别和配置系统硬件。
- 挂载根文件系统。内核挂载根文件系统,准备用户空间启动。
多阶段引导
- 第一阶段:固件中的小型引导加载程序(BIOS/UEFI)运行。固件负责初始化硬件并加载引导程序。
- 第二阶段:加载位于固定磁盘位置(引导块)的第二加载程序。第二加载程序负责加载操作系统内核。
引导加载器
- BIOS:传统的基本输入/输出系统。BIOS是传统的引导固件。
- UEFI:统一可扩展固件接口,提供更好的64位和大型磁盘支持,作为单一引导管理器更快。UEFI是现代计算机的标准引导固件。
- GRUB:Linux和UNIX的开源引导程序。GRUB是常用的Linux引导加载器。
- 移动系统引导加载器:Android不使用GRUB,通常使用厂商提供的引导加载程序(如LK)。移动设备使用特定的引导加载器。
特殊启动模式
- 恢复模式:用于诊断和修复系统。恢复模式提供系统修复功能。
- 单用户模式:用于系统维护和故障排除。单用户模式用于系统维护。
2.4.2 内核加载
内核加载是引导过程的关键阶段。内核加载是系统启动的核心步骤。
加载过程
- 引导加载程序将操作系统内核从存储设备加载到内存。引导加载程序负责内核的加载。
- 内核文件有特定格式,必须放置在内存正确位置。内核加载需要特定的内存布局。
- 加载后,控制权转交给内核。内核加载完成后开始执行。
2.4.3 初始化过程
内核初始化包括多个关键步骤。内核初始化是系统启动的重要环节。
初始化步骤
- 硬件检测与初始化:识别和配置系统硬件。内核识别并配置硬件设备。
- 内存管理系统设置:建立内存管理数据结构。内核初始化内存管理系统。
- 调度器和进程管理初始化:准备进程调度系统。内核初始化进程调度器。
- 设备驱动程序加载:加载必要的设备驱动。内核加载设备驱动程序。
- 文件系统初始化:准备文件系统访问。内核初始化文件系统。
- 用户空间初始化:启动第一个用户空间进程。内核启动用户空间进程。