操作系统与 Linux 内核实战教程

操作系统与 Linux 内核实战教程

系列博客 | 基于华为云 ECS 真实环境 | ecs-bc5b 集群

服务器: 4× c6.large.2 (2vCPU / 4GiB) | Ubuntu 24.04 | 内核 6.8.0-106-generic

涵盖: 系统基础 → 进程管理 → 系统编程 → 并发同步 → 中断驱动 → 内存管理 → 文件系统 → 死锁分析 → 综合项目

全部命令输出来自真实服务器 ecs-bc5b-0001 (159.138.23.92),保证可复现。


实验环境

复制代码
┌──────────────────────────────────────────────────────────────────┐
│                    ecs-bc5b 集群拓扑                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│   os-01 (ecs-bc5b-0001)     os-02 (ecs-bc5b-0002)               │
│   159.138.23.92             189.1.231.16                         │
│   192.168.0.150             192.168.0.41                         │
│   ┌──────────────┐          ┌──────────────┐                    │
│   │ 2vCPU / 4GiB │          │ 2vCPU / 4GiB │                    │
│   │ Ubuntu 24.04 │          │ Ubuntu 24.04 │                    │
│   └──────────────┘          └──────────────┘                    │
│                                                                  │
│   os-03 (ecs-bc5b-0003)     os-04 (ecs-bc5b-0004)               │
│   119.8.35.131              159.138.148.88                       │
│   192.168.0.144             192.168.0.52                         │
│   ┌──────────────┐          ┌──────────────┐                    │
│   │ 2vCPU / 4GiB │          │ 2vCPU / 4GiB │                    │
│   │ Ubuntu 24.04 │          │ Ubuntu 24.04 │                    │
│   └──────────────┘          └──────────────┘                    │
└──────────────────────────────────────────────────────────────────┘

第一部分:Linux 系统基础篇

第 01 讲:Linux 系统入门与环境搭建

1.1 Linux 系统架构

复制代码
┌─────────────────────────────────────────────────────────┐
│                     Userspace (用户空间)                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │   Shell  │  │   Apps   │  │  Daemons │  │  Tools   │ │
│  │  (bash)  │  │ (nginx)  │  │ (sshd)   │  │ (gcc)    │ │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘ │
│       └──────────────┴─────────────┴─────────────┘       │
│                          │ GNU C Library (glibc)          │
├──────────────────────────┼────────────────────────────────┤
│            Kernelspace (内核空间)                          │
│  ┌─────────────────────────────────────────────────────┐  │
│  │          System Call Interface (SCI)                │  │
│  ├──────────┬──────────┬──────────┬───────────────────┤  │
│  │  Process │  Memory  │   VFS    │  Network Stack    │  │
│  │  Mgmt    │  Mgmt    │ (ext4)   │  (TCP/IP)         │  │
│  ├──────────┴──────────┴──────────┴───────────────────┤  │
│  │              Device Drivers                         │  │
│  ├─────────────────────────────────────────────────────┤  │
│  │                  Hardware                           │  │
│  └─────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘

关键概念中英对照:

中文 English 说明
内核 Kernel 操作系统核心,直接管理硬件
用户空间 Userspace / Userland 应用程序运行的区域
系统调用 System Call (syscall) 用户程序请求内核服务的接口
守护进程 Daemon 后台运行的服务进程
Shell Shell 命令行解释器,用户与内核的桥梁
GNU C 库 glibc (GNU C Library) Linux 系统最基础的 C 库

1.2 实验环境验证

在 os-01 上执行 uname -a 确认系统信息:

bash 复制代码
root@os-01:~# uname -a
Linux ecs-bc5b-0001 6.8.0-106-generic #106-Ubuntu SMP PREEMPT_DYNAMIC
  x86_64 x86_64 x86_64 GNU/Linux

root@os-01:~# cat /etc/os-release | head -3
PRETTY_NAME="Ubuntu 24.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"

root@os-01:~# nproc
2

root@os-01:~# free -h
               total        used        free      shared  buff/cache   available
Mem:           3.5Gi       436Mi       2.7Gi       1.0Mi       531Mi       3.0Gi
Swap:             0B          0B          0B

1.3 发行版选型

发行版 包管理 适用场景 内核策略
Ubuntu/Debian apt / dpkg 桌面开发、教学 较新内核
CentOS/RHEL yum / rpm 企业服务器 长期稳定内核
Alpine apk 容器镜像 musl libc 轻量
Arch pacman 滚动更新爱好者 最新主线内核

本教程选择 Ubuntu 24.04 LTS,因为它兼具较新内核(6.8.x)和 5 年长期支持,适合教学。


第 02 讲:用户管理与文件权限

2.1 Linux 用户与组

/etc/passwd 格式: 用户名:x:UID:GID:描述:家目录:Shell

文件 用途
/etc/passwd 用户账户信息(密码占位符 x)
/etc/shadow 加密密码 + 过期策略
/etc/group 组信息
/etc/sudoers sudo 权限配置

用户管理命令速查:

命令 作用 示例
useradd 创建用户 useradd -m -s /bin/bash devuser
usermod 修改用户 usermod -aG docker devuser
userdel 删除用户 userdel -r devuser
passwd 设置密码 passwd devuser
id 查看身份 id devuser

2.2 文件权限 rwx 机制

复制代码
  ┌──── 文件类型: - 普通文件, d 目录, l 符号链接
  │  ┌─ Owner  ┬─ Group  ┬─ Others
  │  │  r w x  │  r w x  │  r w x
 -│rwx│r-x│r--
  │   │   │      │
  │   │   └── 组权限 (Group)
  │   └────── 所有者权限 (Owner / User)
  └────────── 文件类型 (Type)

权限含义:

权限 数字 文件 目录
r (Read) 4 读取文件内容 列出目录内容 (ls)
w (Write) 2 修改文件内容 创建/删除目录内文件
x (eXecute) 1 执行文件 进入目录 (cd)

数字模式速查 : chmod 755 file = rwxr-xr-x, chmod 644 file = rw-r--r--

符号模式 : chmod u+x file (给 owner 加执行), chmod go-w file (移除 group/other 写)

2.3 特殊权限位

权限 数字 作用 经典案例
SUID (Set UID) 4 以文件所有者身份执行 /usr/bin/passwd, /usr/bin/sudo
SGID (Set GID) 2 以文件所属组身份执行 /usr/bin/wall
Sticky Bit 1 只有 owner 能删自己的文件 /tmp 目录
bash 复制代码
root@os-01:~# ls -la /usr/bin/passwd /usr/bin/sudo /tmp
-rwsr-xr-x 1 root root  72456 Mar 30  2026 /usr/bin/passwd   # SUID
-rwsr-xr-x 1 root root 249624 Mar 30  2026 /usr/bin/sudo     # SUID
drwxrwxrwt 2 root root   4096 Jun  7 20:00 /tmp               # Sticky Bit

SUID 风险: 如果 SUID 程序有漏洞可被利用提权,务必谨慎审计。


第 03 讲:Linux 目录结构与文件操作

3.1 FHS 标准目录树

