操作系统(Operating System, OS)是管理计算机硬件与软件资源的系统软件,是用户与计算机硬件之间的桥梁。从手机里的 Android/iOS 到服务器上的 Linux,从嵌入式设备的 RTOS 到超级计算机的专用系统,操作系统无处不在。
功能 说明 示例
进程管理 进程/线程的创建、调度、同步与通信 Linux 的 fork()、sched
内存管理 内存分配、虚拟内存、地址转换 分页、交换(swap)
文件管理 文件的组织、存储、访问与权限 ext4、NTFS、FAT32
设备管理 设备驱动、中断处理、I/O 调度 显卡驱动、磁盘驱动
1940s 无操作系统:程序员直接操作硬件
1950s 批处理系统:作业成批提交
1960s 分时系统:多个用户共享计算机(CTSS、Multics)
1970s Unix 诞生:Ken Thompson、Dennis Ritchie 在贝尔实验室
1980s MS-DOS、macOS 相继出现
1990s Linux 发布(1991,Linus Torvalds)、Windows NT
2000s 移动操作系统:iOS(2007)、Android(2008)
2020s 云计算与容器:Linux 成为云与 AI 的基础底座
操作系统 类型 特点
Linux 开源、类 Unix 服务器、云计算、AI 训练的事实标准
Windows 闭源 桌面与办公市场占有率最高
macOS 类 Unix(BSD 内核) 苹果生态、开发者友好
Android 基于 Linux 内核 全球装机量最大的移动系统
iOS 类 Unix(XNU 内核) 苹果移动生态
RTOS (FreeRTOS 等)实时系统 嵌入式、汽车、工业控制
进程(Process)是程序的一次执行实例,拥有独立的地址空间。
进程的五大状态:
创建 → 就绪 → 运行 → 终止
↑ ↓
└─ 阻塞 ←┘
状态 说明
新建(New) 进程正在被创建
就绪(Ready) 等待 CPU,随时可运行
运行(Running) 正在占用 CPU 执行
阻塞(Blocked) 等待某个事件(如 I/O 完成)
终止(Terminated) 执行完毕或被强制结束
维度 进程 线程
资源 拥有独立地址空间和资源 共享所属进程的地址空间
开销 创建/切换开销大 创建/切换开销小
通信 需通过 IPC 机制 直接读写共享内存
独立性 一个崩溃不影响其他进程 一个线程崩溃可能导致进程崩溃
应用 隔离要求高的场景 高并发计算场景
#![allow(unused)]
fn main() {
use std::process::Command;
// 创建子进程
fn spawn_process() {
let output = Command::new("ls")
.arg("-l")
.output()
.expect("failed to execute");
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
}
// 创建线程
fn spawn_thread() {
let handle = std::thread::spawn(|| {
for i in 0..10 {
println!("子线程: {i}");
}
});
handle.join().unwrap(); // 等待线程结束
}
}
算法 说明 优点 缺点
FCFS (先来先服务)按到达顺序执行 公平、实现简单 短任务等待长任务(护航效应)
SJF (短作业优先)优先执行最短任务 平均等待时间最短 长任务可能饿死
RR (时间片轮转)每个任务分得固定时间片 响应时间均匀 时间片大小难权衡
优先级调度 高优先级先执行 灵活 低优先级可能饿死
多级反馈队列 多队列 + 动态优先级 兼顾响应与吞吐 实现复杂
时间片 = 4
任务队列: A(8) B(4) C(2)
A A A A | B B B B | C C | A A A A
0 4 8 10 14
方式 说明 特点
管道(Pipe) 父子进程间单向数据流 简单,Linux 命令 | 的底层
命名管道(FIFO) 任意进程间通信 需创建文件系统入口
消息队列 消息的链表,按类型读取 有界缓冲、消息粒度
共享内存 多个进程映射同一块内存 最快,需自行同步
信号量 计数器,用于同步与互斥 常与共享内存配合
信号(Signal) 异步事件通知 如 SIGINT(Ctrl+C)
Socket 网络通信,也可本地通信 跨机器通用
进程 A ──写──> [共享内存] <──读── 进程 B
↑
信号量(互斥保护)
#![allow(unused)]
fn main() {
use std::sync::{Arc, Mutex};
use std::thread;
fn mutex_example() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
}));
}
for h in handles {
h.join().unwrap();
}
println!("count = {}", *counter.lock().unwrap()); // count = 10
}
}
多个线程同时读写共享数据时,结果依赖执行顺序,称为竞态条件(Race Condition) 。解决方法是让共享数据的访问进入临界区(Critical Section) ,同一时刻只允许一个线程进入。
机制 说明
互斥锁(Mutex) 一次只有一个线程持有锁
读写锁(RwLock) 读读共享、读写互斥
信号量(Semaphore) 允许多个(N 个)线程进入
条件变量(Condvar) 等待某个条件满足后被唤醒
死锁(Deadlock)产生的四个必要条件:
条件 说明
互斥 资源一次只能被一个进程占用
占有并等待 持有资源的同时等待其他资源
不可剥夺 资源只能由持有者主动释放
循环等待 多个进程形成等待环路
#![allow(unused)]
fn main() {
// 经典死锁场景:两个线程以相反顺序加锁
// 线程 A: lock(x) -> lock(y)
// 线程 B: lock(y) -> lock(x)
// 破局方法:所有线程按相同顺序加锁
}
操作系统为每个进程提供独立的虚拟地址空间,通过页表 映射到物理内存。虚拟内存让进程“以为“自己独占全部内存。
虚拟地址 → 页号 + 偏移量
│
页表查询
│
物理地址 ← 帧号 + 偏移量
当内存不足时,需要把部分页面换出到磁盘:
算法 说明 特点
FIFO 换出最早进入的页 实现简单,可能有 Belady 异常
LRU 换出最久未使用的页 性能好,实现开销大
时钟(Clock) 近似 LRU 的环形扫描 折中方案,Linux 采用
OPT 换出未来最久不用的页 理论最优,无法实现(用于对比)
Rust 的所有权(Ownership)和借用(Borrow)机制在编译期保证内存安全,从根源上杜绝了 C/C++ 常见的悬垂指针、缓冲区溢出、释放后使用 等问题——这正是 Rust 被称为“内存安全语言“的原因。
#![allow(unused)]
fn main() {
fn memory_safety() {
let s = String::from("hello");
let t = &s; // 借用,不可变
// let u = &mut s; // 编译错误:不能同时可变借用
println!("{t}");
// s 在离开作用域时自动释放,不会泄漏也不会重复释放
}
}
组件 说明
目录 文件的层级组织(树形结构)
inode 保存文件元数据(权限、大小、数据块位置)
超级块 描述整个文件系统的元信息
挂载点 文件系统接入目录树的入口(如 /、/home)
文件系统 平台 特点
ext4 Linux 默认文件系统,支持日志、大文件
XFS Linux 高性能,适合大文件
Btrfs Linux 支持快照、压缩、校验和
NTFS Windows 支持权限、加密、日志
APFS macOS 苹果默认,支持快照与加密
FAT32/exFAT 跨平台 U 盘和存储卡通用
#![allow(unused)]
fn main() {
use std::fs;
use std::io::Write;
fn file_ops() {
// 写文件
fs::write("hello.txt", "Hello, OS!\n").unwrap();
// 追加写入
let mut f = fs::OpenOptions::new()
.append(true)
.open("hello.txt")
.unwrap();
f.write_all(b"Append line.\n").unwrap();
// 读文件
let content = fs::read_to_string("hello.txt").unwrap();
println!("{content}");
// 遍历目录
for entry in fs::read_dir(".").unwrap() {
println!("{:?}", entry.unwrap().file_name());
}
}
}
方式 说明 特点
程序查询(Polling) CPU 不断轮询设备状态 简单,浪费 CPU
中断(Interrupt) 设备完成后通知 CPU 高效,现代系统主流
DMA 设备直接读写内存,完成后中断 传输大数据时不占用 CPU
设备发出中断信号
↓
CPU 保存现场(寄存器、PC)
↓
跳转到中断处理程序(ISR)
↓
处理设备请求
↓
恢复现场,继续执行原程序
命令 用途
ls / cd / pwd查看目录、切换目录、显示当前路径
ps / top查看进程 / 动态查看进程与资源
kill发送信号终止进程
chmod / chown修改权限 / 修改所有者
mount / df挂载文件系统 / 查看磁盘使用
grep / find文本搜索 / 文件查找
systemctl管理 systemd 服务
-rwxr-xr-- 1 user group 4096 Aug 16 10:00 main.rs
| | | | |
| | | └─ 其他用户: r--
| | └─ 组用户: r-x
| └─ 所有者: rwx
└─ 文件类型: - 普通文件 / d 目录 / l 符号链接
用户程序不能直接操作硬件,必须通过**系统调用(System Call)**请求内核服务。
用户态(User Mode) 内核态(Kernel Mode)
┌─────────────────┐ syscall ┌─────────────────┐
│ 应用程序 │ ──────────> │ 内核服务 │
│ read() write() │ <────────── │ 文件/网络/内存 │
└─────────────────┘ 返回结果 └─────────────────┘
#![allow(unused)]
fn main() {
use std::fs::File;
use std::io::Read;
// std::fs::File::open 底层调用 open(2) 系统调用
fn syscall_example() {
let mut f = File::open("/etc/hostname").unwrap();
let mut buf = String::new();
f.read_to_string(&mut buf).unwrap();
println!("hostname: {buf}");
}
}
Rust 的**无标准库(#![no_std])**模式和所有权模型非常适合编写操作系统内核。著名的教学项目包括:
#![allow(unused)]
fn main() {
// 最小的 Rust 内核入口(示意)
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
}
知识点 要点
操作系统功能 进程、内存、文件、设备四大管理
进程与线程 独立地址空间 vs 共享地址空间
进程调度 FCFS、SJF、RR、优先级、多级反馈队列
进程间通信 管道、消息队列、共享内存、信号量、信号、Socket
同步与互斥 Mutex、RwLock、信号量、条件变量、死锁四条件
内存管理 虚拟内存、页表、页面置换(LRU、Clock)
文件系统 ext4、NTFS、inode、权限、挂载
I/O 方式 轮询、中断、DMA
系统调用 用户态/内核态切换
Rust 生态 std::thread、std::sync、nix、tokio、no_std 内核
多线程累加 :用 Rust 的 Arc<Mutex<u64>> 实现 10 个线程并发累加,观察结果并解释为什么需要互斥锁。
生产者-消费者 :用 std::sync::mpsc 通道实现生产者-消费者模型,验证有界缓冲的阻塞行为。
死锁复现 :设计两个线程以相反顺序加两把锁的程序,观察死锁现象,再用“统一加锁顺序“修复。
文件系统探索 :用 Rust 遍历一个目录树,统计各类文件的数量和总大小,模拟 du 命令。
调度模拟 :实现 FCFS 和 RR 两种调度算法的模拟器,比较不同任务序列下的平均等待时间。
系统调用观察 :用 strace 跟踪 ls 命令的系统调用,找出它访问了哪些文件、调用了哪些 syscall。