操作系统与 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 个部分, 完整覆盖操作系统核心知识点)

相关推荐
yichengerp4 小时前
中小电子厂用什么ERP?电子厂ERP管理软件实施要点与易呈方案推荐
大数据·运维·人工智能·云计算·制造
励志师code4 小时前
RoCE 网络实战:从 NCCL 调优到 RDMA 硬件计数器排障
linux·运维·网络·云计算
水饺编程4 小时前
第2章:Linux 基础,pwd 命令
linux·c语言·汇编
鱼很腾apoc4 小时前
【MySQL】 第1期 从核心概念到表约束全解析
linux·c语言·数据库·学习·mysql
程序员-Benothing4 小时前
Linux进程优先级与调度:nice、renice、CPU亲和性实战
linux·运维·服务器
程序员-Benothing4 小时前
Linux进程前后台管理:jobs、fg、bg、nohup、disown
linux·运维·服务器
JavaEdge.4 小时前
Docker Compose部署Dify
运维·docker·容器
企业解惑小助手4 小时前
大型企业终端如何统一管理排障实录:批量终端策略不生效问题排查
运维·服务器·网络·域智盾软件
luo_guibin4 小时前
almalinux9新系统常规命令操作
运维·服务器
进击的荆棘5 小时前
Linux系统——库的原理与制作之ELF文件的形成与加载的过程(下)
linux·动静态库