复制代码
/                            # 根目录
├── bin -> usr/bin           # 基本命令 (cat, ls, cp)
├── sbin -> usr/sbin         # 系统管理命令 (fdisk, mkfs)
├── boot/                    # 内核 & 引导文件
├── dev/                     # 设备文件
├── etc/                     # 系统配置文件 ⭐
├── home/                    # 普通用户家目录
├── lib -> usr/lib           # 共享库
├── media/ | mnt/            # 挂载点
├── opt/                     # 第三方软件
├── proc/                    # 进程信息伪文件系统 ⭐
├── root/                    # root 家目录
├── run/                     # 运行时临时文件
├── sys/                     # 内核信息伪文件系统 ⭐
├── tmp/                     # 临时文件 (Sticky Bit)
├── usr/                     # Unix System Resources
│   ├── bin/                 # 用户命令
│   ├── lib/                 # 库文件
│   ├── local/               # 本地编译安装
│   └── share/               # 架构无关数据
└── var/                     # 可变数据 (日志、缓存)
    ├── log/                 # 日志文件 ⭐
    ├── cache/               # 缓存
    └── spool/               # 任务队列

3.2 文本处理三剑客

grep --- 文本搜索
bash 复制代码
# 在 /etc/passwd 中搜索包含 root 的行
root@os-01:~# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash

# 递归搜索 + 忽略大小写
grep -rin "error" /var/log/
# -r = recursive  -i = ignore case  -n = line numbers
选项 含义
-i 忽略大小写 (ignore case)
-v 反向匹配 (invert match)
-r 递归搜索 (recursive)
-n 显示行号 (line number)
-c 计数匹配行 (count)
-E 扩展正则 (extended regex)
sed --- 流编辑器
bash 复制代码
# 替换文本
echo "Hello World" | sed 's/World/Linux/'
# => Hello Linux

# 删除空行
sed '/^$/d' file.txt

# 在第 3 行后插入
sed '3a\new line' file.txt

# 就地修改 (-i)
sed -i 's/old/new/g' file.txt   # g = global (替换所有匹配)
awk --- 数据处理
bash 复制代码
# 打印第1列和第3列
root@os-01:~# awk -F: '{print $1, $3}' /etc/passwd | head -5
root 0
daemon 1
bin 2
sys 3
sync 4

# 条件过滤: UID >= 1000 的用户
awk -F: '$3 >= 1000 {print $1, $3}' /etc/passwd

# 统计所有进程的内存总和
ps aux | awk '{sum+=$6} END {print sum/1024 " MB"}'
内置变量 含义
$0 整行
$1, $2, ... 第 N 列
NF 列数 (Number of Fields)
NR 行号 (Number of Records)
FS / -F 字段分隔符 (Field Separator)
END 处理完后执行

第二部分:进程管理与并发编程篇

第 04 讲:Linux 进程基础

4.1 进程生命周期

复制代码
     fork()                  exec()             _exit()
  ┌─────────┐  创建子进程    ┌─────────┐  进程结束   ┌─────────┐
  │ Running │ ─────────────→ │  Ready  │ ──────────→ │ Zombie  │
  └─────────┘                └─────────┘             └─────────┘
       │                         ↑                       │
       │  等待 I/O               │ 调度                  │ wait()
       ↓                         │                       ↓
  ┌─────────┐                    │                  ┌──────────┐
  │ Blocked │ ──── I/O 完成 ────→┘                  │ Terminated│
  └─────────┘                                       └──────────┘

关键系统调用:

系统调用 作用 返回值
fork() 创建子进程(复制父进程) 父进程返回子PID, 子进程返回0
execve() 加载新程序替换当前进程 成功不返回, 失败返回-1
wait() / waitpid() 父进程等待子进程结束 子进程PID
exit() / _exit() 终止当前进程

4.2 fork() 实战

在 os-01 上编译运行 fork 演示程序:

bash 复制代码
root@os-01:~# ./fork_demo
[PID=7783] Before fork
[PID=7783] Before fork        # ← 注意: printf 被 fork 复制了缓冲区!
[PID=7784] Child: PPID=7783, fork returned 0
[PID=7783] Parent: fork returned child PID=7784
[PID=7783] Parent: Child has exited

代码解析:

c 复制代码
pid_t pid = fork();
if (pid == 0) {
    // 子进程: fork() 返回 0
    printf("[PID=%d] Child: PPID=%d, fork returned %d\n", getpid(), getppid(), pid);
} else if (pid > 0) {
    // 父进程: fork() 返回子进程 PID
    printf("[PID=%d] Parent: fork returned child PID=%d\n", getpid(), pid);
    wait(NULL);  // 等待子进程结束,防止僵尸进程 (Zombie)
}

4.3 /proc/PID/status 解读

bash 复制代码
root@os-01:~# cat /proc/$SLEEP_PID/status | head -16
Name:   sleep              # 进程名称
Umask:  0022               # 文件权限掩码
State:  S (sleeping)       # 进程状态: R=运行 S=睡眠 D=不可中断 T=停止 Z=僵尸
Tgid:   7785               # 线程组 ID
Pid:    7785               # 进程 ID
PPid:   7776               # 父进程 ID
Uid:    0   0   0   0      # 实际/有效/保存/文件系统 UID
Gid:    0   0   0   0      # 实际/有效/保存/文件系统 GID
FDSize: 64                 # 文件描述符表大小

进程状态一览:

状态码 含义 常见场景
R (Running) 正在运行或在运行队列中 CPU 密集型任务
S (Sleeping) 可中断睡眠 等待 I/O
D (Disk Sleep) 不可中断睡眠 等待磁盘 I/O
T (Stopped) 暂停 Ctrl+Z / SIGSTOP
Z (Zombie) 僵尸进程 子进程已退出但父进程未 wait()

第 05 讲:进程管理与监控

5.1 ps --- 进程快照

bash 复制代码
root@os-01:~# ps aux --sort=-%mem | head -8
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         337  0.0  0.7 288952 27292 ?        SLsl 20:00   0:00 /sbin/multipathd -d -s
root        4504  0.0  0.6 109640 23456 ?        Ssl  20:00   0:00 /usr/bin/python3 ...
root        4320  0.0  0.5 333776 18916 ?        Ssl  20:00   0:00 /usr/sbin/NetworkManager
root           1  0.2  0.3  22604 13740 ?        Ss   20:00   0:02 /sbin/init noibrs
含义
USER 进程所有者
PID 进程 ID
%CPU CPU 使用率
%MEM 物理内存使用率
VSZ 虚拟内存大小 (Virtual Memory Size) - KiB
RSS 常驻内存大小 (Resident Set Size) - KiB
STAT 进程状态码
START 启动时间
TIME 累计 CPU 时间

5.2 top --- 实时监控

bash 复制代码
root@os-01:~# top -b -n 1 | head -12
top - 20:15:53 up 15 min,  1 user,  load average: 0.10, 0.07, 0.05
Tasks: 116 total,   1 running, 115 sleeping,   0 stopped,   0 zombie
%Cpu(s):  4.3 us,  0.0 sy,  0.0 ni, 91.3 id,  4.3 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3531.5 total,   2787.3 free,    436.7 used,    531.0 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   3094.9 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      1 root      20   0   22604  13740   9584 S   0.0   0.4   0:02.73 systemd
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kthreadd

Load Average 解读 : 0.10, 0.07, 0.05 分别表示 1分钟 / 5分钟 / 15分钟 的平均负载。

负载与 CPU 核心数的关系 : 负载 < CPU 核心数 = 正常;负载 > CPU 核心数 = 过载排队。

本例 2 vCPU,负载 0.10,表示系统非常空闲。

top 交互快捷键:

快捷键 功能
1 显示每个 CPU 核心
M 按内存使用排序
P 按 CPU 使用排序
c 显示完整命令行
k 杀进程(输入 PID)
q 退出

