Linux 操作系统原理剖析

Linux 操作系统原理剖析

实验环境 : ecs-57c4-0004 (华为云香港 FlexusX, 8vCPU/16GiB, Ubuntu 24.04, Linux 6.8, gcc 13.3.0)

文档版本 : v1.0 | 创建日期 : 2026-06-17

涵盖实验: OS概述 · 进程控制 · 管道IPC · 消息队列 · 信号 · 信号量 · 共享内存 · 线程 · 死锁 · 内存管理 · 文件系统 · 设备网络


目录

  1. 操作系统概述
  2. [Linux 内核及进程](#Linux 内核及进程)
  3. 进程间通信---管道
  4. 进程间通信---消息队列
  5. 进程间通信---信号
  6. 进程间通信---信号量
  7. 进程间通信---共享内存
  8. [Linux 线程概述](#Linux 线程概述)
  9. 处理机调度与死锁
  10. [Linux 内存管理](#Linux 内存管理)
  11. [Linux 文件系统](#Linux 文件系统)
  12. [Linux 设备与网络](#Linux 设备与网络)

环境信息

bash 复制代码
$ uname -a
Linux ecs-57c4-0004 6.8.0-106-generic #106-Ubuntu SMP PREEMPT_DYNAMIC
  Fri Mar 6 07:58:08 UTC 2026 x86_64 GNU/Linux

$ cat /etc/os-release | grep PRETTY
PRETTY_NAME="Ubuntu 24.04.4 LTS"

$ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0

$ free -m | head -2
              total   used   free   shared  buff/cache  available
Mem:          15131    538   13931     2        942      14593

$ getconf _NPROCESSORS_ONLN
8
复制代码
系统参数:
  pid_max:   4,194,304
  msgmax:    8,192 bytes
  shmmax:    18,446,744,073,692,774,399 bytes (≈16 EB)
  sem:       32,000 arrays / 1,024,000,000 semaphores / 500 ops / 32,000 undos

1. 操作系统概述

1.1 操作系统功能

复制代码
  ┌───────────────────────────────────────────────┐
  │               用户程序 (User)                  │
  ├───────────────────────────────────────────────┤
  │              系统调用接口 (syscall)             │
  ├──────┬──────┬──────┬──────┬──────┬────────────┤
  │ 进程  │ 内存  │ 文件  │ 设备  │ 网络  │  安全     │
  │ 管理  │ 管理  │ 系统  │ 管理  │ 协议  │  机制     │
  ├──────┴──────┴──────┴──────┴──────┴────────────┤
  │              硬件抽象层 (HAL)                  │
  ├───────────────────────────────────────────────┤
  │         CPU | 内存 | 磁盘 | 网卡 | ...          │
  └───────────────────────────────────────────────┘

五大核心功能:进程管理、内存管理、文件系统、设备管理、网络协议栈。

1.2 操作系统分类

类型 特点 代表
批处理 作业批量处理,无交互 IBM z/OS
分时系统 多用户分时共享,交互式 Unix/Linux
实时系统 严格时限,确定性响应 RT-Linux, VxWorks, QNX
网络OS 网络资源共享 Windows Server
分布式 多机协同,透明 Kubernetes 集群

1.3 Linux 发行版信息(实验环境)

复制代码
$ cat /etc/os-release | grep -E '^PRETTY|^VERSION='
PRETTY_NAME="Ubuntu 24.04.4 LTS"
VERSION="24.04.4 LTS (Noble Numbat)"

$ cat /proc/version
Linux version 6.8.0-106-generic
(gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0, GNU Binutils 2.42)
#106-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar  6 07:58:08 UTC 2026

$ uptime
22:36:29 up 40 min, 2 users, load average: 0.00, 0.00, 0.00

1.4 Linux 内核模块

复制代码
$ lsmod | head -10
Module                  Size  Used by
tls                   155648  0
qrtr                   53248  2
intel_rapl_msr         20480  0
joydev                 32768  0
i2c_piix4              32768  0
intel_rapl_common      40960  1 intel_rapl_msr
input_leds             12288  0
serio_raw              20480  0
mac_hid                12288  0

Linux 采用宏内核架构,文件系统、网络协议栈、设备驱动均运行在内核空间,通过可加载模块 (LKM) 动态扩展。


2. Linux 内核及进程

2.1 fork:创建子进程

fork() 是 Linux 创建进程的唯一方式------克隆当前进程,返回 2 次(父进程返回子进程 PID,子进程返回 0)。

c 复制代码
pid_t pid = fork();
if (pid == 0) {
    printf("[子进程] PID=%d  PPID=%d\n", getpid(), getppid());
    exit(0);
} else {
    printf("[父进程] PID=%d  fork()返回=%d (子进程PID)\n", getpid(), pid);
    wait(NULL);
}

实操输出:

复制代码
  [子进程] PID=12825  PPID=12824
  [父进程] PID=12824  fork()返回=12825 (子进程PID)

2.2 父子进程地址空间独立

复制代码
  [子进程] 初始: x=100
  [子进程] 修改: x=200
  [子进程] &x=0x7ffe0147d7a4
  [父进程] 初始: x=100
  [父进程] 子进程结束后: x=100 (未变!)
  [父进程] &x=0x7ffe0147d7a4

关键发现 : 虽然 &x 地址相同,但分属不同虚拟地址空间。Linux 采用 COW (Copy-On-Write) 优化------fork 时页表共享,仅在写入时才复制物理页。

2.3 exec:替换进程映像

c 复制代码
execl("/bin/uname", "uname", "-a", NULL);

输出:

复制代码
Linux ecs-57c4-0004 6.8.0-106-generic ... x86_64 GNU/Linux

exec 系列函数不创建新进程,而是用新程序替换当前进程的代码段、数据段、堆和栈。PID 不变。

2.4 孤儿进程与僵尸进程

复制代码
  [子进程] PID=12828  PPID=12824 (将成为孤儿)
  父进程退出...
  [子进程] PID=12828  新 PPID=1 (被 init 收养)
类型 成因 现象 处理
孤儿进程 父进程先于子进程退出 被 init (PID=1) 收养 自动处理
僵尸进程 子进程退出但父进程未 wait 保留在进程表,占用 PID wait() 或 SIGCHLD→SIG_IGN

2.5 进程信息(实操)

复制代码
  PID=12824  PPID=12823  PGRP=12823  SID=12823
  UID=0  GID=0

  /proc/self/status 摘要:
  Name:    grep
  State:    R (running)
  VmSize:    6684 kB
  VmRSS:    2468 kB
  Threads:    1

  ulimit -a (前5行):
  time(seconds)        unlimited
  file(blocks)         unlimited
  data(kbytes)         unlimited
  stack(kbytes)        8192

进程状态统计:

复制代码
$ ps -eo stat | sort | uniq -c
  I:   31    (Idle 内核线程)
  I<:  50    (高优先级空闲)
  R:    1    (Running/运行中)
  S:   63    (Sleeping/可中断睡眠)
  Sl:   2    (Sleeping+多线程)
  Ss:  12    (Session leader+Sleeping)
  Ss+:  2    (前台会话)
  Ssl:  9    (多线程+Session leader)

Linux 进程状态: R (运行/就绪), S (可中断睡眠), D (不可中断睡眠), Z (僵尸), T (停止), I(空闲)


3. 进程间通信---管道

3.1 匿名管道 (pipe)

管道是 Linux 最古老的 IPC 机制,单向、字节流、仅限父子/兄弟进程。

c 复制代码
int fd[2];
pipe(fd);  // fd[0]=读端, fd[1]=写端

// 子进程 → 父进程
pid_t pid = fork();
if (pid == 0) {
    close(fd[0]);
    write(fd[1], "Hello from child via pipe!", 26);
    close(fd[1]);
    exit(0);
} else {
    close(fd[1]);
    char buf[256];
    read(fd[0], buf, sizeof(buf));
    printf("[父进程] 从管道读取: \"%s\"\n", buf);
    close(fd[0]);
    wait(NULL);
}

实操输出:

复制代码
  [子进程] 写入管道: "Hello from child via pipe!"
  [父进程] 从管道读取: "Hello from child via pipe!"

管道限制:

系统参数 本机值 说明
PIPE_BUF 4096 bytes 原子写上限
/proc/sys/fs/pipe-max-size 1MB 管道缓冲区最大值

3.2 双向通信(2 个管道)

管道是单向的,双向通信需要 2 个管道:

复制代码
  parent → child:  p2c[0] ← p2c[1]
  child → parent:  c2p[0] ← c2p[1]

实操输出:

复制代码
  [父进程] 发送: "PING"
  [子进程] 收到: "PING"
  [父进程] 收到: "PONG"

3.3 命名管道 (FIFO)

命名管道有文件系统入口,无关进程也可通信。

c 复制代码
mkfifo("/tmp/test_fifo_os", 0666);

// Writer
int fd = open("/tmp/test_fifo_os", O_WRONLY);
write(fd, "Data via FIFO!", 14);

// Reader
int fd = open("/tmp/test_fifo_os", O_RDONLY);
read(fd, buf, sizeof(buf));

实操输出:

复制代码
$ ls -la /tmp/test_fifo_os
prw-r--r--  /tmp/test_fifo_os         ← 'p' 表示 FIFO

  FIFO 已创建: /tmp/test_fifo_os
  [Writer] 写入: "Data via FIFO!"
  [Reader] 读取: "Data via FIFO!"

pipe vs FIFO:

维度 匿名管道 (pipe) 命名管道 (FIFO)
创建方式 pipe(fd) mkfifo(path, mode)
可见性 仅 fd 文件系统可 ls
通信范围 父子/兄弟进程 任意进程
生命周期 随进程 随文件系统 (需 unlink)
类型标识 prw-r--r-- (ls 显示 p)

4. 进程间通信---消息队列

4.1 System V 消息队列原理

消息队列提供带类型标签的异步消息传递,支持按类型选择性接收。

复制代码
  ┌─────────────┐     ┌───────────────────┐     ┌─────────────┐
  │  Sender     │ ──→ │  msgget() 队列   │ ──→ │  Receiver   │
  │  msgsnd()   │     │  ┌───┬───┬───┐  │     │  msgrcv()   │
  └─────────────┘     │  │m1 │m2 │m3 │  │     └─────────────┘
                      │  └───┴───┴───┘  │
                      └───────────────────┘
                        mtype=1: LOGIN
                        mtype=2: ALERT (高优先级, 可先收)
                        mtype=3: DATA

4.2 核心 API

c 复制代码
#include <sys/msg.h>

struct msgbuf { long mtype; char mtext[256]; };

key_t key = ftok("/tmp", 'M');
int msqid = msgget(key, IPC_CREAT | 0666);

// 发送
msgsnd(msqid, &msg, strlen(msg.mtext) + 1, 0);

// 接收 (指定类型)
msgrcv(msqid, &rcv, sizeof(rcv.mtext), 2, 0);  // 仅收 mtype=2
msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0);  // 收任意类型

msgctl(msqid, IPC_RMID, NULL);  // 删除队列

4.3 实操输出

复制代码
  msgget: key=0x4d016002  msqid=0

  [Sender] mtype=1: "LOGIN: user=root"
  [Sender] mtype=2: "ALERT: disk 90% full"
  [Sender] mtype=3: "DATA: temp=25.6C"

  --- 按类型接收 (先收 mtype=2 高优先级) ---
  [Receiver] mtype=2: "ALERT: disk 90% full"    ← 优先处理
  [Receiver] mtype=3: "DATA: temp=25.6C"
  [Receiver] mtype=1: "LOGIN: user=root"

  msgctl(IPC_RMID): 消息队列已删除

查看队列:

复制代码
$ ipcs -q
key        msqid  owner  perms  used-bytes  messages
0x4d016002 0      root   666    0           0

消息队列 vs 管道:

维度 管道 消息队列
数据边界 字节流 (无边界) 有边界 (每条消息独立)
优先级 mtype 选择性接收
生命周期 随进程 随内核 (重启才清除)
容量限制 PIPE_BUF (4KB) msgmax (8KB)/msgmnb (16KB)

5. 进程间通信---信号

5.1 信号概述

信号是异步通知机制------内核或进程向目标进程发送一个整数(信号编号),触发预定义或自定义处理器。

复制代码
  发送方 ── kill(pid, SIGUSR1) ──→ 接收方
                                     │
  ┌──────────────────────────────────┼──────────────────────────────────┐
  │ 默认动作: Term/Ign/Core/Stop      │  自定义: signal()/sigaction()    │
  └──────────────────────────────────┴──────────────────────────────────┘

5.2 常用信号列表

信号 编号 默认动作 说明
SIGHUP 1 Term 终端挂断
SIGINT 2 Term Ctrl+C
SIGQUIT 3 Core Ctrl+\
SIGKILL 9 Term 不可捕获/忽略
SIGTERM 15 Term 优雅终止
SIGUSR1 10 Term 用户自定义1
SIGUSR2 12 Term 用户自定义2
SIGALRM 14 Term alarm() 定时器
SIGCHLD 17 Ign 子进程状态变化
SIGSTOP 19 Stop 不可捕获

5.3 signal() 基础注册

c 复制代码
signal(SIGINT,  sigint_handler);
signal(SIGUSR1, sigusr1_handler);
signal(SIGALRM, sigalrm_handler);

5.4 kill/raise/alarm 实操

复制代码
  PID=12955

  raise(SIGUSR1) → 给自己发信号:
  [处理器] 收到 SIGUSR1 (用户自定义), 累计=1

  alarm(2) → 2 秒后触发 SIGALRM...
  [处理器] alarm(2) 触发 SIGALRM!
  alarm_fired=1

5.5 父子进程信号通信

复制代码
  [父进程] 发送 SIGUSR1 → 子进程
  [子进程 PID=12957] 等待父进程信号...
  [处理器] 收到 SIGUSR1 (用户自定义), 累计=2
  [子进程] 收到信号, 回复父进程

5.6 sigaction --- 高级信号处理

c 复制代码
struct sigaction sa = {0};
sa.sa_sigaction = sigact_handler;
sa.sa_flags = SA_SIGINFO;       // 获取发送者信息
sigaction(SIGUSR2, &sa, NULL);

实操输出:

复制代码
  [sigaction] 收到信号 12 来自 PID=12955  UID=0
  [sigaction] 收到信号 12 来自 PID=12957  UID=0

5.7 信号集与阻塞

复制代码
  --- 信号阻塞 (sigprocmask) ---
  SIGINT 和 SIGTERM 已阻塞
  raise(SIGINT) → 信号被阻塞, 进入 pending
  sigpending: SIGINT=1

  sigprocmask(SIG_UNBLOCK) → 解除阻塞
  [处理器] 收到 SIGINT (Ctrl+C), sig_count=1

5.8 SIGCHLD 自动回收

c 复制代码
signal(SIGCHLD, SIG_IGN);  // 子进程退出自动回收, 无僵尸
复制代码
  [子进程] 短暂存在后退出 (SIGCHLD→SIG_IGN 自动回收)
  [父进程] 子进程已自动回收 (无僵尸)

6. 进程间通信---信号量

6.1 System V 信号量原理

System V 信号量是计数器,不是简单的二元锁。支持原子增减和 undo 操作。

复制代码
  semget(key, nsems, IPC_CREAT | 0666)  → 创建信号量集
  semop(semid, &sops, nsops)            → P/V 操作
  semctl(semid, semnum, IPC_RMID)       → 删除/控制

6.2 数据结构

复制代码
  struct sembuf {
    unsigned short sem_num;  // 信号量编号 (0-based)
    short sem_op;            // -1=P操作(减)  +1=V操作(加)
    short sem_flg;           // SEM_UNDO(进程退出自动释放)
  };

6.3 实操输出

有信号量保护(5 个子进程各自增 100 次,预期 500):

复制代码
  信号量初始值: 1 (互斥锁)

  5 个子进程竞争临界资源...
  [子进程 1] 完成 100 次自增
  [子进程 2] 完成 100 次自增
  [子进程 3] 完成 100 次自增
  [子进程 4] 完成 100 次自增
  [子进程 5] 完成 100 次自增

  期望值: 500  实际值: 500  ← ✓ 正确 (无数据竞争)

无信号量保护(演示数据竞争):

复制代码
  期望值: 500  实际值: 100  ← ❌ 数据竞争!

100/500 说明 80% 的更新丢失------5 个进程并行读写共享受限变量,无原子性保证。

系统信号量限制:

复制代码
$ cat /proc/sys/kernel/sem
32000  1024000000  500  32000
  ↑         ↑       ↑     ↑
  SEMMSL   SEMMNS  SEMOPM SEMMNI
  (每组    (系统总   (每次   (系统总
  最大)    最大)    ops)   组数)

7. 进程间通信---共享内存

7.1 原理

共享内存是最快的 IPC------数据不需要在用户态和内核态之间拷贝。

复制代码
  进程A                    进程B
  ┌──────────┐            ┌──────────┐
  │ 用户空间  │            │ 用户空间  │
  │  ┌──────┐ │            │  ┌──────┐ │
  │  │shmat │ │            │  │shmat │ │
  │  └──┬───┘ │            │  └──┬───┘ │
  └─────┼─────┘            └─────┼─────┘
        │        ┌───────────────┘
        │        │
  ┌─────┴────────┴─────┐
  │  物理共享内存页     │
  │  (shmget 创建)     │
  └────────────────────┘

7.2 核心 API

c 复制代码
#include <sys/shm.h>

int shmid = shmget(key, size, IPC_CREAT | 0666);  // 创建段
void *addr = shmat(shmid, NULL, 0);               // 附加到地址空间
shmdt(addr);                                       // 分离
shmctl(shmid, IPC_RMID, NULL);                     // 删除段

7.3 实操输出

复制代码
  shmget: key=0x48016002  shmid=0  size=4096 bytes

  [Writer] PID=12906  shmid=0  size=4096  attaches=0
  [Writer] 写入: [0]=42 [1]=100 [2]=999 ... [15]=150
  [Writer] shmdt 完成

  [Reader] 读取: [0]=42 [1]=100 [2]=999 ... [15]=150
  [Reader] 地址 &data[0]=0x757a7cf82000
  [Reader] 修改 [0]=888
  [Reader] 只读映射验证: [0]=888  ← 共享内存跨映射保持

  shmctl(IPC_RMID): 共享内存已删除

IPC 速度对比:

IPC 机制 数据拷贝次数 相对速度
共享内存 0 次 最快
消息队列 2 次 (user→kern→user)
管道/FIFO 2 次 (user→kern→user)
Socket 2 次 + 协议栈开销 较慢

8. Linux 线程概述

线程实验已在同一节点的 /root/pthread-exp/ 完成,详见 pthread-linux-blog-guide.md。此处仅做关键回顾。

8.1 核心发现

实验 关键数据
pthread_create 8vCPU, 栈默认 8MB, TID≠PID
pthread_once 5 线程竞争仅执行 1 次 (TID=10880)
TSD 3 线程独立副本 + 析构自动调用
pthread_cancel 可取消/不可取消/带清理三种模式
mutex 类型 NORMAL(死锁) / ERRORCHECK(EDEADLK=35) / RECURSIVE(计数)
cond signal vs broadcast
性能 mutex 5.8ns/op vs spinlock 4.7ns/op

8.2 线程 vs 进程

维度 进程 线程
地址空间 独立 共享
创建开销 大 (fork+COW)
通信 IPC (管道/消息队列/共享内存) 直接读写共享变量
隔离性 弱 (一个崩溃→全部崩溃)
内核调度 是 (NPTL 1:1)

9. 处理机调度与死锁

9.1 Linux 调度器

复制代码
$ cat /sys/block/vda/queue/scheduler
mq-deadline  (默认)

调度策略:
  SCHED_OTHER  (CFS --- 完全公平调度, 默认)
  SCHED_FIFO   (实时 FIFO, 优先级 1-99)
  SCHED_RR     (实时轮转)
  SCHED_BATCH  (批处理, 不抢占)
  SCHED_IDLE   (最低优先级)

nice 值: -20 (高优先级) ~ 19 (低优先级), 默认 0

9.2 三级调度

复制代码
  高级调度 (作业调度)
    ↓
  中级调度 (内存调度, swapping)
    ↓
  低级调度 (进程调度, 最频繁)
调度算法 类型 特点
FCFS 非抢占 先来先服务,简单但平均等待时间长
SJF 非抢占 最短作业优先,最优但需预知时间
RR 抢占 时间片轮转 (Linux 默认基于 CFS)
优先级 抢占 高优先级优先
CFS 抢占 Linux 2.6+, 红黑树 + 虚拟运行时间

9.3 死锁

死锁四条件(必要条件,缺一不可):

复制代码
  ① 互斥 (Mutual Exclusion):  资源独占
  ② 持有等待 (Hold & Wait):   持有 lockA 等 lockB
  ③ 不可剥夺 (No Preemption):  锁不能被抢走
  ④ 循环等待 (Circular Wait):  线程1→A→B→线程2→A

死锁场景代码:

c 复制代码
// 线程1: 先 lockA 再 lockB
pthread_mutex_lock(&lockA);
pthread_mutex_lock(&lockB);  // ← 等待 lockB

// 线程2: 先 lockB 再 lockA
pthread_mutex_lock(&lockB);
pthread_mutex_lock(&lockA);  // ← 等待 lockA → 死锁!

实操输出:

复制代码
  [线程1] 尝试获取 lockA...
  [线程1] 获得 lockA
  [线程2] 尝试获取 lockB...
  [线程2] 获得 lockB
  [线程1] 尝试获取 lockB...        ← 阻塞 (线程2 持有)
  [线程2] 尝试获取 lockA...        ← 阻塞 (线程1 持有)

  → 死锁! 线程1 在 lockB, 线程2 在 lockA
  → 中断死锁线程 (pthread_cancel)

死锁预防:

策略 方法
破坏循环等待 统一加锁顺序 (都先 A 后 B)
破坏持有等待 一次性获取所有锁 (或 trylock 回退)
破坏不可剥夺 pthread_mutex_trylock() + 释放已有锁
破坏互斥 无锁数据结构 (lock-free)

10. Linux 内存管理

10.1 分页机制

复制代码
  虚拟地址 → 页表 (多级) → 物理地址

  页大小:     4096 bytes (4KB)
  物理内存:   15131 MB (≈15.1 GB)
  swap:       无 (未配置)

  /proc/meminfo:
  MemTotal:       15494424 kB
  MemFree:        14257632 kB
  MemAvailable:   14938140 kB
  Cached:          878080 kB
  SwapTotal:             0 kB

10.2 进程地址空间布局

复制代码
$ head -8 /proc/self/maps
  ┌───────────────────────────── 0x7ff... ─┐
  │  [stack]  (主线程栈)                    │
  ├───────────────────────────── 0x7f7... ─┤
  │  [mmap]  (动态库, 匿名映射)             │
  ├───────────────────────────── 0x5a... ──┤
  │  [heap]  (brk/sbrk 管理)               │
  ├─────────────────────────────            ┤
  │  [BSS]  (未初始化全局变量)              │
  │  [data] (已初始化全局变量)              │
  │  [text] (代码段, 只读)                  │
  └───────────────────────────── 0x00400000 ┘

10.3 mmap 内存映射

c 复制代码
// 匿名映射
int *anon = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
anon[0] = 0xDEAD;
printf("mmap(MAP_ANONYMOUS): addr=%p  [0]=0x%X\n", (void*)anon, anon[0]);
munmap(anon, 4096);
复制代码
  mmap(MAP_ANONYMOUS): addr=0x77c1f28af000  [0]=0xDEAD

文件映射:

复制代码
  mmap(file): addr=0x77c1f28ad000  内容: "Hello mmap world!"

10.4 malloc 内部分析

复制代码
  malloc(1024):    addr=0x608306bbc2a0
  malloc(1MB):     addr=0x608306abc2a0
  地址差:          1048576 bytes (≈1024.0 KB)

malloc 小内存使用 brk/sbrk 扩展堆,大内存(>128KB)使用 mmap。

堆增长:

复制代码
  sbrk(0) (堆顶):  0x608306abc000
  sbrk(4096) → 新堆顶: 0x608306abd000  (增加了 4KB)

10.5 内核态 vs 用户态内存

复制代码
  kmalloc() / vmalloc()    → 内核内存 (不可换出)
  malloc() / mmap()        → 用户内存 (可换出)
  Page Cache / Buffer      → 磁盘缓存 (可回收)

  Buffers:           39976 kB   ← 块设备缓冲
  Cached:           878080 kB   ← 页缓存 (文件数据)
  Dirty:              1072 kB   ← 待写回脏页
  Writeback:             0 kB   ← 正在写回的页

11. Linux 文件系统

11.1 inode 结构

复制代码
  /etc/hostname:
  inode:      394910
  大小:       14 bytes
  块数:       8 (块大小=512)
  链接数:     1
  权限:       644 (rw-r--r--)
  UID/GID:    0/0
  atime:      Wed Jun 17 21:56:42 2026

inode 包含的信息:

复制代码
  ┌─────────────────────────┐
  │  inode (索引节点)        │
  ├─────────────────────────┤
  │  文件类型 & 权限         │
  │  UID / GID              │
  │  大小 / 块数             │
  │  atime / mtime / ctime  │
  │  链接计数                │
  │  数据块指针 (直接/间接)   │
  ├─────────────────────────┤
  │  不包含: 文件名!         │  ← 文件名在目录项中
  └─────────────────────────┘

11.2 文件系统信息

复制代码
  $ statfs("/") →
  类型:        ext4
  块大小:      4096 bytes
  总块数:      10,292,311
  可用块数:    8,985,812
  总容量:      39.3 GB
  可用容量:    34.3 GB
  inode总数:   2,621,440
  可用inode:   2,515,804

11.3 硬链接 vs 软链接

复制代码
  原始文件: /tmp/original.txt (inode=1294, links=1, size=19)

  硬链接:   ln /tmp/original.txt /tmp/hardlink.txt
    inode=1294 (← 与原文件相同!)  links=2 ← 链接计数增加

  软链接:   ln -s /tmp/original.txt /tmp/softlink.txt
    inode=1295 (← 不同)  size=17  type=S_IFLNK
    指向: /tmp/original.txt

删除原始文件后:

复制代码
  unlink(/tmp/original.txt)
  hardlink.txt 内容: "Original content..." (✓ 仍可读)
  softlink.txt 打开: ❌ No such file (断链!)

对比表:

维度 硬链接 软链接 (符号链接)
inode 相同 不同
跨文件系统
链接目录 ❌ (一般不允许)
删原始后 数据仍在 断链
存储 无额外空间 存储路径字符串
实现 目录项指向同一 inode 独立 inode 存路径

11.4 文件类型

复制代码
$ ls -la /dev/null /etc/hostname /tmp
  /dev/null       → crw-rw-rw-   c = 字符设备
  /etc/hostname   → -rw-r--r--   - = 普通文件
  /tmp            → drwxrwxrwt   d = 目录

文件类型:  - 普通  d 目录  l 软链接  c 字符设备  b 块设备  s 套接字  p 管道

12. Linux 设备与网络

12.1 设备文件

Linux "一切皆文件"------设备也通过文件系统节点访问:

复制代码
  字符设备: /dev/null, /dev/tty, /dev/urandom  (按字节访问)
  块设备:   /dev/vda, /dev/sda                 (按块访问, 支持缓存)

$ ls -la /dev/null /dev/vda
  /dev/null       → crw-rw-rw- 1 root root 1, 3 ...
  /dev/vda        → brw-rw---- 1 root disk 253, 1 ...

  maj:min = 主设备号:次设备号
  主设备号 → 驱动程序选择
  次设备号 → 同类设备区分

12.2 磁盘缓冲与缓存

复制代码
$ grep -E '^(Buffers|Cached|Dirty|Writeback):' /proc/meminfo
Buffers:           39976 kB   ← 块设备元数据缓存
Cached:           878080 kB   ← 文件数据页缓存
Dirty:              1072 kB   ← 待写回的脏页
Writeback:             0 kB   ← 正在写回的页

12.3 设备驱动

复制代码
$ head -12 /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input

12.4 网络接口 (SIOCGIFADDR ioctl)

c 复制代码
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr;
strcpy(ifr.ifr_name, "eth0");
ioctl(sock, SIOCGIFADDR, &ifr);  // 获取 IP 地址
复制代码
  eth0 IP: 192.168.0.114        (私有 IP)

所有网络接口:

复制代码
$ ip -br addr show
  lo      UNKNOWN  127.0.0.1/8
  eth0    UP       192.168.0.114/24

12.5 已挂载文件系统

复制代码
$ df -hT
Filesystem  Type   Size  Used  Avail  Use%  Mounted on
tmpfs       tmpfs  1.5G  1.1M  1.5G   1%   /run
/dev/vda1   ext4   40G   3.3G  35G    9%   /
tmpfs       tmpfs  7.4G  0     7.4G   0%   /dev/shm
tmpfs       tmpfs  5.0M  0     5.0M   0%   /run/lock

12.6 网络连接状态

复制代码
$ ss -tuln
Netid State   Recv-Q Send-Q  Local Address:Port   Peer Address:Port
udp   UNCONN  0      0       127.0.0.54:53         0.0.0.0:*
udp   UNCONN  0      0       127.0.0.53%lo:53      0.0.0.0:*
udp   UNCONN  0      0        127.0.0.1:323        0.0.0.0:*
tcp   LISTEN  0      4096     127.0.0.53%lo:53     0.0.0.0:*
tcp   LISTEN  0      4096     0.0.0.0:22           0.0.0.0:*

127.0.0.53:53 = systemd-resolved DNS 解析器; 0.0.0.0:22 = SSH 服务


附录

A. IPC 速查表

机制 创建 发送/上锁 接收/解锁 销毁
匿名管道 pipe(fd) write() read() close(fd)
命名管道 mkfifo() write() read() unlink()
消息队列 msgget() msgsnd() msgrcv() msgctl(IPC_RMID)
信号量 semget() semop(-1) semop(+1) semctl(IPC_RMID)
共享内存 shmget() shmat()+write shmat()+read shmctl(IPC_RMID)
信号 --- kill()/raise() signal()/sigaction() ---
线程互斥 pthread_mutex_init lock unlock destroy
线程条件 pthread_cond_init signal/broadcast wait destroy

B. 系统参数

参数 路径
PID 上限 4,194,304 /proc/sys/kernel/pid_max
消息最大 8,192 bytes /proc/sys/kernel/msgmax
共享内存最大 16 EB (理论) /proc/sys/kernel/shmmax
页大小 4,096 bytes getconf PAGESIZE
线程最大 125,671 /proc/sys/kernel/threads-max
栈默认 8,192 KB ulimit -s
文件句柄 unlimited ulimit -n

C. 编译运行

makefile 复制代码
CC       = gcc
CFLAGS   = -Wall -Wextra -O2 -g
LDFLAGS  = -lpthread -lrt

all: exp_process exp_sysv_ipc exp_signals exp_deadlock_memory exp_fs_dev_net
bash 复制代码
# 编译全部
make all

# 运行各实验
./exp_process              # 进程 + 管道
./exp_sysv_ipc             # 消息队列 + 信号量 + 共享内存
./exp_signals              # 信号
./exp_deadlock_memory      # 死锁 + 内存管理
./exp_fs_dev_net           # 文件系统 + 设备 + 网络
bash theory_info.sh        # 系统信息采集

D. 实验环境

复制代码
操作系统: Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-106-generic)
硬件:     FlexusX x2e.8u.16g (8vCPU / 16GiB)
存储:     ext4 /dev/vda1 (40GB)
编译器:   gcc 13.3.0 (Ubuntu)
线程库:   NPTL 2.39 (glibc 内置)
节点:     ecs-57c4-0004 (120.46.167.216 / 192.168.0.114)

文档版本 : v1.0 | 创建日期 : 2026-06-17

运行环境 : ecs-57c4-0004 (华为云香港 FlexusX)

对应实验: OS概述 · 进程控制 · 管道 · 消息队列 · 信号 · 信号量 · 共享内存 · 线程 · 死锁 · 内存管理 · 文件系统 · 设备网络