Linux 是怎样工作的 - [日] 武内觉

本书结合大量实验程序和图表,通俗易懂地介绍了 Linux 操作系统的运行原理和硬件的基础知识,涉及进程管理、进程调度器、内存管理、存储层次、文件系统和外部存储器等。
关于作者
武内觉 是日本知名的系统编程专家和技术作家:
- 系统软件工程师:长期从事操作系统和底层系统开发
- 技术作家:著有多本 Linux 和系统编程相关书籍
- 教育家:擅长用通俗易懂的方式讲解复杂的系统概念
武内觉以其"透过现象看本质"的写作风格著称,通过大量实验程序和图表,将抽象的操作系统概念具象化。
核心内容
1. 进程与线程
// 进程创建 - fork()
#include <unistd.h>
pid_t pid = fork();
if (pid == 0) {
// 子进程
printf("Child process, PID: %d\n", getpid());
execvp("/bin/ls", args); // 执行新程序
} else if (pid > 0) {
// 父进程
printf("Parent process, Child PID: %d\n", pid);
waitpid(pid, &status, 0); // 等待子进程
} else {
// fork 失败
perror("fork failed");
}
// 进程状态
// R (Running): 运行中或可运行
// S (Sleeping): 可中断睡眠
// D (Disk Sleep): 不可中断睡眠
// Z (Zombie): 僵尸进程
// T (Stopped): 停止状态
// 查看进程
ps aux // 查看所有进程
ps -ef | grep nginx // 查找特定进程
top // 实时查看进程状态
htop // 增强版 top
// 线程创建 - pthread
#include <pthread.h>
void* thread_func(void* arg) {
int* value = (int*)arg;
printf("Thread running, value: %d\n", *value);
return NULL;
}
pthread_t thread;
int value = 42;
// 创建线程
pthread_create(&thread, NULL, thread_func, &value);
// 等待线程结束
pthread_join(thread, NULL);
// 互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);
2. 进程调度
调度策略:
1. FIFO (SCHED_FIFO)
- 实时进程
- 先到先得,高优先级可抢占低优先级
2. Round Robin (SCHED_RR)
- 实时进程
- 时间片轮转,公平调度
3. CFS (Completely Fair Scheduler)
- 普通进程
- 根据 vruntime (虚拟运行时间) 调度
- 保证每个进程获得公平的 CPU 时间
查看调度信息:
chrt -p <pid> # 查看进程调度策略
chrt -f 10 ./program # 以 FIFO 策略运行程序
niceness (优先级):
nice -n 10 ./program # 以 nice 值 10 运行
renice 5 -p <pid> # 修改运行中进程的 nice 值
3. 内存管理
// 内存布局
/*
高地址
+------------------+
| 内核空间 |
+------------------+
| 栈 (Stack) | ← 向下增长
| ↓ |
| 堆 (Heap) | ← 向上增长
| ↑ |
| BSS 段 | 未初始化全局变量
| 数据段 | 已初始化全局变量
| 代码段 | 程序代码
+------------------+
低地址
*/
// 内存分配
#include <stdlib.h>
int* arr = (int*)malloc(100 * sizeof(int)); // 分配
arr = (int*)realloc(arr, 200 * sizeof(int)); // 重新分配
free(arr); // 释放
// 查看内存使用
cat /proc/meminfo # 详细内存信息
free -h # 人类可读格式
ps aux --sort=-%mem # 按内存使用排序进程
// 虚拟内存
// - 每个进程有独立的虚拟地址空间
// - 通过页表映射到物理内存
// - 支持内存超卖 (overcommit)
4. 文件系统
# 文件系统层次结构
/
├── bin/ # 基本命令
├── boot/ # 启动文件
├── dev/ # 设备文件
├── etc/ # 配置文件
├── home/ # 用户主目录
├── lib/ # 库文件
├── media/ # 可移动媒体
├── mnt/ # 挂载点
├── opt/ # 可选软件包
├── proc/ # 进程信息 (虚拟文件系统)
├── root/ # root 用户主目录
├── run/ # 运行时数据
├── sbin/ # 系统管理命令
├── sys/ # 系统信息 (虚拟文件系统)
├── tmp/ # 临时文件
├── usr/ # 用户程序
└── var/ # 可变数据 (日志、缓存等)
# 查看文件系统的信息
df -h # 磁盘使用量
du -sh * # 目录大小
inode # inode 信息
ls -li # 查看 inode 号
# 文件类型
# - : 普通文件
# d : 目录
# l : 符号链接
# c : 字符设备
# b : 块设备
# p : 管道
# s : 套接字
5. 系统调用
// I/O 系统调用
#include <fcntl.h>
#include <unistd.h>
int fd = open("file.txt", O_RDONLY); // 打开文件
char buf[1024];
read(fd, buf, sizeof(buf)); // 读取
write(fd, buf, len); // 写入
close(fd); // 关闭
// 文件操作
unlink("file.txt"); // 删除文件
link("old", "new"); // 硬链接
symlink("target", "link"); // 软链接
rename("old", "new"); // 重命名
// 进程控制
pid_t getpid(void); // 获取进程 ID
pid_t getppid(void); // 获取父进程 ID
pid_t fork(void); // 创建进程
int execvp(...); // 执行程序
int wait(...); // 等待进程
// 查看系统调用
strace -p <pid> # 跟踪进程的系统调用
lsof -p <pid> # 查看进程打开的文件
6. 信号处理
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void signal_handler(int sig) {
printf("Received signal: %d\n", sig);
}
// 注册信号处理函数
signal(SIGINT, signal_handler); // Ctrl+C
signal(SIGTERM, signal_handler); // 终止信号
signal(SIGHUP, signal_handler); // 挂起信号
// 发送信号
kill -SIGTERM <pid> # 优雅终止
kill -SIGKILL <pid> # 强制终止
kill -SIGHUP <pid> # 重新加载配置
kill -SIGUSR1 <pid> # 用户定义信号
// 常见信号
// SIGINT (2): 中断信号 (Ctrl+C)
// SIGTERM (15): 终止信号
// SIGKILL (9): 强制杀死 (不可捕获)
// SIGHUP (1): 挂起信号
// SIGSEGV (11): 段错误
// SIGPIPE (13): 管道破裂
// SIGCHLD (17): 子进程状态改变