5.3 进程优先级与 nice 值

复制代码
Nice 值范围: -20 (最高优先级) 到 19 (最低优先级), 默认 0

  高优先级 ←──────────────────────→ 低优先级
   -20    -10      0      10     19
   │       │      │       │      │
   └─── root ──┘  └── 普通用户 ──┘
       可设负值      仅可设正值
命令 作用
nice -n 10 command 以较低优先级启动
renice -n -5 -p PID 调整运行中进程的优先级

第 06 讲:Shell 多进程并发

6.1 后台任务

bash 复制代码
# & 放后台
command &

# Ctrl+Z 暂停 → bg 放后台继续
long_running_command
# Ctrl+Z
bg

# jobs 查看后台任务
jobs -l

# fg 调回前台
fg %1

6.2 并发实战

bash 复制代码
root@os-01:~# ./concurrent.sh
=== Shell Concurrent Demo ===
[Task 1] 7776 started at 20:15:53
[Task 4] 7776 started at 20:15:53
[Task 2] 7776 started at 20:15:53
[Task 3] 7776 started at 20:15:53
[Task 5] 7776 started at 20:15:53
[Task 2] 7776 finished at 20:15:54
[Task 1] 7776 finished at 20:15:55
[Task 5] 7776 finished at 20:15:55
[Task 4] 7776 finished at 20:15:56
[Task 3] 7776 finished at 20:15:56
All tasks done!

脚本解析:

bash 复制代码
# 启动并发任务
for i in 1 2 3 4 5; do
    task $i &       # & = 后台运行, 不等待完成
    pids[$i]=$!     # $! = 最近后台进程的 PID
done

# 等待所有任务
for i in 1 2 3 4 5; do
    wait ${pids[$i]}  # wait 阻塞直到指定 PID 退出
done

6.3 并发控制

bash 复制代码
# 限制并发数(进程池模式)
MAX_JOBS=4
running=0
for file in *.log; do
    process "$file" &
    running=$((running + 1))
    if [ $running -ge $MAX_JOBS ]; then
        wait -n           # 等待任何一个完成
        running=$((running - 1))
    fi
done
wait  # 等待剩余任务

第三部分:系统编程与调试工具篇

第 07 讲:GCC 编译器深度使用

7.1 GCC 编译四阶段

复制代码
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  预处理       │ → │  编译        │ → │  汇编        │ → │  链接        │
│  Preprocessor│    │  Compiler    │    │  Assembler   │    │  Linker      │
├──────────────┤    ├──────────────┤    ├──────────────┤    ├──────────────┤
│ .c ──→ .i   │    │ .i ──→ .s   │    │ .s ──→ .o   │    │ .o ──→ elf  │
│ 展开宏/头文件 │    │ 生成汇编     │    │ 生成目标码   │    │ 链接库       │
│ gcc -E       │    │ gcc -S      │    │ gcc -c      │    │ gcc          │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘

真实输出:

bash 复制代码
root@os-01:~# gcc -E hello.c -o hello.i
# hello.i 共 820 行(头文件展开后)

root@os-01:~# gcc -S hello.i -o hello.s
root@os-01:~# head -15 hello.s
        .file   "hello.c"
        .text
        .globl  add
        .type   add, @function
add:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)    # 第一个参数 a
        movl    %esi, -8(%rbp)    # 第二个参数 b

root@os-01:~# gcc -c hello.s -o hello.o
root@os-01:~# file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

root@os-01:~# gcc hello.o -o hello_bin
root@os-01:~# file hello_bin
hello_bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
           dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
           BuildID[sha1]=ff355b..., not stripped

root@os-01:~# ./hello_bin
MSG=Hello from GCC pipeline, add(3,4)=7

root@os-01:~# ldd hello_bin
        linux-vdso.so.1 (0x00007ffe30fa3000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007edbbc000000)
        /lib64/ld-linux-x86-64.so.2 (0x00007edbbc33b000)
文件类型 说明
.i 预处理后文件 --- 宏展开、头文件内联
.s 汇编代码 --- 人类可读
.o 目标文件 (Object File) --- 可重定位 (relocatable)
elf executable 可执行文件 --- ELF 格式

7.2 编译优化级别

级别 说明 编译时间 二进制大小 适用
-O0 无优化(默认) 最快 调试
-O1 基本优化 较快 ---
-O2 标准优化 生产环境
-O3 激进优化 不确定 性能关键
-Os 优化体积 最小 嵌入式
-Og 调试友好优化 较快 --- GDB 调试

第 08 讲:GDB 调试器实战

8.1 GDB 基本命令

命令 缩写 作用
break <location> b 设断点
run r 启动程序
next n 单步(不进入函数)
step s 单步(进入函数)
continue c 继续执行
print <expr> p 打印表达式值
backtrace bt 查看调用栈
info locals --- 查看局部变量
info registers i r 查看寄存器
quit q 退出

8.2 GDB 批处理模式

bash 复制代码
# 非交互模式调试
gdb -batch -ex "break factorial" -ex "run" \
    -ex "bt" -ex "info locals" -ex "continue" \
    ./gdb_test

8.3 核心转储 (Core Dump) 分析

bash 复制代码
# 启用 core dump
ulimit -c unlimited

# 程序崩溃后
gdb ./program core

# 常用命令
(gdb) bt full       # 完整调用栈 + 局部变量值
(gdb) frame 3       # 切换到第 4 帧
(gdb) info registers # 寄存器状态

注意 : Ubuntu 24.04 默认不安装 gdb,需 apt install gdb


第 09 讲:系统调用深入实践

9.1 strace --- 系统调用追踪

bash 复制代码
root@os-01:~# strace -c /bin/echo hello
hello
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  0.00    0.000000           0         1           read
  0.00    0.000000           0         1           write
  0.00    0.000000           0         5           close
  0.00    0.000000           0         4           fstat
  0.00    0.000000           0         9           mmap
  0.00    0.000000           0         3           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           pread64
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         3           openat
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000           0        40         1 total

一个简单的 echo hello 触发了 40 次系统调用 !其中 mmap(9次)close(5次)fstat(4次) 是运行时链接和内存初始化产生的。

strace 常用选项:

选项 作用
-c 统计每种系统调用的次数和时间
-e trace=open,read,write 只追踪指定系统调用
-p PID 附加到运行中的进程
-f 追踪子进程
-t 显示时间戳
-o file 输出到文件

9.2 常用系统调用一览

系统调用 作用 参数
open(path, flags, mode) 打开文件 路径, O_RDONLY/O_WRONLY/O_RDWR
read(fd, buf, count) 读取文件 文件描述符, 缓冲区, 大小
write(fd, buf, count) 写入文件 同上
close(fd) 关闭文件 文件描述符
fork() 创建进程
execve(path, argv, envp) 执行程序 路径, 参数, 环境变量
mmap(addr, len, prot, flags, fd, off) 内存映射 地址, 长度, 保护, 标志
brk(addr) / sbrk(inc) 调整堆大小 ---

第四部分:并发与同步机制篇

第 10 讲:线程编程基础

10.1 线程 vs 进程

