CS Basics

计算机基础知识

操作系统 · 组成原理 · 网络 · Linux/容器 · 编程工程 · 分布式与 AI Infra

osarchitecturenetworkinglinuxcppdistributed
当前模块
学习进度0 / 7
Module Switcher
计算机基础知识
系统与硬件2
网络与容器2
AI 视角与工程3
内容模块

操作系统基础

基础★☆☆⏱ 75 min

一句话结论

进程是资源隔离单位、线程是内核调度单位、协程是用户态调度的轻量执行单元——三者按「谁拥有资源、谁被内核调度、谁在用户态切换」分层,切换成本依次降低,隔离性依次减弱。

复习定位

维度内容
所属模块操作系统基础
章节类型概念类
解决问题围绕进程线程、调度、虚拟内存、IO、多路复用、死锁、观测和 AI Infra OS 问题建立系统基础答案。
面试抓手先讲定义,再讲链路,最后讲 AI Infra 中如何使用或排障。

进程、线程、协程:资源边界和调度主体

操作系统面试里,进程、线程、协程不是三个孤立定义,而是三种不同层次的执行模型。核心问题是:谁拥有资源,谁被内核调度,谁在用户态切换。

概念资源边界调度者切换成本适合场景典型风险
进程独立虚拟地址空间、文件描述符、信号处理、资源限制内核最高强隔离、多服务、多 worker、容器主进程IPC 成本高,共享状态复杂
线程共享进程地址空间,独立栈和寄存器上下文内核中等CPU 并行、I/O 并发、推理 worker pool锁竞争、数据竞争、死锁、false sharing
协程运行在线程内,共享进程/线程资源用户态 runtime最低高并发 I/O、异步 RPC、事件循环阻塞调用会卡住调度线程,不能自动利用多核
一句话:进程是资源隔离单位,线程是内核调度单位,协程是用户态调度的轻量执行单元。

上下文切换到底切什么

切换类型需要保存/恢复代价来源排查信号
进程切换寄存器、内核栈、页表/地址空间、调度状态TLB 失效、cache 污染、内核调度pidstat -wvmstat cs
线程切换寄存器、线程栈、调度状态内核调度、cache 污染、锁等待top -Hperf sched
协程切换用户态栈/状态机、少量寄存器runtime 调度,通常无需内核态runtime profiler、事件循环延迟
Q: 进程、线程、协程有什么区别?面试怎么回答?

回答思路:先按资源隔离、调度主体和切换成本三条线回答,再补适用场景。

进程

进程拥有独立地址空间和资源边界,隔离性最好,适合服务拆分和容器主进程,但跨进程通信成本较高。

线程

线程共享进程地址空间,是内核实际调度的执行实体,适合多核并行,但需要处理锁、数据竞争和死锁。

协程

协程是用户态调度的轻量执行单元,适合 I/O 密集和高并发,但单个线程内的协程不能自动利用多核,阻塞调用会影响整个调度线程。

面试口径:进程看隔离,线程看并行,协程看轻量异步;不要只背定义,要说出调度和资源边界。

关联模块

  • GPU 硬件与资源共享:提供硬件、显存、互联和利用率诊断基础。
  • LLM 推理系统 / 分布式训练:提供大模型系统中的实际落点。
  • Kubernetes / 调度与集群:提供平台、资源和多租户治理语境。
  • 专题综合题 / 论文工作:把基础知识组织成可复述的方案和项目叙事。

一句话结论

死锁是一组线程互相持有对方需要的资源、谁都无法继续,需要互斥、持有并等待、不可剥夺、循环等待四个条件同时成立,破坏任意一个即可预防。工程上最实用的是破坏循环等待——所有线程按统一顺序加锁,再用锁超时兜底,比背银行家算法更落地。

复习定位

维度内容
所属模块操作系统基础
章节类型概念类
解决问题围绕进程线程、调度、虚拟内存、IO、多路复用、死锁、观测和 AI Infra OS 问题建立系统基础答案。
面试抓手先讲定义,再讲链路,最后讲 AI Infra 中如何使用或排障。

死锁:四个必要条件

死锁是指一组进程/线程互相持有对方需要的资源,谁都无法继续。下面四个条件同时满足才会发生,破坏任意一个即可预防。

条件含义破坏方式
互斥资源同一时刻只能被一个持有资源可共享化(多数难破坏)
持有并等待持有资源的同时等待新资源一次性申请全部资源
不可剥夺资源不能被强行抢走允许超时释放、可抢占
循环等待存在环形等待链按全局固定顺序加锁
工程上最常用、最实用的手段是破坏“循环等待”——所有线程按统一顺序加锁。

