Linux 系统编程综合指南
本文档是Linux系统编程的综合指南,从基础概念到高级架构,涵盖系统调用、I/O机制、并发编程等核心主题。适合系统工程师、内核开发者以及对Linux内部机制感兴趣的开发者学习参考。
📋 目录
1. 系统架构
1.1 Linux 系统分层架构
Linux 系统完整架构:
┌─────────────────────────────────────────────────────────┐
│ 用户空间 │
│ ┌─────────────┬─────────────┬─────────────┐ │
│ │ 应用程序 │ 系统工具 │ shell │ │
│ └─────────────┴─────────────┴─────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ C 标准库 (glibc) │ │
│ └─────────────────────────────────────────────────┘ │
├═════════════════════════════════════════════════════════┤ ← 用户态/内核态边界
│ 内核空间 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 系统调用接口 │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Linux 内核 │ │
│ │ ┌─────────┬─────────┬─────────┬─────────┐ │ │
│ │ │进程管理 │内存管理 │文件系统 │网络子系统│ │ │
│ │ └─────────┴─────────┴─────────┴─────────┘ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ 设备驱动程序 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────┐
│ 硬件层 │
│ ┌──────┬──────┬──────┬──────┐ │
│ │ CPU │ 内存 │ 磁盘 │ 网卡 │ │
│ └──────┴──────┴──────┴──────┘ │
└─────────────────────────────────┘1.2 内存管理架构
1.2.1 虚拟内存布局
Linux 进程虚拟内存布局 (x86_64):
┌────────────────────────────────────────────────────────┐
│ 0xFFFFFFFFFFFFFFFF │ ← 内核空间起始
│ ┌────────────────────────────────────────────────────┐ │
│ │ 内核空间 │ │
│ │ (不可被用户进程直接访问) │ │
│ └────────────────────────────────────────────────────┘ │
│ 0x00007FFFFFFFFFFF │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 栈区 │ │ ← 向下增长
│ │ ↓ (由高地址向低地址增长) │ │
│ └────────────────────────────────────────────────────┘ │
│ ... │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 堆区 │ │ ← 向上增长
│ │ ↑ (由低地址向高地址增长) │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 数据段 │ │
│ │ ┌──────────┬──────────┐ │ │
│ │ │ BSS段 │ 数据段 │ │ │
│ │ └──────────┴──────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 代码段 │ │
│ │ (程序指令) │ │
│ └────────────────────────────────────────────────────┘ │
│ 0x0000000000400000 │ ← 通常的代码段起始
└────────────────────────────────────────────────────────┘1.2.2 页表机制
c
// x86_64 四级页表示例
typedef struct {
uint64_t present : 1; // 页面存在位
uint64_t write : 1; // 写权限
uint64_t user : 1; // 用户访问权限
uint64_t pwt : 1; // 页级写透
uint64_t pcd : 1; // 页级缓存禁用
uint64_t accessed : 1; // 访问位
uint64_t dirty : 1; // 脏位
uint64_t pat : 1; // 页属性表
uint64_t global : 1; // 全局页
uint64_t ignored : 3; // 忽略位
uint64_t address : 40; // 物理页帧号
uint64_t reserved : 11; // 保留位
uint64_t nx : 1; // 禁止执行位
} page_table_entry_t;1.3 进程调度器架构
1.3.1 调度器概述
Linux 调度器负责决定在多任务环境中哪个进程应该获得 CPU 时间。现代 Linux 采用模块化调度器架构,支持多种调度策略。
Linux 调度器架构演进:
┌─────────────────────────────────────────────────────────┐
│ Linux 2.6 之前: O(n) 调度器 │
│ • 简单轮转调度 │
│ • 时间复杂度 O(n) │
│ • 不适合大量进程 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Linux 2.6: O(1) 调度器 │
│ • 固定时间复杂度 O(1) │
│ • 140个优先级队列 │
│ • 交互进程启发式算法 │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Linux 2.6.23+: CFS (完全公平调度器) │
│ • 基于红黑树的 O(log n) 调度 │
│ • 虚拟运行时间概念 │
│ • 更好的交互性和公平性 │
└─────────────────────────────────────────────────────────┘1.3.2 CFS 调度器详解
CFS 核心概念:
c
// CFS 调度实体结构(简化版)
struct sched_entity {
struct load_weight load; // 进程权重
struct rb_node run_node; // 红黑树节点
u64 vruntime; // 虚拟运行时间
u64 prev_sum_exec_runtime; // 累计执行时间
u64 sum_exec_runtime;
s64 fair_key; // 红黑树排序键值
};
// CFS 运行队列
struct cfs_rq {
struct rb_root tasks_timeline; // 红黑树根
struct rb_node *rb_leftmost; // 最左节点(下一个调度)
unsigned long nr_running; // 运行进程数
u64 min_vruntime; // 最小虚拟运行时间
};CFS 算法原理:
CFS 调度算法流程:
┌─────────────────────────────────────────────────────────┐
│ CFS 调度器 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 红黑树结构 │ │
│ │ │ │
│ │ ○ (vruntime=100) │ │
│ │ ╱ ╲ │ │
│ │ (80) ○ ○ (150) │ │
│ │ ╱ ╲ ╲ │ │
│ │ (50)○ ○ ○(200) │ │
│ │ │ │
│ │ 最左节点 = 下一个要调度的进程 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 核心公式: │
│ • vruntime = 实际运行时间 * NICE_0_LOAD / 权重 │
│ • 权重 = 1024 / (1.25 ^ nice值) │
│ • 时间片 = sysctl_sched_latency * 权重 / 总权重 │
│ │
│ 调度决策: │
│ 1. 选择 vruntime 最小的进程运行 │
│ 2. 进程用完时间片后重新计算 vruntime │
│ 3. 将进程重新插入红黑树 │
│ 4. 重复步骤1 │
└─────────────────────────────────────────────────────────┘1.3.3 调度策略详解
Linux 支持多种调度策略,通过 sched_setscheduler() 系统调用设置:
c
// 调度策略定义
#define SCHED_NORMAL 0 // CFS 调度器(默认)
#define SCHED_FIFO 1 // 实时FIFO调度
#define SCHED_RR 2 // 实时轮转调度
#define SCHED_BATCH 3 // 批处理调度
#define SCHED_IDLE 5 // 空闲调度
#define SCHED_DEADLINE 6 // 截止时间调度
// 调度策略使用示例
#include <sched.h>
void set_scheduling_policy() {
struct sched_param param;
// 设置实时优先级
param.sched_priority = 50;
// 设置为实时FIFO调度
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
perror("sched_setscheduler");
}
// 获取当前调度策略
int policy = sched_getscheduler(0);
printf("Current policy: %d\n", policy);
}各调度策略特点:
调度策略对比:
┌──────────────┬──────────────┬──────────────┬─────────────┐
│ 调度策略 │ 优先级范围 │ 时间片 │ 适用场景 │
├──────────────┼──────────────┼──────────────┼─────────────┤
│ SCHED_NORMAL │ -20~19 │ 动态调整 │ 普通进程 │
│ SCHED_FIFO │ 1~99 │ 无限制 │ 实时系统 │
│ SCHED_RR │ 1~99 │ 固定轮转 │ 实时系统 │
│ SCHED_BATCH │ -20~19 │ 较长 │ 批处理作业 │
│ SCHED_IDLE │ 0 │ 最低 │ 后台任务 │
│ SCHED_DEADLINE│ 动态 │ 截止时间 │ 硬实时系统 │
└──────────────┴──────────────┴──────────────┴─────────────┘1.3.4 CPU 亲和性和 NUMA
CPU 亲和性控制:
c
#define _GNU_SOURCE
#include <sched.h>
void cpu_affinity_example() {
cpu_set_t cpuset;
// 清空 CPU 集合
CPU_ZERO(&cpuset);
// 设置进程只能在 CPU 0 和 CPU 2 上运行
CPU_SET(0, &cpuset);
CPU_SET(2, &cpuset);
// 应用 CPU 亲和性
if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {
perror("sched_setaffinity");
}
// 查询当前 CPU 亲和性
CPU_ZERO(&cpuset);
if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == -1) {
perror("sched_getaffinity");
}
printf("Process can run on CPUs: ");
for (int i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, &cpuset)) {
printf("%d ", i);
}
}
printf("\n");
}NUMA 感知调度:
NUMA 架构下的调度优化:
┌─────────────────────────────────────────────────────────┐
│ NUMA 节点 0 │
│ ┌─────────┬─────────┬─────────────────────────────┐ │
│ │ CPU 0-3 │ 本地内存 │ 缓存一致性 │ │
│ └─────────┴─────────┴─────────────────────────────┘ │
└─────────────────┬───────────────────────────────────────┘
│ 互连总线(较慢)
┌─────────────────▼───────────────────────────────────────┐
│ NUMA 节点 1 │
│ ┌─────────┬─────────┬─────────────────────────────┐ │
│ │ CPU 4-7 │ 本地内存 │ 缓存一致性 │ │
│ └─────────┴─────────┴─────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
调度器 NUMA 优化策略:
• 尽量将进程调度到访问本地内存的 CPU 上
• 考虑内存分配的 NUMA 节点
• 负载均衡时考虑 NUMA 拓扑1.3.5 调度器调优
关键内核参数:
bash
# CFS 调度器参数 (/proc/sys/kernel/)
echo 6000000 > /proc/sys/kernel/sched_latency_ns # 调度延迟
echo 750000 > /proc/sys/kernel/sched_min_granularity_ns # 最小调度粒度
echo 1000000 > /proc/sys/kernel/sched_wakeup_granularity_ns # 唤醒抢占粒度
# 实时调度器参数
echo 950000 > /proc/sys/kernel/sched_rt_runtime_us # 实时进程最大运行时间
echo 1000000 > /proc/sys/kernel/sched_rt_period_us # 实时调度周期
# NUMA 相关参数
echo 1 > /proc/sys/kernel/numa_balancing # 启用 NUMA 平衡性能监控工具:
c
// 获取调度统计信息
#include <sys/resource.h>
void print_scheduling_info() {
struct rusage usage;
// 获取资源使用统计
if (getrusage(RUSAGE_SELF, &usage) == 0) {
printf("User CPU time: %ld.%06ld seconds\n",
usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
printf("System CPU time: %ld.%06ld seconds\n",
usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
printf("Voluntary context switches: %ld\n", usage.ru_nvcsw);
printf("Involuntary context switches: %ld\n", usage.ru_nivcsw);
}
}调度器性能分析:
bash
# 使用 perf 分析调度器性能
perf record -e sched:sched_switch -e sched:sched_wakeup ./my_program
perf script | head -20
# 查看调度器统计信息
cat /proc/schedstat
# 实时查看进程调度信息
perf top -e cycles -s comm,dso2. 系统调用基础
2.1 系统调用概述
系统调用是用户空间程序与内核交互的唯一接口,它提供了一种安全、可控的方式来访问系统资源。
系统调用的关键特性
- 特权级切换:系统调用将处理器从用户态切换到内核态,以便 CPU 访问受保护的内核内存
- 标准化接口:系统调用的组成是固定的,每个系统调用都由一个唯一的数字来标识
- 参数传递:每个系统调用可辅以一套参数,规范用户空间与内核空间之间的信息传递
2.2 系统调用执行机制
以 x86-64 架构为例,系统调用的执行过程:
系统调用执行流程:
┌─────────────────────────────────────────────────────────┐
│ 用户空间 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 应用程序 │ │
│ │ ┌─────────────┐ │ │
│ │ │ open(...) │ │ │
│ │ └─────────────┘ │ │
│ └─────────────────┬───────────────────────────────┘ │
│ │ 1. 调用库函数 │
│ ┌─────────────────▼───────────────────────────────┐ │
│ │ glibc 包装函数 │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ 2. 参数准备和寄存器设置 │ │ │
│ │ │ 3. 系统调用号设置 (%rax) │ │ │
│ │ │ 4. 执行 syscall 指令 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────┬───────────────────────────────┘ │
└────────────────────┼───────────────────────────────────┘
│ 5. 模式切换
┌────────────────────▼───────────────────────────────────┐
│ 内核空间 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 系统调用处理程序 │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ 6. 保存用户态寄存器 │ │ │
│ │ │ 7. 验证系统调用号 │ │ │
│ │ │ 8. 调用具体的系统调用服务例程 │ │ │
│ │ │ 9. 执行实际操作 │ │ │
│ │ │ 10. 恢复寄存器,返回用户态 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘2.3 现代系统调用优化
VDSO (Virtual Dynamic Shared Object)
c
// 某些系统调用通过 VDSO 优化,避免模式切换
#include <time.h>
#include <sys/time.h>
int main() {
struct timespec ts;
// 这个调用可能通过 VDSO 直接在用户空间执行
clock_gettime(CLOCK_REALTIME, &ts);
return 0;
}3. Linux I/O 系统
Linux I/O 系统是操作系统的核心组成部分,从简单的文件操作到复杂的网络通信,都离不开高效的 I/O 机制。
3.1 文件 I/O 基础
3.1.1 文件描述符概念
在 Unix/Linux 系统中,所有 I/O 操作都通过文件描述符进行抽象。文件描述符是一个非负整数,用于标识进程打开的文件、管道、socket、设备等。
进程文件描述符表示例:
┌─────────────────────────────────────────┐
│ 进程 A 文件描述符表 │
├─────────┬───────────────────────────────┤
│ FD │ 指向的资源 │
├─────────┼───────────────────────────────┤
│ 0 │ 标准输入 (stdin) │
│ 1 │ 标准输出 (stdout) │
│ 2 │ 标准错误 (stderr) │
│ 3 │ /home/user/data.txt │
│ 4 │ socket (TCP连接) │
│ 5 │ 管道 (pipe) │
│ ... │ ... │
└─────────┴───────────────────────────────┘3.1.2 基本文件操作
open() - 打开/创建文件:
c
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
// 示例
int fd = open("data.txt", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("open");
return -1;
}读写操作的完整示例:
c
// 安全读取函数,处理短读和信号中断
ssize_t safe_read(int fd, void *buf, size_t count) {
ssize_t total_read = 0;
char *ptr = (char *)buf;
while (total_read < count) {
ssize_t bytes_read = read(fd, ptr + total_read, count - total_read);
if (bytes_read == -1) {
if (errno == EINTR) continue; // 信号中断,重试
return -1; // 真正的错误
}
if (bytes_read == 0) break; // EOF
total_read += bytes_read;
}
return total_read;
}3.2 I/O 缓冲机制
3.2.1 缓冲架构层次
I/O 缓冲层次架构:
┌─────────────────────────────────────────────────────────┐
│ 用户应用程序 │
├─────────────────────────────────────────────────────────┤
│ stdio 缓冲层 │ ← 用户态缓冲
│ ┌─────────────┬─────────────┬─────────────┐ │ (glibc)
│ │ FILE *stdin │ FILE *stdout│ FILE *stderr│ │
│ └─────────────┴─────────────┴─────────────┘ │
├═════════════════════════════════════════════════════════┤ ← 用户态/内核态边界
│ 系统调用接口 │
├─────────────────────────────────────────────────────────┤
│ VFS 层 │ ← 虚拟文件系统
├─────────────────────────────────────────────────────────┤
│ Page Cache │ ← 内核页缓存
├─────────────────────────────────────────────────────────┤
│ Buffer Cache │ ← 块设备缓存
├─────────────────────────────────────────────────────────┤
│ 块I/O层 │ ← I/O调度和合并
├─────────────────────────────────────────────────────────┤
│ 设备驱动层 │ ← 硬件驱动
└─────────────────────────────────────────────────────────┘2.2.2 不同I/O方式的数据路径
传统 Buffered I/O:
应用程序 ←─ 内存拷贝 ←─ Page Cache ←─ DMA ←─ 磁盘
数据拷贝次数:2次
优点:缓存效果好,适合重复访问
缺点:额外的内存拷贝开销内存映射 (mmap):
应用程序 ←─ 虚拟内存映射 ←─ Page Cache ←─ DMA ←─ 磁盘
数据拷贝次数:0次(用户空间视角)
优点:零拷贝,高效随机访问
缺点:页面fault开销,内存占用2.3 I/O 模型深入
2.3.1 五种 I/O 模型
I/O 模型分类:
┌─────────────────────────────────────────────────┐
│ 同步 I/O │
│ ┌─────────────┬─────────────┬─────────────┐ │
│ │ 阻塞I/O │ 非阻塞I/O │ I/O复用 │ │
│ │ (Blocking) │(Non-block.) │(Multiplexing)│ │
│ └─────────────┴─────────────┴─────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ 信号驱动I/O │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 异步 I/O │
│ ┌─────────────────────────────────────────┐ │
│ │ 异步I/O │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘3.3.2 高性能 I/O:epoll 示例
c
#include <sys/epoll.h>
#define MAX_EVENTS 1024
int epoll_server_example(int port) {
int server_fd, epoll_fd;
struct epoll_event event, events[MAX_EVENTS];
// 创建服务器socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
// ... 绑定和监听 ...
// 创建epoll实例
epoll_fd = epoll_create1(0);
// 添加服务器socket到epoll
event.events = EPOLLIN;
event.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);
// 事件循环
while (1) {
int nready = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < nready; i++) {
if (events[i].data.fd == server_fd) {
// 处理新连接
int client_fd = accept(server_fd, NULL, NULL);
event.events = EPOLLIN | EPOLLET; // 边缘触发
event.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
} else {
// 处理客户端数据
handle_client(events[i].data.fd);
}
}
}
return 0;
}3.4 现代 I/O 技术
3.4.1 io_uring
c
#include <liburing.h>
// 现代异步 I/O 示例
int io_uring_example() {
struct io_uring ring;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
// 初始化 io_uring
io_uring_queue_init(32, &ring, 0);
// 准备异步读取操作
sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buffer, size, offset);
// 提交操作
io_uring_submit(&ring);
// 等待完成
io_uring_wait_cqe(&ring, &cqe);
// 处理结果
if (cqe->res < 0) {
printf("Error: %s\n", strerror(-cqe->res));
}
io_uring_cqe_seen(&ring, cqe);
io_uring_queue_exit(&ring);
return 0;
}4. 并发编程
4.1 线程编程基础
4.1.1 线程vs进程
进程与线程对比:
┌────────────────┬──────────────────┬──────────────────┐
│ 特征 │ 进程 │ 线程 │
├────────────────┼──────────────────┼──────────────────┤
│ 内存空间 │ 独立 │ 共享 │
│ 创建开销 │ 大 │ 小 │
│ 切换开销 │ 大 │ 小 │
│ 通信方式 │ IPC(管道,共享内存)│ 共享变量 │
│ 崩溃影响 │ 隔离 │ 相互影响 │
└────────────────┴──────────────────┴──────────────────┘4.1.2 线程同步机制
互斥锁示例:
c
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_counter = 0;
void* thread_function(void* arg) {
for (int i = 0; i < 10000; i++) {
pthread_mutex_lock(&mutex);
shared_counter++; // 临界区
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t threads[4];
// 创建4个线程
for (int i = 0; i < 4; i++) {
pthread_create(&threads[i], NULL, thread_function, NULL);
}
// 等待所有线程完成
for (int i = 0; i < 4; i++) {
pthread_join(threads[i], NULL);
}
printf("Final counter: %d\n", shared_counter); // 应该是40000
return 0;
}4.2 生产者-消费者模型
c
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 10
typedef struct {
int buffer[BUFFER_SIZE];
int in, out;
pthread_mutex_t mutex;
sem_t empty, full;
} bounded_buffer_t;
bounded_buffer_t bb = {
.in = 0, .out = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER
};
void buffer_init() {
sem_init(&bb.empty, 0, BUFFER_SIZE); // 初始时缓冲区为空
sem_init(&bb.full, 0, 0); // 初始时没有数据
}
void* producer(void* arg) {
for (int i = 0; i < 20; i++) {
int item = i;
sem_wait(&bb.empty); // 等待空槽
pthread_mutex_lock(&bb.mutex);
bb.buffer[bb.in] = item; // 放入数据
bb.in = (bb.in + 1) % BUFFER_SIZE;
printf("Produced: %d\n", item);
pthread_mutex_unlock(&bb.mutex);
sem_post(&bb.full); // 通知有数据
}
return NULL;
}
void* consumer(void* arg) {
for (int i = 0; i < 20; i++) {
sem_wait(&bb.full); // 等待数据
pthread_mutex_lock(&bb.mutex);
int item = bb.buffer[bb.out]; // 取出数据
bb.out = (bb.out + 1) % BUFFER_SIZE;
printf("Consumed: %d\n", item);
pthread_mutex_unlock(&bb.mutex);
sem_post(&bb.empty); // 通知有空槽
}
return NULL;
}5. 高级特性
5.1 内核同步机制
5.1.1 RCU (Read-Copy-Update)
c
// RCU 机制示例
struct my_data {
int value;
struct rcu_head rcu;
};
struct my_data *global_ptr;
// 读者 (无锁)
void reader_function() {
struct my_data *ptr;
rcu_read_lock();
ptr = rcu_dereference(global_ptr);
if (ptr) {
// 使用 ptr->value
printf("Value: %d\n", ptr->value);
}
rcu_read_unlock();
}
// 写者 (需要同步)
void writer_function(int new_value) {
struct my_data *new_ptr, *old_ptr;
new_ptr = kmalloc(sizeof(*new_ptr), GFP_KERNEL);
new_ptr->value = new_value;
spin_lock(&my_lock);
old_ptr = global_ptr;
rcu_assign_pointer(global_ptr, new_ptr);
spin_unlock(&my_lock);
synchronize_rcu(); // 等待所有读者完成
kfree(old_ptr); // 安全释放旧数据
}5.2 性能优化技术
5.2.1 Zero-Copy 技术
c
// sendfile: 零拷贝文件传输
#include <sys/sendfile.h>
ssize_t zero_copy_file_transfer(int out_fd, int in_fd, size_t count) {
return sendfile(out_fd, in_fd, NULL, count);
}
// splice: 管道间零拷贝
#include <fcntl.h>
ssize_t pipe_zero_copy(int fd_in, int fd_out, size_t len) {
int pipefd[2];
pipe(pipefd);
// 数据从 fd_in 拷贝到管道
ssize_t bytes = splice(fd_in, NULL, pipefd[1], NULL, len, SPLICE_F_MOVE);
// 数据从管道拷贝到 fd_out
splice(pipefd[0], NULL, fd_out, NULL, bytes, SPLICE_F_MOVE);
close(pipefd[0]);
close(pipefd[1]);
return bytes;
}📚 补充资料
libc & glibc
GNU C Library (glibc) 是 Linux 系统的标准 C 库实现:
- 系统调用包装:提供对系统调用的高级接口
- 标准函数:实现 C 标准库函数(malloc、printf 等)
- 线程支持:提供 POSIX 线程实现
- 国际化:支持多语言和地区设置
存储器层次结构
存储器金字塔(访问速度从快到慢):
┌─────────────┐
│ 寄存器 │ ← ~1 周期
├─────────────┤
│ L1 Cache │ ← ~4 周期
├─────────────┤
│ L2 Cache │ ← ~10 周期
├─────────────┤
│ L3 Cache │ ← ~40 周期
├─────────────┤
│ 内存 │ ← ~200 周期
├─────────────┤
│ SSD │ ← ~25,000 周期
├─────────────┤
│ 机械硬盘 │ ← ~20,000,000 周期
└─────────────┘这种层次结构的存在是现代计算机系统中缓存、内存管理等技术的理论基础。
🎯 学习建议
- 基础优先:先掌握系统调用和基本 I/O 操作
- 实践验证:通过编程实验验证理论知识
- 性能意识:关注不同方案的性能特点和适用场景
- 源码学习:阅读相关的内核源码加深理解
- 工具使用:熟练使用 strace、perf、gdb 等调试工具
通过系统学习这些内容,可以建立起对 Linux 系统编程的全面认识,为开发高性能、可靠的系统应用奠定坚实基础。