复制代码
┌─────────────────────────────────┐  ┌─────────────────────────────────┐
│           进程 A                │  │           线程对比              │
│  ┌───────────────────────────┐  │  ├─────────────┬───────────────────┤
│  │   Code | Data | Files     │  │  │ 特性        │ 进程     │ 线程   │
│  ├──────────┬────────────────┤  │  ├─────────────┼──────────┼────────┤
│  │ Thread 1 │ Thread 2       │  │  │ 创建开销     │ 大       │ 小     │
│  │ (Stack)  │ (Stack)        │  │  │ 上下文切换   │ 慢       │ 快     │
│  │ (Regs)   │ (Regs)         │  │  │ 内存空间     │ 独立     │ 共享   │
│  └──────────┴────────────────┘  │  │ 通信方式     │ IPC      │ 共享变量│
│          共享地址空间            │  │ 独立性       │ 强       │ 弱     │
└─────────────────────────────────┘  └─────────────┴──────────┴────────┘

10.2 Pthread 实战

bash 复制代码
root@os-01:~# ./thread_demo
[Thread 1] ID=127008239515328 started
[Thread 2] ID=127008231122624 started
[Thread 3] ID=127008222729920 started
[Thread 1] ID=127008239515328 done
[Thread 2] ID=127008231122624 done
[Thread 3] ID=127008222729920 done
Thread 1 returned 10
Thread 2 returned 20
Thread 3 returned 30

核心 API:

函数 作用
pthread_create(&tid, attr, func, arg) 创建线程
pthread_join(tid, &retval) 等待线程结束并获取返回值
pthread_self() 获取当前线程 ID
pthread_detach(tid) 分离线程(自动回收资源)
pthread_exit(retval) 线程退出

编译: gcc -pthread 必须加 -pthread 链接 pthread 库。


第 11 讲:线程同步与互斥

11.1 Mutex 互斥锁

bash 复制代码
root@os-01:~# ./mutex_demo
Expected=2000000, Actual=2000000

两个线程各执行 100 万次 counter++,由于 Mutex 保护,最终结果准确 = 200 万。如果不加锁,由于竞态条件(Race Condition),结果通常小于预期值。

互斥锁 API:

c 复制代码
pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);   // 初始化
pthread_mutex_lock(&lock);         // 加锁 (阻塞等待)
pthread_mutex_trylock(&lock);      // 尝试加锁 (非阻塞)
pthread_mutex_unlock(&lock);       // 解锁
pthread_mutex_destroy(&lock);      // 销毁
锁类型 说明
PTHREAD_MUTEX_NORMAL 普通锁 --- 重复加锁导致死锁
PTHREAD_MUTEX_ERRORCHECK 错误检查 --- 重复加锁返回错误
PTHREAD_MUTEX_RECURSIVE 递归锁 --- 同一线程可重复加锁
PTHREAD_MUTEX_DEFAULT 默认 --- 行为未定义

11.2 读写锁 (rwlock)

操作 条件
读锁 (shared) 多个读者可同时持有
写锁 (exclusive) 写者独占,读者被阻塞
读→写升级 不支持(会死锁)
c 复制代码
pthread_rwlock_t rwlock;
pthread_rwlock_rdlock(&rwlock);   // 读锁
pthread_rwlock_wrlock(&rwlock);   // 写锁
pthread_rwlock_unlock(&rwlock);   // 解锁

第 12 讲:信号量与生产者-消费者问题

12.1 信号量概念

复制代码
  信号量 (Semaphore): 一个非负整数计数器

  sem_wait()  →  P 操作 (Proberen, 荷兰语"测试")
    if counter > 0: counter-- else: 阻塞等待

  sem_post()  →  V 操作 (Verhogen, 荷兰语"增加")
    counter++, 唤醒一个等待线程

12.2 生产者-消费者实战

bash 复制代码
root@os-01:~# ./sem_demo
Producer: made item 1 (buffer=1)
Consumer: got item 1 (buffer=0)
Producer: made item 2 (buffer=1)
Consumer: got item 2 (buffer=0)
Producer: made item 3 (buffer=1)
Consumer: got item 3 (buffer=0)
Producer: made item 4 (buffer=1)
Consumer: got item 4 (buffer=0)
Producer: made item 5 (buffer=1)
Consumer: got item 5 (buffer=0)

同步机制分析:

复制代码
        Producer                    Consumer
           │                           │
    ┌──────▼──────┐            ┌──────▼──────┐
    │ sem_wait    │            │ sem_wait    │
    │  (empty)    │            │  (full)     │
    │  buffer 有空位? │         │  buffer 有数据?│
    └──────┬──────┘            └──────┬──────┘
           │ 有空位                    │ 有数据
    ┌──────▼──────┐            ┌──────▼──────┐
    │ lock mutex  │            │ lock mutex  │
    │ buffer[count++] │        │ item = buffer[--count]│
    │ unlock mutex│            │ unlock mutex│
    └──────┬──────┘            └──────┬──────┘
           │                           │
    ┌──────▼──────┐            ┌──────▼──────┐
    │ sem_post    │            │ sem_post    │
    │  (full)     │            │  (empty)    │
    │  通知消费者  │            │  通知生产者  │
    └─────────────┘            └─────────────┘
同步原语 适用场景 特点
Mutex 临界区互斥 同一时刻仅一个线程进入
Semaphore 资源计数/信号通知 允许多个线程
Condition Variable 等待特定条件 配合 Mutex 使用
Barrier 多线程同步点 所有线程到达后才继续

死锁预防策略:

策略 方法
破坏互斥 尽量用无锁数据结构
破坏持有等待 一次性申请所有资源
破坏不可抢占 使用 trylock + 回退
破坏循环等待 统一资源获取顺序

第五部分:中断与驱动开发篇

第 13 讲:Linux 中断机制

13.1 中断类型

复制代码
                    ┌─────────────┐
  硬件中断 ←────────│     CPU     │────────→ 软件中断
  (Hardware IRQ)    │             │          (Software IRQ)
                    └─────────────┘
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
        ┌─────────┐ ┌─────────┐ ┌─────────┐
        │ 时钟中断 │ │ I/O中断  │ │ 异常    │
        │ (Timer) │ │ (Disk/  │ │ (Page   │
        │         │ │  Net)   │ │  Fault) │
        └─────────┘ └─────────┘ └─────────┘

13.2 /proc/interrupts 详细解读

bash 复制代码
root@os-01:~# head -20 /proc/interrupts
           CPU0       CPU1
  1:          9          0   IO-APIC   1-edge      i8042            # 键盘控制器
  4:       1146          0   IO-APIC   4-edge      ttyS0            # 串口
  8:          0          0   IO-APIC   8-edge      rtc0             # 实时时钟
 10:          0         98   IO-APIC  10-fasteoi   virtio2          # VirtIO 设备
 27:          0      14176  PCI-MSIX-0000:02:01.0   1-edge  virtio3-req.0  # 虚拟网卡
 29:        834        195  PCI-MSIX-0000:00:03.0   1-edge  virtio0-input.0  # 磁盘输入
 30:       1531          1  PCI-MSIX-0000:00:03.0   2-edge  virtio0-output.0 # 磁盘输出
含义
第 1 列 IRQ 编号
CPU0/CPU1 各 CPU 处理的中断次数
IO-APIC / PCI-MSIX 中断控制器类型
edge / fasteoi 中断触发方式
最后一列 设备名称

中断触发方式:

方式 说明 特点
Edge (边沿触发) 电平变化时触发一次 可能丢失中断
Level (电平触发) 高电平持续触发 不会丢失但需处理
MSI-X PCIe 消息信号中断 现代设备,支持多向量

13.3 软中断 (Softirq)