处理策略:预防 / 避免 / 检测 / 恢复

策略做法代价
预防破坏四条件之一(如固定加锁顺序)降低并发或资源利用率
避免运行时判断是否进入不安全状态(银行家算法)需预知最大需求,实际少用
检测构建资源分配图找环检测有开销
恢复杀进程、回滚、抢占资源有副作用,需可重试

大多数业务系统采用预防 + 超时:统一加锁顺序避免大部分死锁,再用锁超时(try_lock + 超时回退)兜底,而不是上线复杂的银行家算法。

经典死锁代码(加锁顺序不一致)


// 线程 A: lock(mutex1) -> lock(mutex2)
// 线程 B: lock(mutex2) -> lock(mutex1)   <-- 顺序相反,可能死锁

// 修复:所有线程统一按地址/ID 顺序加锁
std::lock(mutex1, mutex2);            // C++ 一次性获取,避免顺序问题
std::lock_guard g1(mutex1, std::adopt_lock);
std::lock_guard g2(mutex2, std::adopt_lock);

死锁排查与活锁/饥饿区分

现象特征排查/区分
死锁线程互相等待,CPU 不忙但卡死gdb / pstack 看线程栈都停在 lock;jstack(Java)能直接报 deadlock
活锁线程不停重试却都没进展,CPU 很忙加随机退避打破对称
饥饿某线程长期抢不到资源用公平锁、优先级 aging

和 AI Infra / 分布式的联系

死锁不只在单机锁里出现:① 分布式训练中,集合通信(如 NCCL all-reduce)要求所有 rank 都参与,如果某个 rank 因异常没进入通信原语,其他 rank 会一直等待,表现为整作业 hang(本质是分布式死锁/挂起);② 资源调度中,多个大作业各占一部分 GPU 又都等不到完整资源,形成资源死锁,需靠 gang scheduling(要么全给要么不给)破解;③ 数据库/分布式锁跨服务加锁顺序不一致同样会死锁。排查训练 hang 常用 py-spy dump / 看各 rank 卡在哪一步。

Q: 如何预防死锁?工程上最实用的办法是什么?

理论上破坏四个必要条件之一即可,但互斥和不可剥夺往往难破坏。工程上最实用的是破坏循环等待:给所有锁定义全局顺序,任何线程都按同一顺序加锁,环就不可能形成。再辅以锁超时 + 可重试兜底,以及减小锁粒度、缩短临界区、能用无锁结构就用无锁。

面试口径:先说四条件,再落到“统一加锁顺序 + 超时回退”这种可落地的方案,比背银行家算法更有说服力。
Q: 分布式训练任务 hang 住了,可能和死锁有什么关系?

集合通信是同步屏障,要求所有 rank 一起到达。如果某个 rank 提前报错退出、走了不同的代码分支、或数据加载卡住没进入 all-reduce,其余 rank 会在通信原语上无限等待,整个作业 hang——这是一种分布式层面的“互相等待”。排查时用 py-spy/栈抓取看各 rank 卡在哪,常见根因是 rank 间逻辑分支不一致或某卡 OOM/异常。

关联模块

  • GPU 硬件与资源共享:提供硬件、显存、互联和利用率诊断基础。
  • LLM 推理系统 / 分布式训练:提供大模型系统中的实际落点。
  • Kubernetes / 调度与集群:提供平台、资源和多租户治理语境。
  • 专题综合题 / 论文工作:把基础知识组织成可复述的方案和项目叙事。

一句话结论

进程是资源分配单位、线程是执行调度单位、协程是用户态轻量执行流。AI Infra 里这套并发模型直接决定数据加载吞吐和推理服务并发:Python 数据预处理用多进程绕过 GIL,推理前后处理和网络收发用线程池或协程。线程过多反而会让 CPU 时间耗在上下文切换和锁竞争上。

复习定位

维度内容
所属模块操作系统基础
章节类型概念类
解决问题围绕进程线程、调度、虚拟内存、IO、多路复用、死锁、观测和 AI Infra OS 问题建立系统基础答案。
面试抓手先讲定义,再讲链路,最后讲 AI Infra 中如何使用或排障。

AI Infra 面试模块:进程、线程与并发模型

AI Infra 面试里,进程、线程和协程不是概念背诵题,而是资源隔离、调度成本、数据加载吞吐和推理服务并发模型的基础。

