这些概念在面试与排障中最容易被混为一谈。每张表给「一句话区别 + 判据 + 常见误用」。
| 项 | 进程 | 线程 |
| 地址空间 | 独立 | 共享 |
| fd 表 | 独立(fork 复制) | 共享 |
| 信号处置 | 独立 | 共享(掩码每线程) |
| 栈 | 独立内核栈 + 用户栈 | 独立栈(默认 8 MB 虚拟) |
| 切换成本 | 高(页表/TLB) | 低(同地址空间) |
| 崩溃影响 | 通常只死自己 | 整个进程退出 |
判据:需要隔离/独立资源 → 进程;需要共享大量数据/低切换成本 → 线程。容器是「进程 + namespace/cgroups」,不是线程。
| 原语 | 等待方式 | 适用 | 不能做什么 |
互斥锁 mutex | 睡眠 | 临界区可能长/可能阻塞 | 不能在信号处理器里用 |
自旋锁 spinlock | 忙等 | 极短临界区、不可睡眠上下文 | 持锁睡眠会死锁 |
信号量 semaphore | 睡眠/计数 | 资源计数、生产者-消费者 | 不保证所有权,易误用 |
| 条件变量 | 睡眠+唤醒 | 等某个条件成立 | 必须配互斥锁、while |
| 读写锁 | 睡眠 | 读多写少 | 写者可能饿死 |
| RCU | 无锁读 | 读多写极少、可容忍旧值 | 读侧不能睡眠 |
| 原子操作 | 不等待 | 计数/标志/无锁结构 | 复杂逻辑易错 |
判据:临界区能否睡眠?能 → mutex/信号量;不能(中断/原子上下文)→ spinlock/原子;读多写少 → rwlock/RCU。
| 机制 | 复杂度 | fd 上限 | 触发 | 适用 |
select | O(n) | FD_SETSIZE(常 1024) | LT | 兼容旧代码 |
poll | O(n) | 无硬上限 | LT | 少量 fd、可移植 |
epoll | O(1) 取就绪 | 受 fd 限制 | LT/ET | 大量连接 |
io_uring | 批量提交/完成 | 受 ring 大小 | 完成事件 | 高吞吐、低延迟、异步 |
判据:连接数上千 → epoll;要极致 IOPS/避免系统调用 → io_uring;跨平台/少量 fd → poll。
| 项 | LT(水平触发) | ET(边缘触发) |
| 通知时机 | 只要就绪就通知 | 状态变化时通知一次 |
| 是否必须读完 | 否 | 是,读到 EAGAIN |
| fd 是否必须非阻塞 | 否 | 是 |
| 编程难度 | 低 | 高,易丢事件 |
| 默认 | epoll 默认 | 需显式 EPOLLET |
常见误用:ET 只 read 一次;ET 用阻塞 fd;ET 忘记处理 EPOLLHUP。
| 项 | fork | vfork | clone |
| 地址空间 | COW 复制 | 共享,子进程先运行 | 按 flags 选择 |
| 父进程阻塞 | 否 | 是,直到子进程 exec/exit | 否 |
| 用途 | 通用 | 历史优化(立即 exec) | 线程/命名空间 |
| 风险 | 大内存页表复制 | 子进程改数据会破坏父进程 | flags 组合复杂 |
判据:现代代码用 fork + exec 或 posix_spawn;线程用 pthread_create(内部 clone);不要用 vfork。
| 项 | 硬链接 | 软链接 |
| inode | 同一 inode | 新 inode,内容是路径 |
| 跨文件系统 | 不可以 | 可以 |
| 指向目录 | 不允许(普通用户) | 可以 |
| 原文件删除 | 仍可访问(nlink 减 1) | 悬空 |
ls -l | 普通文件 | l 类型 + -> |
判据:需要同一文件多个名字 → 硬链接;需要跨 FS/目录/可悬空 → 软链接。
| 操作 | 保证 |
write | 数据到 page cache(可能还没下盘) |
fdatasync | 数据 + 必要元数据到持久介质 |
fsync | 数据 + 元数据到持久介质 |
O_SYNC | 每次 write 同步完成 |
O_DSYNC | 每次 write 数据同步(元数据按需) |
sync | 全系统回写(不等单个文件) |
常见误用:write 返回就认为落盘;fsync 后忘记 fsync 目录(rename 场景)。
| 项 | 缓冲 I/O | O_DIRECT |
| 经过 page cache | 是 | 否 |
| 对齐要求 | 无 | 地址/偏移/长度按块对齐 |
| 适用 | 通用 | 数据库/存储引擎自管缓存 |
| 风险 | 缓存污染/回写抖动 | 对齐错误 EINVAL、性能反而差 |
判据:没有自管缓存与对齐能力,不要用 O_DIRECT。
| 项 | 说明 |
malloc | libc 堆分配器;小块从 arena,大块可能 mmap |
brk | 调整堆顶;连续、易碎片 |
mmap | 映射匿名页/文件;大块、可 munmap |
slab | 内核对象缓存,不是用户态 malloc |
判据:用户态用 malloc;大块/文件映射用 mmap;内核用 kmalloc/vmalloc/slab。
| 项 | read | pread |
| 偏移 | 使用并推进 f_pos | 指定偏移,不改 f_pos |
| 多线程共享 fd | 互相影响 | 安全 |
| 原子性 | 读+推进偏移是原子的 | 读+定位是原子的 |
判据:多线程读同一文件用 pread;顺序流用 read。
| 项 | flock | fcntl(F_SETLK) |
| 粒度 | 整个文件 | 字节范围 |
| 继承 | 随 fd 复制 | 随进程(关闭任一 fd 可能释放) |
| 网络 FS | 依赖实现 | 依赖实现 |
| 用途 | 简单互斥 | 数据库/文件区域锁 |
判据:只要「单实例」用 flock;要锁文件区域用 fcntl。
| 项 | TCP | UDP |
| 连接 | 面向连接 | 无连接 |
| 可靠 | 重传/排序/去重 | 不保证 |
| 边界 | 字节流 | 数据报 |
| 拥塞控制 | 有 | 无(应用自管) |
| 适用 | HTTP/SSH/DB | DNS/视频/游戏/QUIC 底层 |
判据:需要可靠有序 → TCP;需要低延迟/自定义可靠性 → UDP + 应用层。
| 状态 | 谁进入 | 原因 | 处理 |
TIME_WAIT | 主动关闭方 | 等 2MSL,处理迟到报文 | 正常;端口耗尽才调 tcp_tw_reuse |
CLOSE_WAIT | 被动关闭方 | 收到 FIN,应用没 close | 修应用;调内核无用 |
判据:ss -tan state time-wait 多 → 正常/端口规划;close-wait 多 → 代码 bug。
| 项 | SO_REUSEADDR | SO_REUSEPORT |
| 作用 | 允许绑定 TIME_WAIT/通配地址冲突 | 多个 socket 绑同一端口,内核负载均衡 |
| 用途 | 服务快速重启 | 多进程/多线程 acceptor |
| 风险 | 可能绑到不该绑的地址 | 连接分布不均、迁移复杂 |
判据:重启报 EADDRINUSE → SO_REUSEADDR;要多 acceptor → SO_REUSEPORT。
| 项 | 静态 | 动态 |
| 库代码 | 复制进可执行文件 | 运行时加载 .so |
| 体积 | 大 | 小 |
| 升级 | 需重编 | 换 .so 即可(ABI 兼容) |
| 依赖 | 少 | ldd/RPATH/LD_LIBRARY_PATH |
| 安全 | 无共享 | 可 LD_PRELOAD 打桩 |
判据:容器/单文件分发 → 静态(或 musl);系统库/多程序共享 → 动态。
| 项 | syscall(x86-64) | int 0x80(32 位) |
| 入口 | syscall 指令 | 中断门 |
| 号寄存器 | %rax | %eax |
| 参数 | %rdi/%rsi/%rdx/%r10/%r8/%r9 | %ebx/%ecx/%edx/%esi/%edi/%ebp |
| 性能 | 快 | 慢 |
| 64 位程序 | 正确 | 指针截断,不要用 |
| 项 | CFS | EEVDF |
| 版本 | 2.6.23–6.5 | 6.6+ |
| 核心 | vruntime 红黑树 | lag/vslice,按延迟需求分配 |
| 调参 | sched_latency 等 | sched_base_slice 等 |
| 读旧资料 | 仍可理解公平性 | 注意字段/默认值变化 |
| 项 | namespace | cgroup | 虚拟机 |
| 隔离什么 | 看见什么(PID/网络/挂载…) | 用多少(CPU/内存/IO) | 整台硬件 |
| 内核 | 共享 | 共享 | 独立 |
| 启动 | 毫秒 | 毫秒 | 秒级 |
| 安全边界 | 较弱(共享内核) | 资源限制 | 强(虚拟化) |
判据:容器 = namespace + cgroup + rootfs;不是轻量虚拟机。
| 项 | iptables | nftables |
| 规则集 | 表/链线性 | 表/链/集合/映射 |
| 原子更新 | 逐条 | nft -f 原子 |
| 多族 | 分 iptables/ip6tables | inet 统一 |
| 语法 | -A/-j | nft add rule |
判据:新系统用 nftables;旧脚本用 iptables-nft 兼容层,逐步迁移。
| 问题 | 先问 |
| 要隔离还是共享? | 进程/线程、容器/VM |
| 临界区能睡眠吗? | mutex vs spinlock |
| 连接多还是少? | epoll vs poll |
| 要可靠还是低延迟? | TCP vs UDP |
| 要持久化还是只要下盘? | write vs fsync |
| 要共享内核还是独立内核? | 容器 vs VM |
回 R.6 术语表;做题去 R.8 综合自测卷。