bash 复制代码
root@os-01:~# head -1 /proc/stat
softirq 139501 2 19614 3 8396 14043 0 64 25895 0 71484
#       总次数  HI  TIMER NET_TX NET_RX BLOCK IRQ_POLL TASKLET SCHED HRTIMER RCU
软中断 说明
HI / TIMER 高优先级 / 普通定时器
NET_TX / NET_RX 网络发送/接收
BLOCK 块设备 I/O
TASKLET 任务小进程
SCHED 调度器
RCU Read-Copy-Update

第 14 讲:驱动程序开发基础

14.1 驱动架构

复制代码
┌─────────────────────────────────────────────────┐
│                    Applications                  │
├─────────────────────────────────────────────────┤
│                  System Call Interface           │
├──────────┬──────────┬──────────┬────────────────┤
│ Char Dev │ Block Dev│ Net Dev  │ ...            │
│ (键盘)   │ (磁盘)   │ (网卡)   │                │
├──────────┴──────────┴──────────┴────────────────┤
│              Device Drivers (内核模块)            │
├─────────────────────────────────────────────────┤
│                  Hardware                       │
└─────────────────────────────────────────────────┘
驱动类型 设备号 访问方式 典型设备
Character (字符) c 主:次 字节流, 顺序访问 /dev/tty, /dev/null
Block (块) b 主:次 块随机访问, 缓存 /dev/vda, /dev/sda
Network (网络) 无设备文件 socket 接口 eth0, wlan0
bash 复制代码
root@os-01:~# ls -la /dev/null /dev/zero /dev/random /dev/tty
crw-rw-rw- 1 root root 1, 3 Jun  7 20:00 /dev/null    # 字符设备 1:3
crw-rw-rw- 1 root root 1, 5 Jun  7 20:00 /dev/zero    # 字符设备 1:5
crw-rw-rw- 1 root root 1, 8 Jun  7 20:00 /dev/random  # 字符设备 1:8
crw-rw-rw- 1 root tty  5, 0 Jun  7 20:00 /dev/tty     # 字符设备 5:0
  • 主设备号 (Major) 1 = Memory devices, 5 = TTY devices
  • 次设备号 (Minor) 区分同类不同设备

14.2 内核模块开发

bash 复制代码
# 确认内核版本和头文件
root@os-01:~# uname -r
6.8.0-106-generic

root@os-01:~# ls /lib/modules/
6.8.0-106-generic

编译内核模块需要 linux-headers-$(uname -r) 包。本教程云服务器无需编译自定义驱动,主要学习 /proc/sys 接口进行系统诊断。


第六部分:内存管理与性能优化篇

第 15 讲:Linux 内存管理基础

15.1 虚拟内存架构

复制代码
  Virtual Address Space              Physical Memory
  ┌─────────────────────┐            ┌─────────┐
  │       Stack ↓       │            │  Page   │
  │   (函数调用/局部变量)  │    MMU     │  Frame  │
  │                     │  ┌──────┐   │   0     │
  ├─────────────────────┤  │ Page │   ├─────────┤
  │         ↓           │  │ Table│   │  Page   │
  │    mmap 区域        │  │      │   │  Frame  │
  │   (共享库/文件映射)  │──┤      ├──→│   1     │
  ├─────────────────────┤  │      │   ├─────────┤
  │         ↑           │  │      │   │  Page   │
  │       Heap          │  │      │   │  Frame  │
  │   (malloc/new)      │  └──────┘   │   2     │
  ├─────────────────────┤            ├─────────┤
  │       BSS           │            │   ...   │
  │   (未初始化全局变量)  │            └─────────┘
  ├─────────────────────┤
  │       Data          │
  │   (已初始化全局变量)  │
  ├─────────────────────┤
  │       Text/Code     │
  │   (程序指令)         │
  └─────────────────────┘

15.2 /proc/meminfo 解读

bash 复制代码
root@os-01:~# head -10 /proc/meminfo
MemTotal:        3616292 kB    # 物理内存总量 = 3.5 GiB
MemFree:         2883432 kB    # 完全空闲内存 = 2.75 GiB
MemAvailable:    3198624 kB    # 可用内存(含可回收) = 3.05 GiB ⭐
Buffers:           30504 kB    # 块设备缓冲
Cached:           485672 kB    # 页缓存
SwapCached:            0 kB    # 换出后又在内存中的页
Active:           321696 kB    # 活跃内存
Inactive:         236132 kB    # 不活跃内存

MemFree vs MemAvailable:

指标 含义 使用建议
MemFree 完全未被使用的内存 低不一定有问题
MemAvailable 可用内存 (含可回收的 buffer/cache) 更准确反映可用性 ⭐

15.3 vmstat 虚拟内存统计

bash 复制代码
root@os-01:~# vmstat 1 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 2883432  30504 513372    0    0   427   749  187    1  1  1 98  1  0
 0  0      0 2883432  30504 513412    0    0     0     0   96  128  0  0 100 0  0
含义
r (run queue) 运行队列中的进程数
b (blocked) 等待 I/O 的进程数
swpd 已使用的 swap
si / so swap in / out
bi / bo block in / out (磁盘 I/O)
in / cs 中断数 / 上下文切换数
us / sy / id / wa CPU: 用户/系统/空闲/等待 I/O

第 16 讲:mmap() 系统调用深度实践

16.1 mmap 原理

复制代码
  read() 方式                    mmap() 方式
  ┌──────┐   copy   ┌──────┐   ┌──────┐
  │ Disk │ ───────→ │ Page │   │ Disk │ ←──────→ 直接映射
  │      │          │Cache │   │      │   page fault
  └──────┘          └──┬───┘   └──────┘
                       │ copy              ┌──────┐
                  ┌────▼───┐          ┌───→│ VMA  │
                  │  User  │          │    └──────┘
                  │ Buffer │          │ 用户直接访问
                  └────────┘          │
                      2次拷贝         1次拷贝 (零拷贝技术)

16.2 mmap 实战

bash 复制代码
root@os-01:~# ./mmap_demo
Written via mmap: Hello from mmap! This is shared memory via file mapping.
Read from file: Hello from mmap! This is shared memory via file mapping.
Anonymous mmap: anon[0]=42, anon[1]=100

mmap 参数详解:

c 复制代码
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
参数 选项 说明
addr NULL 让内核选择映射地址
length 页对齐 映射区域大小
prot PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE 访问权限
flags MAP_SHARED (共享), MAP_PRIVATE (写时复制), MAP_ANONYMOUS (无文件) 映射类型
fd 文件描述符 被映射的文件
offset 页对齐 文件偏移

16.3 /proc/PID/maps 进程内存布局

bash 复制代码
root@os-01:~# cat /proc/$$/maps | head -10
5c057f25c000-5c057f28c000 r--p 00000000 fd:01 512    /usr/bin/bash       # 代码段(只读)
5c057f28c000-5c057f37b000 r-xp 00030000 fd:01 512    /usr/bin/bash       # 代码段(可执行)
5c057f37b000-5c057f3b0000 r--p 0011f000 fd:01 512    /usr/bin/bash       # 只读数据
5c057f3b0000-5c057f3b4000 r--p 00154000 fd:01 512    /usr/bin/bash       # 只读数据
5c057f3b4000-5c057f3bd000 rw-p 00158000 fd:01 512    /usr/bin/bash       # 可读写数据
5c057f3bd000-5c057f3c8000 rw-p 00000000 00:00 0                          # 匿名映射 (BSS)
5c05b8f88000-5c05b8fa9000 rw-p 00000000 00:00 0      [heap]              # 堆
73e639600000-73e639628000 r--p 00000000 fd:01 39295   libc.so.6          # libc 代码
73e639628000-73e6397b0000 r-xp 00028000 fd:01 39295   libc.so.6          # libc 可执行
权限位 含义
r 可读 (Read)
w 可写 (Write)
x 可执行 (eXecute)
p 私有 (Private) --- 写时复制 (Copy-on-Write)
s 共享 (Shared)

