用 C++ 从零实现 Shell
进程、管道、信号、作业控制——这些「面试必问」的硬核知识点,最好的学法就是亲手写一个 Shell。
1–2 周,从零实现一个可运行、可验收、可写进简历的 mini-shell。
周期
约 1–2 周·自定进度
节奏
5 个 MVP 阶段·每步可验收
交付
可用 mini-shell + 测试脚本
形式
结构化自学 + 图文实战
课程定位
系统编程综合实验 · 项目驱动 · 可复现
Outcomes
你将收获
把 fork/exec/wait 的进程模型讲透:理解“为什么父子进程会同时往下走”。
写出可测试的 tokenizer/parser,把输入稳定地变成 AST,而不是到处 if/else。
真正掌握重定向与管道:close/dup2 的文件描述符接线不再玄学。
搞懂 Ctrl-C/Ctrl-Z 的信号机制:能让 Shell 自己不被打断,并支持前台/后台作业。
沉淀一套可复现的调试与测试方法:单测验证解析,集成测试验证执行。
用 **8 种设计模式** 把代码重构成“工程”:命令/组合/解释器/访问者/策略/状态/适配器/外观模式 + RAII 实战落地。
Teaching Philosophy
教学理念:把 Shell 当作系统课的“总线”
你可以把这门课当作一门“系统编程综合实验”。我们不从语法大全开始,而是从一串很现实的问题开始:
为什么 fork() 之后父子进程会同时往下走?为什么 execve() 一旦成功就回不来了?管道到底是什么,为什么它能把两个进程连起来?
Ctrl-C 为什么会打断程序?它打断的是谁?前台/后台在内核里落在哪些结构上?
每一章都是一个可验收的小里程碑:用单元测试验证 tokenizer/parser,用集成测试验证执行行为,用可观测日志与可复现脚本定位 bug。
每一步都可验证
先让它能用,再逐步逼近真实 Shell 的交互与边界。
解析与执行分层,避免把逻辑写成不可维护的面条代码。
围绕可复现脚本与日志调试,让 bug 有迹可循。
Learning Blueprint
学习路线图
先把 prompt 立起来,再把最简单的外部命令跑起来。你能看到 mini$ ls。
让它能稳定地理解输入:tokenizer + parser + 最小 builtin(cd/exit)。你能跑 mini$ cd ..。
开始做 Shell 最核心的“接线”:重定向与管道。你能跑 mini$ cat a.txt | grep hi > out.txt。
处理信号与作业控制:Ctrl-C 不会把 Shell 自己送走;Ctrl-Z 能停住前台任务;支持 fg/bg/jobs。
在可用基础上做“顺手/可维护/出错不含糊”:更清晰的错误路径、日志、测试策略与重构。
功能跑通后再做架构:命令/组合/解释器/访问者/策略/状态/适配器/外观 + RAII,把代码变成“工程”。
Who Is This For
适合人群
- 正在学习 OS / Unix 系统编程,希望把进程、文件、信号、作业控制串成一个“能跑起来”的综合实验的人。
- 做 C/C++ 工程但对 fork/exec/pipe/signal 只是“见过”,想补齐可迁移的系统能力的人。
- 准备面试系统/后端岗位,希望用一个 mini-shell 项目做硬核作品集的人。
- 喜欢造轮子,希望把日常终端命令背后的机制亲手复现一次的人。
Deliverables
交付物与资产
- 一个可运行的 mini-shell(prompt + 外部命令 + 管道/重定向 + 信号与作业控制基础)。
- tokenizer/parser 的单元测试与一组可复现的执行集成测试脚本。
- 一份“为什么这样实现”的设计复盘:进程组、终端控制权、信号策略等关键点。
- 一段可直接写进简历的项目描述与面试问答要点。
Resume-Ready
简历怎么写(可直接复制)
一句话版本
建议放在:项目经历 / 亮点用 C++ 从零实现 mini-shell(POSIX):支持外部命令执行、管道/重定向、信号与作业控制(fg/bg/jobs),并以单元测试/集成测试验证 tokenizer/parser 与执行行为。完整简历示例(三种风格)
版本 A · 偏技术深度
mini-shell(C++ / POSIX)
- • 设计并实现递归下降 parser,将命令行输入构建为 AST;支持引号/转义、环境变量展开与 glob 通配
- • 基于 fork/exec/wait 实现进程生命周期管理,正确处理僵尸进程回收与错误码传递
- • 实现多级管道与重定向(> / < / 2> / >> / |):用 close/dup2 完成 FD 接线,无泄漏
- • 实现作业控制:进程组 + tcsetpgrp 切换终端控制权,支持 Ctrl-C/Ctrl-Z 与 jobs/fg/bg
- • 单元测试覆盖 tokenizer/parser 边界用例 50+;集成测试脚本覆盖 25 组典型命令组合
- • 用命令/组合模式统一命令执行模型;访问者模式解耦 AST 遍历与执行逻辑
版本 B · 偏工程实践
mini-shell(C++ / POSIX)
- • 从零实现 POSIX 兼容的交互式 shell,支持外部命令、管道、重定向与作业控制
- • 采用模块化架构:tokenizer → parser → executor 解耦,便于单元测试与后续扩展
- • 编写 50+ 单元测试验证解析边界;20+ 集成脚本覆盖 pipeline、重定向、信号等场景
- • 实现 FD 生命周期追踪与泄漏检测;错误路径统一处理,提供清晰的用户提示
- • README 提供一键构建与 5 条验证脚本,支持 CI 集成
- • 设计模式重构:策略模式抽象 PATH 查找;适配器/外观模式封装系统调用,支持 mock 测试
版本 C · 偏学习成长
mini-shell(C++ / POSIX)
- • 系统学习 POSIX 进程模型(fork/exec/wait)与文件描述符机制,并通过项目实践落地
- • 独立实现 tokenizer + parser,掌握词法分析与语法树构建的基本方法
- • 深入理解管道与重定向原理,能清晰解释 close/dup2 的 FD 接线逻辑
- • 掌握信号与作业控制机制:进程组、终端控制权、前台/后台切换
- • 建立「先写测试再实现」的工程习惯,单元测试与集成测试覆盖核心路径
- • 掌握状态模式管理作业状态机;RAII 自动归还 fd/signal mask/终端控制权
以上三个版本侧重点不同,建议根据目标岗位选择或混搭——技术深度、工程能力、学习成长各有侧重,结合你的实际数据调整细节。
面试常见追问
- 为什么 cd/exit 不能用 fork+exec?
- 管道两端 close 的顺序有讲究吗?
- Ctrl-C 打断的是谁?shell 还是子进程?
- 如何避免僵尸进程?waitpid 的 flags?
简历注意事项
- 数字要真实:测试用例数、覆盖率等要能自圆其说
- 准备好 demo:面试官可能要求现场演示
- README 要清晰:一键构建 + 典型用例
- 能讲清「为什么这样设计」比「做了什么」更重要
技术栈一览
- 语言:C++17 / 现代风格
- 系统调用:fork / exec / wait / pipe / dup2
- 信号处理:sigaction / tcsetpgrp
- 测试:单元测试 + 集成脚本
- 设计模式:命令 / 访问者 / 策略 / 状态 / RAII
Interview Talk Track
面试怎么展开讲(你可以照着讲)
解释“builtin 必须在父进程修改状态”:cd 改变当前进程工作目录;exit 结束 shell 本身。
为什么 fork 后父子都往下走;exec 成功后为何不会返回;如何用 wait/waitpid 回收子进程避免僵尸。
描述每个进程应关闭哪些端以避免阻塞;为什么必须关掉未使用的 pipe fd;如何串联多级 pipeline。
讲清楚前台进程组与信号投递:shell 自己如何忽略/转发信号;fg/bg 如何切换终端控制权。
Make It Quantified
让简历更有杀伤力:量化与复现模板
把“支持了什么”变成清单:外部命令、引号/转义、重定向、pipeline、jobs/fg/bg、错误码与提示等。
把“可靠性”写出来:你写了多少 tokenizer/parser 单测、多少条集成脚本(把数字填进去会非常加分)。
把“难点决策”写出来:进程组/终端控制权/信号策略、FD 生命周期管理、错误处理与回收策略。
附上可复现方式:README 给出 3–5 条一键脚本用例(例如带管道与重定向的组合命令)。
Syllabus
章节目录
为什么要自己写 Shell?
01-why-build-shell.md
创世纪:一个最小 REPL(prompt + read + run)
02-genesis-min-repl.md
进程模型速成:fork/exec/wait 的三件套(先跑通绝对路径:/bin/ls)
03-process-model-fork-exec-wait.md
Tokenizer:把一行命令切成 token
04-tokenizer.md
Parser:从 token 到 AST(先支持“简单命令”)
05-parser-ast.md
命令查找(PATH):把 execvp 的黑盒掀开
06-path-and-execve.md
Builtins I:cd / exit / pwd(为什么它们不能用 exec)
07-builtins-cd-exit-pwd.md
引号与转义:让 echo "a b" 正常工作
08-quotes-and-escape.md
环境变量与展开:$PATH、$HOME、最小 glob(可选)
09-env-and-expansion.md
重定向:> < 2> 的文件描述符接线
10-redirection.md
| 的本质是一串 close/dup2
11-pipeline.md
Signals:Ctrl-C 打断的是谁?(为作业控制铺路)
12-signals.md
作业控制 I:前台/后台与进程组 (process group)
13-job-control-process-group.md
作业控制 II:jobs/fg/bg 与终端控制权
14-job-control-fg-bg-jobs.md
行编辑与历史:把交互做“顺手”(最小实现,可选加餐)
15-line-editing-history.md
重构与测试策略:让 Shell 可维护
16-refactor-and-tests.md
总结与下一步:从 Shell 走向 OS / 编译原理 / 容器
17-summary-next-steps.md
设计模式重构 I:命令与语法树(命令 + 组合/解释器模式)
18-design-pattern-command-composite.md
设计模式重构 II:解耦执行与规则(访问者 + 策略模式)
19-design-pattern-visitor-strategy.md
设计模式重构 III:作业控制与资源归还(状态模式 + RAII)
20-design-pattern-state-raii.md
设计模式重构 IV:系统调用封装与可测试性(适配器 + 外观模式)
21-design-pattern-adapter-facade.md
Scope
本课程的“边界”
不追求 bash/zsh 的完整兼容;追求每一小步都能验证、能解释、能复现。
以 POSIX/类 Unix(macOS/Linux)作为统一基线;Windows 也可通过 WSL / 容器 / 虚拟机等方式完成全部实践。
容易拖进深坑的特性会标为可选挑战:完整 glob、子 shell、脚本语言、补全等。
Next Step
想要课程大纲、价格或开班时间?
扫描右侧二维码添加微信,备注「shell」,获取 mini-shell 课程大纲、价格与开班时间。
微信扫码添加 · 备注「shell」