
一台计算机的硬件资源就那么多:一个或多个 CPU、一块内存、若干磁盘和网卡。如果让每个程序自己去抢 CPU、自己算内存地址、自己跟磁盘控制器打交道,结果必然是混乱、低效,甚至互相踩踏。
操作系统的职责,就是把这些裸资源包装成一套干净、有序、可共享的抽象,然后分发给上层程序:
硬件 | 操作系统提供的抽象 |
|---|---|
CPU | 进程、线程 |
内存 | 虚拟地址空间 |
磁盘 | 文件、目录 |
网卡 | Socket |
键盘/屏幕 | 标准输入输出流 |
所以程序员写 printf("hi") 时,根本不需要知道屏幕是 HDMI 还是串口——这就是抽象的价值。
1. 进程管理 CPU 只有一个(或几个),但看上去能同时跑几十个程序。靠的是时间片轮转 + 上下文切换:每隔几毫秒保存当前进程的寄存器现场,换另一个进程上场。切换本身有开销,所以时间片不能太短。
2. 内存管理 每个进程都以为自己独占一整块连续内存,实际是操作系统配合 MMU 做的虚拟内存映射。好处有三:进程之间互相隔离、可以用比物理内存更大的空间(配合换页)、程序加载地址不必固定。
3. 文件系统
把磁盘上散落的扇区组织成树形目录。它还负责缓存、日志、权限。一次 write 调用返回时,数据可能还在内存的页缓存里——这正是文件系统“骗”你的地方,也是它快的原因。
4. 设备管理 通过驱动程序统一各种硬件的差异,再用中断和 DMA 把 CPU 从“轮询等待”中解放出来。
Linux 选择了宏内核 + 可加载模块,算是一种务实折中。
用户代码不能直接碰硬件,必须通过系统调用这个受控入口。看一段最简单的 C 程序:
#include <unistd.h>
int main() {
write(1, "hi\n", 3); // 1 = 标准输出
return 0;
}write 并不是普通函数,它最终会触发一条特殊指令(x86 上是 syscall),CPU 切换到内核态,由内核完成真正的输出,再切回来。用 strace 能看到这个过程:
write(1, "hi\n", 3) = 3#include <stdio.h>
#include <unistd.h>
int main() {
fork(); // 从这里开始,变成两个进程
printf("pid = %d\n", getpid());
return 0;
}运行后你会看到两行输出,两个不同的 pid。fork 一次调用、两次返回:父进程拿到子进程的 pid,子进程拿到 0。这一行代码背后,是内核复制页表、分配 task_struct、把新进程挂进调度队列的整套动作。
操作系统最迷人的地方在于:它用有限的几样硬件,造出了一个近乎无限、彼此隔离、还能互相通信的世界。你写的每一行代码,几乎都在和它打交道——只是大多数时候,它安静得像不存在。
理解它,不是为了重写一个内核,而是为了在程序变慢、内存泄漏、进程卡死的时候,知道该往哪个方向看。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。