第 17 讲:内存检测与性能优化

17.1 内存泄漏示例

bash 复制代码
root@os-01:~# ./leak_demo
This memory is leaked!
This memory is leaked!
This memory is leaked!
Done - check with valgrind

上述程序每次调用 func_with_leak() 分配 1KB 但不释放,3 次调用泄漏 3KB

17.2 内存检测工具对比

工具 用途 开销 使用方法
Valgrind 内存泄漏/错误检测 10-50x 慢 valgrind --leak-check=full ./prog
AddressSanitizer (ASan) 编译期插桩检测 2x 慢 gcc -fsanitize=address -g prog.c
perf 性能分析 极小 perf record ./prog; perf report
pmap 进程内存映射 极小 pmap -x PID
slabtop 内核 slab 分配器 极小 slabtop

17.3 Buddy 分配器与 Slab 分配器

bash 复制代码
root@os-01:~# cat /proc/buddyinfo
Node 0, zone      DMA      0  0  0  0  0  0  0  1  0  1  3
Node 0, zone    DMA32      5  4  3  3  2  1  2  4  4  4  630
Node 0, zone   Normal      2 871 542 387 345 243 142 86 47 10  8

Buddy System (伙伴系统): 管理物理页框,按 2^n 个连续页分组。

  • 第 N 列 = 2^N 页的空闲块数量
  • 例如 Normal zone 有 871 个 2^1=2页 (8KB) 的空闲块
bash 复制代码
root@os-01:~# cat /proc/slabinfo | head -5
slabinfo - version: 2.1
# name         <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>
QIPCRTR               19     19    832   19    4
ext4_groupinfo_4k    352    352    184   22    1

Slab 分配器: 在内核中缓存常用数据结构对象,避免频繁分配/释放。


第七部分:文件系统与链接库篇

第 18 讲:Linux 文件系统深入

18.1 文件系统类型

bash 复制代码
root@os-01:~# df -T | head -6
Filesystem     Type  1K-blocks    Used Available Use% Mounted on
tmpfs          tmpfs    361632    1044    360588   1% /run
/dev/vda1      ext4   41169244 3469444  35900960   9% /
tmpfs          tmpfs   1808144       0   1808144   0% /dev/shm
tmpfs          tmpfs      5120       0      5120   0% /run/lock
tmpfs          tmpfs    361628      12    361616   1% /run/user/0
文件系统 类型 特点
ext4 磁盘文件系统 稳定、成熟、Linux 标配
tmpfs 内存文件系统 速度快、重启丢失
devtmpfs 设备文件系统 内核自动管理 /dev
proc 伪文件系统 进程/内核信息

18.2 inode 详解

bash 复制代码
root@os-01:~# stat /etc/passwd
  File: /etc/passwd
  Size: 1863            Blocks: 8          IO Block: 4096   regular file
Device: 253,1           Inode: 395285      Links: 1
Access: (0644/-rw-r--r--)  Uid: (0/root)   Gid: (0/root)
Access: 2026-06-07 20:00:37.746999448 +0800    # atime: 最后访问时间
Modify: 2026-03-30 11:49:53.543330565 +0800    # mtime: 最后修改时间
Change: 2026-03-30 11:49:53.546330565 +0800    # ctime: 最后状态变更时间
 Birth: 2026-03-30 11:49:53.543330565 +0800    # 创建时间 (ext4+)

inode 包含的信息:

  • 文件类型和权限 (mode)
  • 所有者 (UID/GID)
  • 大小 (size)
  • 时间戳 (atime, mtime, ctime)
  • 数据块指针 (block pointers)
  • 链接数 (links count)

不包含: 文件名!文件名存储在目录的目录项 (dentry) 中。

18.3 VFS (Virtual File System)

复制代码
  用户程序
     │     read() / write() / open() / close()
     ▼
┌─────────────────────────────────────────────────┐
│              VFS (Virtual File System)           │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │ Super   │  │  Inode  │  │  File   │         │
│  │ Block   │  │  Object │  │  Object │  ...    │
│  └────┬────┘  └────┬────┘  └────┬────┘         │
├───────┼────────────┼────────────┼───────────────┤
│       ▼            ▼            ▼               │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │  ext4   │  │  xfs    │  │  nfs    │  ...    │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘

18.4 支持的文件系统列表

bash 复制代码
root@os-01:~# cat /proc/filesystems | head -15
nodev   sysfs
nodev   tmpfs
nodev   proc
nodev   cgroup
nodev   cgroup2
nodev   devtmpfs
nodev   configfs
nodev   debugfs
nodev   tracefs
nodev   securityfs
nodev   sockfs
nodev   bpf
nodev   pipefs

nodev 表示不需要底层块设备(伪文件系统 / 内核文件系统)。


第 19 讲:静态库与动态库

19.1 编译链接流程

复制代码
  mymath.c                     main.c
     │                            │
     ▼ gcc -c                    ▼ gcc -c
  mymath.o          →→→        main.o
     │                            │
     ├── ar rcs                   │
     ├→ libmymath.a ── 静态链接 ──┤→ main_static (785KB)
     │                            │
     ├── gcc -shared              │
     └→ libmymath.so ─ 动态链接 ──┤→ main_dynamic (16KB)

真实对比:

bash 复制代码
root@os-01:~# ls -la libmymath.a main_static
libmymath.a:   1498 bytes
main_static: 785400 bytes   # 静态链接 → 很大!

root@os-01:~# ls -la libmymath.so main_dynamic
libmymath.so: 15144 bytes
main_dynamic: 16024 bytes   # 动态链接 → 小很多!

# 运行结果相同
root@os-01:~# ./main_static && LD_LIBRARY_PATH=. ./main_dynamic
add(10,20)=30, multiply(10,20)=200
add(10,20)=30, multiply(10,20)=200

19.2 静态库 vs 动态库

对比维度 静态库 (.a) 动态库 (.so)
生成命令 ar rcs gcc -shared -fPIC
链接时机 编译时链接 运行时加载
二进制大小 (785KB) (16KB)
部署方式 自包含,无外部依赖 需要库文件存在
库更新 需重新编译 替换 .so 即可
内存共享 每个进程独立副本 多进程共享同一物理页
符号冲突 编译时报错 运行时可能出错 (DLL Hell)

19.3 ldd --- 查看动态库依赖

bash 复制代码
root@os-01:~# ldd ./main_dynamic
        linux-vdso.so.1 (0x00007ffca1b58000)    # 内核虚拟动态库
        libmymath.so => not found                # 自定义库(路径未配置)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6  # C 标准库
        /lib64/ld-linux-x86-64.so.2              # 动态链接器

为什么 libmymath.so 显示 "not found":

  • LD_LIBRARY_PATH=. 仅对运行时有效
  • ldd 静态分析时不读取环境变量
  • 配置方法: export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH

19.4 fPIC 的含义

