目录
一、进程与线程
1.1 进程
定义:程序的一次执行过程,是资源分配的基本单位
进程控制块(PCB):
- 进程标识符 (PID)
- 进程状态
- 程序计数器 (PC)
- CPU 寄存器
- 内存管理信息
- 打开文件列表
进程状态:新建 → 就绪 ⇄ 运行 → 阻塞 → 终止
1.2 线程
定义:CPU 调度的基本单位,共享进程资源
特性 | 进程 | 线程 |
资源 | 独立地址空间 | 共享进程地址空间 |
开销 | 创建/切换开销大 | 开销小 |
通信 | IPC 机制 | 直接读写共享内存 |
安全性 | 隔离性好 | 一个崩溃影响整个进程 |
1.3 协程
- 用户态轻量级线程,由程序自己调度
- 切换开销极小(不涉及内核)
- 协作式调度(主动让出)
1.4 上下文切换
进程切换:保存/恢复 PCB、更新页表、刷新 TLB
线程切换:同进程内只需保存/恢复寄存器
开销:进程 > 线程 > 协程
1.5 线程的实现方式
类型 | 说明 | 优点 | 缺点 |
用户级线程 | 用户空间实现,内核不感知 | 切换快、无系统调用 | 一个阻塞全部阻塞 |
内核级线程 | 内核管理和调度 | 可利用多核 | 切换开销大 |
混合模型 | M:N 映射 | 兼顾两者优点 | 实现复杂 |
1.6 进程 vs 线程 vs 协程
特性 | 进程 | 线程 | 协程 |
调度 | 内核 | 内核 | 用户程序 |
切换开销 | 大(ms级) | 中(μs级) | 小(ns级) |
内存 | 独立地址空间 | 共享进程空间 | 共享线程栈 |
通信 | IPC | 共享内存 | 直接访问 |
并行 | 可并行 | 可并行 | 单线程内不可 |
1.7 守护进程
特点:
- 后台运行,无控制终端
- 生命周期长,随系统启动
- 典型:sshd、nginx、mysqld
创建步骤:
pid_t pid = fork();
if (pid > 0) exit(0); // 父进程退出
setsid(); // 创建新会话
chdir("/"); // 切换工作目录
umask(0); // 重设文件权限掩码
close(STDIN_FILENO); // 关闭标准输入输出1.8 exec 系列函数
// 替换当前进程映像
execl("/bin/ls", "ls", "-l", NULL);
execlp("ls", "ls", "-l", NULL); // 使用 PATH
execv("/bin/ls", argv);
execvp("ls", argv);特点:
- 不创建新进程,PID 不变
- 替换代码段、数据段、堆栈
- 通常与 fork 配合使用
1.9 wait/waitpid 函数
pid_t wait(int *status); // 阻塞等待任一子进程
pid_t waitpid(pid_t pid, int *status, int options);
// 获取退出状态
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
}waitpid 参数:
- pid > 0:等待指定进程
- pid = -1:等待任一子进程
- WNOHANG:非阻塞
二、进程调度
2.1 调度算法
算法 | 特点 | 缺点 |
FCFS | 先来先服务 | 护航效应 |
SJF | 短作业优先 | 长作业饥饿 |
优先级 | 高优先级先执行 | 低优先级饥饿 |
RR | 时间片轮转,公平 | 切换开销 |
多级反馈队列 | 兼顾响应和吞吐 | 实现复杂 |
2.2 多级反馈队列
高优先级队列 (时间片小) → 未完成降级
↓
中优先级队列 (时间片中) → 未完成降级
↓
低优先级队列 (时间片大/FCFS)2.3 Linux CFS 调度器
完全公平调度器(Completely Fair Scheduler):
- 使用红黑树管理可运行进程
- 虚拟运行时间(vruntime)越小,优先级越高
- 每个进程按权重分配 CPU 时间
vruntime = 实际运行时间 × (NICE_0_LOAD / 进程权重)特点:
- O(log n) 复杂度选择下一个进程
- 支持组调度(cgroups)
- 自动平衡交互性和吞吐量
2.4 实时调度算法
算法 | 说明 |
SCHED_FIFO | 先进先出,高优先级抢占 |
SCHED_RR | 时间片轮转,同优先级轮换 |
SCHED_DEADLINE | 基于截止时间调度(EDF) |
实时进程优先级:1-99(高于普通进程)
struct sched_param param;
param.sched_priority = 50;
sched_setscheduler(pid, SCHED_FIFO, ¶m);三、进程间通信
3.1 管道(Pipe)
int fd[2];
pipe(fd); // fd[0] 读端,fd[1] 写端- 半双工,单向流动
- 仅限父子进程
- 命名管道(FIFO)可用于无关进程
3.2 消息队列
int msgid = msgget(key, IPC_CREAT | 0666);
msgsnd(msgid, &msg, sizeof(msg), 0);
msgrcv(msgid, &msg, sizeof(msg), type, 0);- 消息有类型,可选择性接收
- 内核中存储,有大小限制
3.3 共享内存(最快)
int shmid = shmget(key, size, IPC_CREAT | 0666);
void* ptr = shmat(shmid, NULL, 0);
// 直接读写 ptr
shmdt(ptr);- 多进程直接访问同一内存
- 需要同步机制(信号量)
3.4 信号量
sem_t sem;
sem_init(&sem, 1, 1); // 进程间共享,初值1
sem_wait(&sem); // P 操作
// 临界区
sem_post(&sem); // V 操作3.5 信号
signal(SIGINT, handler); // 注册信号处理函数
kill(pid, SIGTERM); // 发送信号3.6 IPC 对比
方式 | 特点 | 适用场景 |
管道 | 简单,单向 | 父子进程 |
消息队列 | 有类型,异步 | 解耦通信 |
共享内存 | 最快 | 大数据量 |
信号量 | 同步/互斥 | 资源控制 |
Socket | 跨主机 | 网络通信 |
3.7 线程同步机制
互斥锁(Mutex)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
// 临界区
pthread_mutex_unlock(&mutex);自旋锁
pthread_spinlock_t spinlock;
pthread_spin_init(&spinlock, 0);
pthread_spin_lock(&spinlock);
// 临界区(应该很短)
pthread_spin_unlock(&spinlock);特性 | 互斥锁 | 自旋锁 |
等待方式 | 阻塞睡眠 | 忙等待 |
适用场景 | 临界区长 | 临界区短、多核 |
开销 | 上下文切换 | CPU 空转 |
读写锁
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock); // 读锁(共享)
pthread_rwlock_wrlock(&rwlock); // 写锁(独占)
pthread_rwlock_unlock(&rwlock);条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
// 等待方
pthread_mutex_lock(&mutex);
while (!condition) {
pthread_cond_wait(&cond, &mutex); // 原子释放锁并等待
}
// 处理
pthread_mutex_unlock(&mutex);
// 通知方
pthread_mutex_lock(&mutex);
condition = true;
pthread_cond_signal(&cond); // 或 pthread_cond_broadcast
pthread_mutex_unlock(&mutex);3.8 生产者消费者问题
#include<queue>
#include<mutex>
#include<condition_variable>
template<typename T>
class BlockingQueue {
std::queue<T> queue;
std::mutex mtx;
std::condition_variable not_empty;
std::condition_variable not_full;
size_t capacity;
public:
void push(T item) {
std::unique_lock<std::mutex> lock(mtx);
not_full.wait(lock, [this]{ return queue.size() < capacity; });
queue.push(std::move(item));
not_empty.notify_one();
}
T pop() {
std::unique_lock<std::mutex> lock(mtx);
not_empty.wait(lock, [this]{ return !queue.empty(); });
T item = std::move(queue.front());
queue.pop();
not_full.notify_one();
return item;
}
};3.9 读者写者问题
#include<shared_mutex>
class ReadWriteLock {
std::shared_mutex rw_mutex;
int data = 0;
public:
// 读者:共享锁
int read() {
std::shared_lock<std::shared_mutex> lock(rw_mutex);
return data;
}
// 写者:独占锁
void write(int value) {
std::unique_lock<std::shared_mutex> lock(rw_mutex);
data = value;
}
};读者优先 vs 写者优先:
- 读者优先:可能导致写者饥饿
- 写者优先:可能导致读者饥饿
- 公平策略:按到达顺序处理
3.10 哲学家就餐问题
问题:5 个哲学家围坐,每人需要两把叉子才能吃饭
解决方案:
1. 资源分级:按编号顺序获取叉子
2. 仲裁者:引入服务员协调
3. 限制人数:最多 4 人同时尝试
// 资源分级解法
void philosopher(int id) {
int left = id;
int right = (id + 1) % 5;
// 总是先获取编号小的叉子
if (left < right) {
lock(fork[left]);
lock(fork[right]);
} else {
lock(fork[right]);
lock(fork[left]);
}
eat();
unlock(fork[left]);
unlock(fork[right]);
}四、死锁
4.1 死锁条件(四个必要条件)
- 互斥:资源只能被一个进程使用
- 占有并等待:持有资源同时等待其他资源
- 不可剥夺:资源只能由持有者释放
- 循环等待:存在进程等待环
4.2 死锁处理
策略 | 方法 |
预防 | 破坏必要条件(资源有序分配等) |
避免 | 银行家算法 |
检测 | 资源分配图检测环 |
恢复 | 终止进程/资源抢占 |
4.3 银行家算法
Available: 可用资源向量
Need = Max - Allocation
安全性检查:
1. 找到 Need <= Available 的进程
2. 假设其完成,释放资源
3. 重复直到所有进程完成(安全)或无法继续(不安全)五、内存管理
5.1 内存分区
高地址
+------------------+
| 内核空间 |
+------------------+
| 栈 | ← 向下增长
| ↓ |
| ↑ |
| 堆 | ← 向上增长
+------------------+
| BSS | ← 未初始化全局变量
+------------------+
| Data | ← 已初始化全局变量
+------------------+
| Text | ← 代码段
+------------------+
低地址5.2 虚拟内存
- 每个进程独立虚拟地址空间
- 通过页表映射到物理内存
- 优点:隔离、大地址空间、按需加载、共享
5.3 分页
虚拟地址 = 页号 + 页内偏移
↓ 页表查询
物理地址 = 帧号 + 页内偏移多级页表:解决页表过大问题
TLB(快表):缓存常用页表项,加速地址转换
5.4 页面置换算法
算法 | 原理 | 特点 |
FIFO | 淘汰最先进入的页 | 简单,可能 Belady 异常 |
LRU | 淘汰最久未使用的页 | 效果好,实现复杂 |
LFU | 淘汰使用次数最少的页 | 需要计数器 |
Clock | LRU 近似,访问位 | 实现简单,效果好 |
LRU 实现:哈希表 + 双向链表
class LRUCache {
int capacity;
list<pair<int, int>> cache; // 双向链表
unordered_map<int, list<pair<int,int>>::iterator> map; // 哈希表
public:
int get(int key) {
if (map.find(key) == map.end()) return -1;
cache.splice(cache.begin(), cache, map[key]); // 移到头部
return map[key]->second;
}
void put(int key, int value) {
if (map.find(key) != map.end()) {
map[key]->second = value;
cache.splice(cache.begin(), cache, map[key]);
} else {
if (cache.size() == capacity) {
map.erase(cache.back().first);
cache.pop_back();
}
cache.push_front({key, value});
map[key] = cache.begin();
}
}
};5.5 内存分配
算法 | 说明 |
首次适应 | 找到第一个足够大的空闲块 |
最佳适应 | 找到最小的足够大的空闲块 |
伙伴系统 | 2^n 大小分配,便于合并 |
Slab | 对象缓存,减少碎片 |
5.6 缺页中断处理
1. CPU 访问虚拟地址
2. MMU 查页表,发现页不在内存(缺页)
3. 触发缺页中断
4. 操作系统处理:
a. 查找空闲物理页(或页面置换)
b. 从磁盘读取页面
c. 更新页表
d. 重新执行指令缺页类型:
- 硬缺页:需要从磁盘读取
- 软缺页:页面在内存中(如共享页)
5.7 内存映射(mmap)
void* addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
// 直接操作 addr
munmap(addr, length);用途:
- 文件映射:减少拷贝
- 匿名映射:大内存分配
- 进程间共享内存
5.8 分段机制
逻辑地址 = 段号 + 段内偏移
↓ 段表查询
物理地址 = 段基址 + 段内偏移段表项:段基址、段长度、访问权限
分段 vs 分页:
| 特性 | 分段 | 分页 |
|——|——|——|
| 大小 | 可变 | 固定 |
| 可见性 | 用户可见 | 用户透明 |
| 碎片 | 外部碎片 | 内部碎片 |
| 共享 | 便于共享 | 较复杂 |
5.9 内存碎片
类型 | 说明 | 解决方案 |
内部碎片 | 分配单元内未使用空间 | 更小的分配粒度 |
外部碎片 | 空闲块太小无法分配 | 紧凑、伙伴系统 |
5.10 伙伴系统
分配 70KB:
1. 需要 128KB(2^7)块
2. 从 256KB 块分裂:128KB + 128KB
3. 返回一个 128KB 块
释放时:
1. 检查伙伴块是否空闲
2. 若空闲则合并
3. 递归向上合并优点:快速分配、减少外部碎片
缺点:内部碎片
5.11 Slab 分配器
Slab 缓存池
+------------------+
| task_struct 缓存 | → [slab1] → [slab2] → ...
+------------------+
| inode 缓存 | → [slab1] → [slab2] → ...
+------------------+特点:
- 预分配常用对象
- 避免频繁 malloc/free
- 减少碎片
5.12 段页式内存管理
逻辑地址 = 段号 + 页号 + 页内偏移
↓ 段表查询
↓ 页表查询
物理地址 = 帧号 + 页内偏移优点:
- 结合分段和分页的优点
- 段用于逻辑划分(代码、数据、栈)
- 页用于物理内存管理
5.13 系统调用过程
1. 用户程序调用库函数(如 read())
2. 库函数将参数放入寄存器
3. 执行 syscall/int 0x80 指令
4. CPU 切换到内核态
5. 根据系统调用号查找处理函数
6. 执行内核函数
7. 返回结果,切换回用户态系统调用 vs 函数调用:
| 特性 | 系统调用 | 函数调用 |
|——|———-|———-|
| 特权级 | 需要切换 | 不需要 |
| 开销 | 大 | 小 |
| 安全检查 | 需要 | 不需要 |
5.14 中断与异常
类型 | 说明 | 示例 |
中断(外部) | 外部设备触发 | 键盘、网卡、时钟 |
异常(内部) | CPU 执行指令时产生 | 除零、缺页、断点 |
陷阱 | 主动触发 | 系统调用 |
中断处理流程:
1. 保存当前上下文
2. 根据中断号查找中断处理程序
3. 执行中断处理
4. 恢复上下文
5. 返回被中断的程序六、文件系统
6.1 文件系统结构
+------------------+
| 超级块 | ← 文件系统元信息
+------------------+
| inode 表 | ← 文件元数据
+------------------+
| 数据块位图 |
+------------------+
| 数据块 | ← 文件内容
+------------------+6.2 inode
存储文件元数据:
- 文件大小、权限、时间戳
- 数据块指针(直接、间接、双重间接)
6.3 硬链接 vs 软链接
特性 | 硬链接 | 软链接 |
inode | 相同 | 不同 |
跨文件系统 | 不可以 | 可以 |
链接目录 | 不可以 | 可以 |
原文件删除 | 仍可访问 | 失效 |
ln file hardlink # 硬链接
ln -s file softlink # 软链接6.4 文件描述符
- 进程打开文件的索引
- 0: stdin, 1: stdout, 2: stderr
- 指向内核文件表项
6.5 文件打开过程
1. 用户调用 open()
2. 内核分配文件描述符
3. 在文件表中创建表项
4. 查找/创建 inode
5. 返回文件描述符三层结构:
进程文件描述符表 → 系统文件表 → inode 表
(fd) (偏移量) (文件元数据)6.6 虚拟文件系统(VFS)
应用程序
↓ 统一接口(open/read/write)
VFS
↓
+-------+-------+-------+
| ext4 | xfs | nfs |
+-------+-------+-------+VFS 对象:
- 超级块对象:文件系统信息
- inode 对象:文件元数据
- 目录项对象:路径组件
- 文件对象:打开的文件
6.7 ext4 文件系统特点
特性 | 说明 |
最大文件 | 16 TB |
最大分区 | 1 EB |
日志功能 | 支持,保证一致性 |
延迟分配 | 提高性能 |
多块分配 | 减少碎片 |
在线碎片整理 | 支持 |
6.8 权限管理
# 权限表示
-rwxr-xr-x 1 user group size date filename
│││ │││ │││
│││ │││ └── 其他用户权限
│││ └── 组权限
└── 所有者权限
# 数字表示
r=4, w=2, x=1
chmod 755 file # rwxr-xr-x
chmod 644 file # rw-r--r--
# 特殊权限
chmod u+s file # SUID:以文件所有者权限执行
chmod g+s dir # SGID:新文件继承目录组
chmod +t dir # Sticky:只有所有者能删除七、IO 管理
7.1 IO 模型
模型 | 说明 |
阻塞 IO | 调用阻塞直到数据就绪 |
非阻塞 IO | 立即返回,轮询检查 |
IO 多路复用 | select/poll/epoll 监听多个 fd |
信号驱动 IO | 数据就绪时发送信号 |
异步 IO | 内核完成 IO 后通知应用 |
7.2 同步 vs 异步,阻塞 vs 非阻塞
- 同步:用户进程主动等待/查询 IO 结果
- 异步:内核完成后通知用户进程
- 阻塞:调用时进程挂起
- 非阻塞:调用立即返回
7.3 零拷贝
传统 IO:
磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
(4次拷贝,4次上下文切换)sendfile:
磁盘 → 内核缓冲区 → Socket 缓冲区 → 网卡
(2次拷贝,2次上下文切换)mmap + write:
磁盘 → 内核缓冲区(用户空间映射)→ Socket 缓冲区 → 网卡7.4 DMA 原理
直接内存访问:外设直接与内存交换数据,无需 CPU 参与
传统 IO:
CPU 控制数据传输,占用 CPU 时间
DMA IO:
1. CPU 发起 DMA 请求
2. DMA 控制器接管总线
3. 外设与内存直接传输
4. 传输完成,中断通知 CPU优点:释放 CPU、提高效率
7.5 磁盘调度算法
算法 | 说明 | 特点 |
FCFS | 先来先服务 | 公平,但寻道时间长 |
SSTF | 最短寻道时间优先 | 效率高,可能饥饿 |
SCAN | 电梯算法,来回扫描 | 公平,响应时间可预测 |
C-SCAN | 单向扫描,返回起点 | 更均匀的等待时间 |
LOOK | SCAN 变体,不到边界 | 减少不必要移动 |
八、Linux 常用命令
8.1 进程管理
ps aux # 查看所有进程
ps -ef | grep nginx # 查找进程
top / htop # 实时监控
kill -9 PID # 强制终止
kill -15 PID # 优雅终止
nohup cmd & # 后台运行8.2 文件操作
ls -la # 列出文件详情
find . -name "*.log" # 查找文件
grep -r "pattern" . # 递归搜索内容
chmod 755 file # 修改权限
chown user:group file # 修改所有者8.3 网络工具
netstat -tunlp # 查看端口监听
ss -tunlp # 更快的 netstat
lsof -i :8080 # 查看端口占用
tcpdump -i eth0 # 抓包
curl -X POST url # HTTP 请求8.4 性能分析
vmstat 1 # 系统整体状态
iostat -x 1 # 磁盘 IO
free -h # 内存使用
df -h # 磁盘空间
sar -n DEV 1 # 网络流量
strace -p PID # 跟踪系统调用8.5 文本处理命令
# awk - 文本处理
awk '{print $1, $3}' file # 打印第1、3列
awk -F: '{print $1}' /etc/passwd # 指定分隔符
awk '$3 > 100 {print}' file # 条件过滤
awk '{sum += $1} END {print sum}' # 求和
# sed - 流编辑器
sed 's/old/new/g' file # 替换
sed -n '10,20p' file # 打印10-20行
sed -i 's/old/new/g' file # 原地修改
sed '/pattern/d' file # 删除匹配行8.6 CPU 100% 排查流程
# 1. 找到高 CPU 进程
top -c
# 2. 找到高 CPU 线程
top -Hp <PID>
# 3. 线程 ID 转十六进制
printf "%x\n" <TID>
# 4. 查看线程堆栈
jstack <PID> | grep -A 30 <TID_HEX>
# 5. 或使用 perf
perf top -p <PID>
perf record -p <PID> -g -- sleep 30
perf report常见原因:
- 死循环
- 频繁 GC
- 正则回溯
- 加密/压缩计算
8.7 内存泄漏排查流程
# 1. 查看内存使用
free -h
cat /proc/<PID>/status | grep -i mem
# 2. 查看进程内存映射
pmap -x <PID>
cat /proc/<PID>/smaps
# 3. 使用 valgrind(C/C++)
valgrind --leak-check=full ./program
# 4. Java 堆分析
jmap -heap <PID>
jmap -histo <PID>
jmap -dump:format=b,file=heap.bin <PID>8.8 磁盘 IO 高排查流程
# 1. 查看整体 IO
iostat -x 1
# 2. 找到高 IO 进程
iotop -o
# 3. 查看进程打开的文件
lsof -p <PID>
# 4. 跟踪 IO 系统调用
strace -e trace=read,write -p <PID>
# 5. 使用 blktrace
blktrace -d /dev/sda -o - | blkparse -i -关键指标:
- %util:磁盘繁忙度
- await:IO 等待时间
- r/s, w/s:每秒读写次数
8.9 日志查看命令
# tail - 查看文件末尾
tail -f /var/log/syslog # 实时跟踪
tail -n 100 file # 最后 100 行
# less - 分页查看
less +F file # 类似 tail -f
less +G file # 跳到文件末尾
# journalctl - systemd 日志
journalctl -u nginx # 查看服务日志
journalctl -f # 实时跟踪
journalctl --since "1 hour ago" # 时间过滤
journalctl -p err # 按级别过滤8.10 信号量 vs 互斥锁
特性 | 信号量 | 互斥锁 |
值范围 | 非负整数 | 0 或 1 |
用途 | 同步 + 互斥 | 仅互斥 |
所有权 | 无 | 有(谁加锁谁解锁) |
典型场景 | 生产者消费者 | 临界区保护 |
// 信号量:可以由不同线程 P/V
sem_wait(&sem); // 任意线程
// ...
sem_post(&sem); // 可以是另一个线程
// 互斥锁:必须由同一线程加锁解锁
pthread_mutex_lock(&mutex);
// ...
pthread_mutex_unlock(&mutex); // 必须是同一线程九、高频面试题与答案
Q1: 进程和线程的区别?
答案:
特性 | 进程 | 线程 |
定义 | 资源分配单位 | CPU 调度单位 |
地址空间 | 独立 | 共享进程空间 |
开销 | 大 | 小 |
通信 | IPC | 共享内存 |
安全性 | 隔离好 | 相互影响 |
Q2: 进程间通信方式有哪些?
答案:
1. 管道:半双工,父子进程
2. 消息队列:内核中消息链表
3. 共享内存:最快,需同步
4. 信号量:同步/互斥
5. 信号:异步通知
6. Socket:跨主机通信
Q3: 什么是死锁?如何避免?
答案:
死锁:多个进程互相等待对方持有的资源
四个必要条件:互斥、占有等待、不可剥夺、循环等待
避免方法:
1. 破坏必要条件(资源有序分配)
2. 银行家算法
3. 加锁顺序一致
4. 超时机制
Q4: 虚拟内存的作用?
答案:
1. 地址空间隔离:进程间互不干扰
2. 扩展内存:使用磁盘作为后备
3. 按需加载:只加载需要的页面
4. 内存共享:多进程共享代码段
5. 简化编程:统一的地址空间
Q5: 页面置换算法有哪些?
答案:
1. FIFO:先进先出
2. LRU:最近最少使用
3. LFU:最不经常使用
4. Clock:LRU 近似算法
LRU 实现:哈希表 + 双向链表,O(1) 操作
Q6: 用户态和内核态的区别?
答案:
特性 | 用户态 | 内核态 |
权限 | 受限 | 完全访问 |
资源访问 | 不能直接访问硬件 | 可以访问所有资源 |
指令 | 非特权指令 | 所有指令 |
切换时机:系统调用、中断、异常
Q7: select、poll、epoll 的区别?
答案:
特性 | select | poll | epoll |
fd 限制 | 1024 | 无 | 无 |
数据结构 | 数组 | 链表 | 红黑树+链表 |
拷贝 | 每次全量 | 每次全量 | 注册时一次 |
复杂度 | O(n) | O(n) | O(1) |
Q8: 什么是零拷贝?
答案:
- 减少 CPU 拷贝次数的技术
- sendfile:内核直接发送,不经过用户空间
- mmap:用户空间映射内核缓冲区
- 应用:Kafka、Nginx
Q9: fork() 的原理?
答案:
1. 创建子进程 PCB
2. 复制父进程地址空间(写时复制 COW)
3. 复制文件描述符表
4. 父进程返回子进程 PID,子进程返回 0
写时复制(COW):fork 后父子共享内存页,写入时才复制
Q10: 什么是僵尸进程和孤儿进程?
答案:
僵尸进程:
- 子进程退出,父进程未调用 wait() 回收
- 占用 PID 和进程表项
- 解决:父进程 wait() 或 SIGCHLD 处理
孤儿进程:
- 父进程先退出
- 被 init(1) 进程收养
- 无危害
最后更新:2026-01-09






Loading Comments...