核心概念定义

概念定义面试重点
进程操作系统分配资源的基本单位,有独立虚拟地址空间、文件描述符表、信号处理和权限上下文。隔离性强,崩溃影响小;创建和切换成本高于线程。
线程进程内的执行流,共享进程地址空间和打开文件,拥有独立栈、寄存器上下文和调度状态。通信方便、切换较轻;需要处理锁竞争、数据竞争和内存安全。
协程用户态轻量执行流,由语言运行时或框架调度,通常用于大量 I/O 等待型并发。切换不必进入内核;阻塞系统调用会阻塞承载线程,必须配合非阻塞 I/O。
IPC进程间通信机制,包括 pipe、socket、shared memory、message queue、signal。共享内存最快但同步复杂;socket 通用但有拷贝和协议开销。
同步原语mutex、rwlock、semaphore、condition variable、barrier 等。要能说明互斥、通知、资源计数、阶段同步分别适合什么场景。

需要掌握

  • 进程与线程的区别:地址空间、资源隔离、崩溃影响范围、上下文切换成本。
  • 用户态线程与内核态线程的区别:用户态切换快,内核态线程可被 OS 真正调度到多核。
  • 多进程、多线程在 CPU 密集型和 I/O 密集型任务中的适用场景。
  • 线程池为什么存在:避免频繁创建销毁线程,限制并发度,保护下游资源。
  • 协程为什么适合网络服务:大量连接多数时间在等待 I/O,用少量线程承载大量逻辑执行流。
  • 死锁产生条件:互斥、占有且等待、不可剥夺、循环等待。

上下文切换成本来自哪里

上下文切换要保存和恢复寄存器、程序计数器、栈指针、调度状态。进程切换还可能切换地址空间和页表,破坏 TLB;线程切换虽然共享地址空间,但仍会破坏 cache locality。大量线程竞争锁或频繁阻塞唤醒,会导致 CPU 时间消耗在调度和内核态,而不是业务计算。

AI Infra 相关关注点

  • DataLoader 多进程加载数据可以绕过 Python GIL,但会引入进程间队列、pickle/IPC、shared memory 和 copy-on-write 成本。
  • Python GIL 会限制纯 Python CPU 密集型多线程并行,训练框架常用多进程、C++ 后端释放 GIL、CUDA 异步执行来绕过。
  • 推理服务常用线程池处理 tokenizer、detokenizer、网络收发、日志和业务逻辑;线程过多会造成 context switch 和锁竞争。
  • CPU 线程数、NUMA 绑核、DataLoader worker 数量会影响 GPU feeding,CPU 准备 batch 不连续会让 GPU 周期性空转。
  • 多 worker 数据预处理要关注队列深度、worker 异常退出、共享内存大小和主进程是否及时消费。

高频问题

Q: 进程和线程有什么区别?什么时候用多进程,什么时候用多线程?

进程是资源分配单位,线程是执行调度单位。进程有独立地址空间,隔离性强,适合强隔离、绕过 GIL、崩溃不互相影响的场景;线程共享地址空间,通信方便,适合 I/O 密集、共享状态多、延迟敏感的服务。AI Infra 中,Python 数据预处理常用多进程,推理服务前后处理和网络收发常用线程池或协程。

Q: mutex 和 semaphore 的区别是什么?

mutex 是互斥锁,用来保护临界区,一般强调谁加锁谁解锁;semaphore 是计数信号量,用来表示可用资源数量,可以允许多个执行流同时进入。连接池容量、GPU slot、队列容量更像 semaphore;共享 map、调度器状态更新更适合 mutex/rwlock。

Q: Python 多线程为什么不能很好利用多核 CPU?

CPython 有 GIL,同一时刻通常只有一个线程执行 Python bytecode,所以纯 Python CPU 密集型多线程不能真正并行利用多核。但 I/O 阻塞、C++ 扩展、CUDA kernel launch 等可能释放 GIL,因此训练框架常通过 C++/CUDA 后端、多进程 DataLoader 和异步 pipeline 提升吞吐。

关联模块

  • GPU 硬件与资源共享:提供硬件、显存、互联和利用率诊断基础。
  • LLM 推理系统 / 分布式训练:提供大模型系统中的实际落点。
  • Kubernetes / 调度与集群:提供平台、资源和多租户治理语境。
  • 专题综合题 / 论文工作:把基础知识组织成可复述的方案和项目叙事。