-fPIC = Position Independent Code (位置无关代码)

  • 动态库可能被加载到任意虚拟地址
  • PIC 代码使用 GOT (Global Offset Table) 和 PLT (Procedure Linkage Table) 进行地址跳转
  • 静态库不需要 -fPIC,因为链接时地址已确定

第八部分:死锁分析与调试工具篇

第 20 讲:死锁现象与理论

20.1 死锁四必要条件

复制代码
┌───────────────────────────────────────────────────────────┐
│              死锁的四个必要条件 (Coffman, 1971)              │
├───────────────────────────────────────────────────────────┤
│                                                            │
│  ① 互斥条件 (Mutual Exclusion)                             │
│     资源只能被一个进程独占                                  │
│                                                            │
│  ② 持有并等待 (Hold and Wait)                              │
│     进程持有资源 A,同时等待资源 B                          │
│                                                            │
│  ③ 不可抢占 (No Preemption)                                │
│     资源不能被强制剥夺,只能主动释放                        │
│                                                            │
│  ④ 循环等待 (Circular Wait)                                │
│     P1 等 P2, P2 等 P3, ... , Pn 等 P1                    │
│                                                            │
│  四个条件同时满足 → 死锁发生!                              │
│  破坏任意一个条件 → 预防死锁                                │
└───────────────────────────────────────────────────────────┘

20.2 死锁复现实战

c 复制代码
// T1: lock A → lock B    T2: lock B → lock A  (顺序相反!)

控制流程图:

复制代码
    Thread 1              Thread 2
       │                     │
  lock(A) ✓              lock(B) ✓
       │                     │
   sleep(1)              sleep(1)
       │                     │
  lock(B) ✗              lock(A) ✗
  [等待 T2 释放 B]        [等待 T1 释放 A]
       │                     │
       └───── 互相等待 ──────┘
              ↑ DEADLOCK ↑

20.3 死锁预防策略

策略 破坏条件 具体方法
一次性分配 ② 持有等待 同时申请所有锁
资源排序 ④ 循环等待 所有线程按相同顺序获取锁
trylock + 回退 ②③ 获取失败则释放已有锁,稍后重试
超时机制 pthread_mutex_timedlock()

第 21 讲:死锁预防实战

21.1 trylock 解决方案

bash 复制代码
root@os-01:~# ./deadlock_fix
=== Deadlock Prevention: trylock ===
T1: lock A
T1: try lock B...
T1: locked B, working...
T2: try lock B...
T2: locked B, try lock A...
Both threads completed without deadlock!

trylock 代码逻辑:

复制代码
 T1 获取 A → trylock B → 成功 → 正常工作
                         → 失败 → 释放 A → 等待 → 重试

 T2 获取 B → trylock A → 成功 → 正常工作
                         → 失败 → 释放 B → 等待 → 重试

21.2 资源排序策略

c 复制代码
// ❌ 危险: 不同顺序
void transfer(Account* from, Account* to, int amount) {
    lock(from);  // 先锁 from
    lock(to);    // 再锁 to
}
// 两个并发 transfer(A,B) 和 transfer(B,A) → 死锁!

// ✅ 安全: 固定顺序
void transfer(Account* a, Account* b, int amount) {
    if (a->id < b->id) { lock(a); lock(b); }
    else               { lock(b); lock(a); }
}
// 永远按 ID 升序获取锁 → 无循环等待

第 22 讲:银行家算法

22.1 算法原理

银行家算法 (Banker's Algorithm, Dijkstra 1965) 是死锁避免的经典算法:

复制代码
核心思想: 当进程请求资源时,系统先"假装"分配,然后检查是否
仍然存在安全序列 (Safe Sequence)。如果安全,真正分配;否则拒绝。

安全状态: 存在一个执行顺序,使得所有进程都能获得所需资源并完成。

22.2 C 语言实现验证

bash 复制代码
root@os-01:~# ./banker
=== Banker Algorithm ===
Available: [3,3,2]
P0: alloc=[0,1,0] max=[7,5,3]
P1: alloc=[2,0,0] max=[3,2,2]
P2: alloc=[3,0,2] max=[9,0,2]
P3: alloc=[2,1,1] max=[2,2,2]
P4: alloc=[0,0,2] max=[4,3,3]
System is SAFE. Sequence: P1 P3 P4 P0 P2

安全序列验证:

步骤 选中进程 释放资源后 Available 说明
1 P1 5,3,2 P1 只需 1,2,2, 现有 3,3,2 → 可满足
2 P3 7,4,3 P3 只需 0,1,1, 现有 5,3,2 → 可满足
3 P4 7,4,5 P4 只需 4,3,1, 现有 7,4,3 → 可满足
4 P0 7,5,5 P0 只需 7,4,3, 现有 7,4,5 → 可满足
5 P2 10,5,7 P2 只需 6,0,0, 现有 7,5,5 → 可满足

所有进程都能按此顺序完成 → 系统处于安全状态


第九部分:综合项目实战篇

第 23 讲:项目一 --- 简易 Shell 实现

23.1 需求分析

复制代码
  用户输入 → 解析命令 → fork() → execvp() → wait()
                │                      │
          支持: 简单命令            子进程执行
                exit 退出            父进程等待

23.2 完整代码

核心实现 (~40 行):

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CMD 256
#define MAX_ARGS 32

int main() {
    char cmd[MAX_CMD];
    printf("Simple Shell v1.0 - type exit to quit\n");
    while (1) {
        printf("mysh> "); fflush(stdout);
        if (!fgets(cmd, MAX_CMD, stdin)) break;
        cmd[strcspn(cmd, "\n")] = 0;
        if (strlen(cmd) == 0) continue;
        if (strcmp(cmd, "exit") == 0) break;

        // 解析命令为参数数组
        char *args[MAX_ARGS]; int argc = 0;
        char *token = strtok(cmd, " ");
        while (token && argc < MAX_ARGS-1) {
            args[argc++] = token;
            token = strtok(NULL, " ");
        }
        args[argc] = NULL;

        pid_t pid = fork();
        if (pid == 0) {
            execvp(args[0], args);           // 子进程: 执行命令
            printf("mysh: command not found: %s\n", args[0]);
            exit(1);
        } else if (pid > 0) {
            int status;
            waitpid(pid, &status, 0);        // 父进程: 等待子进程
        }
    }
    printf("Goodbye!\n");
    return 0;
}

23.3 运行测试

bash 复制代码
root@os-01:~# echo -e "whoami\necho Hello from myshell\nwho\nexit" | ./myshell
Simple Shell v1.0 - type exit to quit
mysh> root
mysh> Hello from myshell
mysh> mysh> Goodbye!

技术要点:

函数 作用 要点
fgets() 读取一行 包含 \n,需用 strcspn 去除
strtok() 分割字符串 会修改原字符串,非线程安全
fork() 创建子进程 子进程返回 0,父进程返回子 PID
execvp() 执行命令 v=vector 参数, p=PATH 搜索
waitpid() 等待子进程 防止僵尸进程

23.4 进阶功能

功能 实现方法 难度
管道 `cmd1 cmd2` pipe() + dup2() + 两个 fork()
重定向 > < open() + dup2() 替换 fd 0/1 ⭐⭐
后台运行 cmd & 不调用 waitpid()
内建命令 cd 直接 chdir() (不能 fork)
信号处理 Ctrl+C signal(SIGINT, handler) ⭐⭐

第 24 讲:项目二 --- 多线程 Web 服务器

24.1 架构设计

复制代码
                    ┌───────────┐
         请求       │  主线程    │
  Client ──────────→│ accept()  │
                    └─────┬─────┘
                          │ 新连接
              ┌───────────┼───────────┐
              ▼           ▼           ▼
        ┌─────────┐ ┌─────────┐ ┌─────────┐
        │Worker 1 │ │Worker 2 │ │Worker N │    线程池
        │ 处理请求 │ │ 处理请求 │ │ 处理请求 │
        └─────────┘ └─────────┘ └─────────┘

24.2 运行测试

bash 复制代码
root@os-01:~# python3 /tmp/simple_http.py 8888 &
HTTP Server listening on port 8888...

root@os-01:~# curl -s http://localhost:8888/
<h1>Simple HTTP Server</h1><p>Running on port 8888</p>

root@os-01:~# curl -s http://localhost:8888/nonexistent
<h1>404</h1>

24.3 技术要点

组件 Python 实现 说明
Socket 创建 socket.socket(AF_INET, SOCK_STREAM) TCP socket
端口复用 setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 避免 "Address in use"
多线程 threading.Thread(target=handler, daemon=True) 每连接一线程
HTTP 解析 字符串 split 简易实现
MIME 类型 扩展名映射字典 支持 html/txt/json

第 25 讲:项目三 --- 调试工具链与故障诊断

25.1 完整调试工具链

复制代码
  问题发生
     │
     ├── 应用层
     │   ├── strace ./prog        系统调用级别追踪
     │   ├── ltrace ./prog        库函数调用追踪
     │   ├── gdb ./prog core      核心转储分析
     │   └── valgrind ./prog      内存泄漏检测
     │
     ├── 系统层
     │   ├── dmesg -T             内核日志
     │   ├── top / htop           实时监控
     │   ├── vmstat / iostat      资源统计
     │   ├── lsof -p PID          打开文件列表
     │   └── netstat -tlnp        网络连接状态
     │
     └── 硬件层
         ├── /proc/interrupts     中断统计
         ├── /proc/meminfo        内存详情
         └── sensors              硬件温度/电压

25.2 故障诊断流程

复制代码
  Step 1: 确认现象
    ├── 进程退出? → 检查退出码 / core dump
    ├── 性能慢? → perf / top / vmstat
    ├── 内存高? → smaps / valgrind / pmap
    └── 网络异常? → netstat / ss / tcpdump

  Step 2: 缩小范围
    ├── 最近改了什么? → git log / diff
    ├── 哪个组件出问题? → 逐层排除
    └── 能复现吗? → 构造最小复现用例

  Step 3: 定位根因
    ├── 加日志/断点 → 精确到代码行
    ├── strace 对比正常/异常行为
    └── 二分法定位引入点 (git bisect)

  Step 4: 修复验证
    ├── 修复代码 → 单元测试
    ├── 回归测试 → 确认无副作用
    └── 监控上线 → 观察指标

附录

附录 A:开发环境配置指南

Ubuntu 24.04 开箱配置

bash 复制代码
# 基础开发包
apt update && apt install -y \
  build-essential gcc g++ gdb make cmake \
  git vim curl wget \
  valgrind strace ltrace perf-tools-unstable \
  python3 python3-pip python3-venv

# 内核开发
apt install -y linux-headers-$(uname -r)

# 代码规范检查
apt install -y clang-format cppcheck

实验环境验证 Checklist

bash 复制代码
# 编译环境
gcc --version          # ✅ GCC 13.x
gdb --version          # ✅ (需安装)
g++ --version          # ✅ G++ 13.x

[OK]   内核版本: 6.8.0-106-generic
[OK]   架构: x86_64
[OK]   内存: 3.5 GiB
[OK]   磁盘: 40 GiB (ext4)

附录 B:代码规范与最佳实践

C 语言编码规范

规则 示例
变量声明在函数顶部 int i; int count = 0;
检查返回值 if ((fd = open(...)) < 0) { perror("open"); return -1; }
释放资源 使用 goto cleanup 模式或 RAII
避免 magic number #define MAX_BUFFER 4096
编译警告全开 gcc -Wall -Wextra -Werror

安全编程要点

  1. 缓冲区溢出 : 用 strncpy / snprintf 替代 strcpy / sprintf
  2. 整数溢出: 检查运算前后值
  3. Use-After-Free: free 后将指针置 NULL
  4. TOCTOU : 用 fstat() + openat() 避免竞态
  5. 格式字符串: 不将用户输入作为 printf 的 format 参数

附录 C:参考资料

资源 说明
The Linux Programming Interface (Michael Kerrisk) Linux 系统编程圣经
Advanced Programming in the UNIX Environment (Stevens) APUE 经典
man-pages (man 2 open, man 3 pthread) 最权威参考
https://www.kernel.org/doc/ Linux 内核官方文档
https://elixir.bootlin.com/linux/latest/source 内核源码在线浏览

附录 D:常见问题 FAQ

问题 答案
fork() 后子进程为什么继承了父进程的缓冲区? printf 使用行缓冲/全缓冲, fork 复制了整个地址空间包括 stdio 缓冲区
wait()waitpid() 的区别? wait 等待任意子进程, waitpid 可指定 PID + 支持非阻塞 (WNOHANG)
静态链接为什么文件那么大? glibc 完整静态链接会将所有用到的 libc 函数都嵌入二进制
为什么 LD_LIBRARY_PATH=. 不安全? 可能加载恶意 .so 文件; 生产环境应用 rpath/etc/ld.so.conf
僵尸进程怎么清理? 父进程调用 wait() 回收; 若父进程已退出, init(PID=1) 自动收养
mmap vs read 什么时候用哪个? 频繁随机访问 → mmap; 顺序读取一次性处理 → read
pthread_mutex vs spinlock? 临界区短 → spinlock; 临界区长 → mutex (避免 CPU 空转)

本教程基于以下环境验证通过:

  • 服务器集群: ecs-bc5b (华为云)
  • 节点: os-01 (159.138.23.92)
  • 操作系统: Ubuntu 24.04.2 LTS
  • 内核版本: 6.8.0-106-generic x86_64
  • 全部命令输出来自真实服务器,保证可复现

系列进度: 第 01-25 讲全部完成 (9 个部分, 完整覆盖操作系统核心知识点)

相关推荐
旺王雪饼 www1 小时前
localStorage 和 sessionStorage区别与联系
服务器·前端·javascript
齐潇宇1 小时前
Redis数据库基础
linux·数据库·redis·缓存
xixingzhe21 小时前
AI运维注意点
运维·人工智能
嵌入式学习和实践1 小时前
Ubuntu 系统 socat 详细介绍与使用教程 - 映射任意两种数据通道
linux·ubuntu·虚拟串口·数据映射·socat
大树881 小时前
PUE 超 1.35 要多交多少?存量机房液冷改造 3 张算账表
大数据·运维·服务器·人工智能
小此方1 小时前
Re:Linux系统篇(二十八)文件篇·一:理解 Linux 文件基础I/O、Linux 文件操作与系统调用机制
linux·运维·服务器
likerhood2 小时前
Linux 服务器基础资源查看:CPU、GPU、内存、磁盘与一键检测脚本
linux·运维·服务器
极客先躯2 小时前
高级java每日一道面试题-2026年01月19日-实战篇[Docker]-如何配置镜像仓库的垃圾回收 (GC)?
java·运维·docker·容器
AOwhisky2 小时前
学习自测与解析:MySQL 系列第三期与第四期
linux·运维·数据库·学习·mysql·云计算