南京大学 操作系统 (JYY) 学习笔记:终端、进程组与 UNIX Shell 的前世今生

发布时间:2026/7/27 13:56:05

南京大学 操作系统 (JYY) 学习笔记:终端、进程组与 UNIX Shell 的前世今生
写在前面这是本系列的第八篇。前面的课程中我们已经知道如何用“文件描述符”相关的系统调用访问操作系统中的对象open,read,write,lseek,close也知道了如何用pipe创建管道对象。但操作系统中的对象远不止于此。今天我们要深入一个每天都在用但细思恐极的对象——终端 (Terminal)。从机械打字机开始一探究竟Ctrl-C到底做了什么并在此基础上剖析 UNIX Shell 的底层原理。终端的历史从机械打字机到显示器我们今天在键盘上敲击的每一个按键在终端里看到的每一次换行都刻满了 100 多年前机械时代的烙印。打字机时代遗产 (Typewriter, 1860s)QWERTY 键盘:这其实是为了降低打字速度而设计的防卡纸方案。因为早期的纯机械结构按键太快会导致字锤卡在一起。Shift 键:按住它机械结构会使字锤或字模向上移动一段距离从而切换字符集打出大写字母或符号。毕竟大小写各占一个物理按键太浪费空间了。回车与换行 (CR LF):\rCR (Carriage Return - 回车):将物理打印头移回行首。\nLF (Line Feed - 换行):滚筒转动将纸张向上移动一行。在 UNIX 中\n在逻辑上同时包含了 CR 和 LF 的动作而在 Windows 中历史遗留问题导致换行依然严格保留着\r\n两个字符。Tab Backspace:它们在早期本质上是“位置移动”指令。在纸上打错字是去不掉的所以早期的 Backspace 配合减号使用退格然后打个横线表示“错了划掉”。封神之路VT100 终端 (1978)为了发电报人们发明了电传打字机 (Teletypewriter, 即 TTY)。后来加入了屏幕进化成了 Video Teletypewriter。DEC 公司在 1978 年推出的VT100终端成为了事实上的行业标准。它是首个完整实现ANSI Escape Sequence (转义序列)的终端。80 列 × 24 行的字符显示成为了至今深深影响终端窗口的标准布局。今天我们用的终端伪终端 (Pseudo Terminal)你在系统里比如 VSCode、Tmux、SSH可以随意创建无数个黑框框它们其实都是伪终端 (PTY)。PTY一对双向通信的“管道”伪终端在底层是成对出现的主设备 (PTY Master):连接到你看到的那个“终端模拟器”黑框框 GUI 界面。从设备 (PTY Slave):连接到 Shell 或其他后台程序例如/dev/pts/0。终端模拟器是怎么实现的现在利用学过的系统调用你也可以自己写一个终端模拟器了调用openpty()函数通过/dev/ptmx向操作系统申请一个新终端。系统会返回两个文件描述符Master 和 Slave。调用fork()创建子进程。子进程把stdin/stdout/stderr全部重定向指向 Slave然后execve执行bash。父进程从 Master 读取子进程的输出渲染画到屏幕上把你敲击的键盘按键写入 Master 传给子进程。极客扩展既然是通过转义序列 (Escape Sequence) 渲染字符那能不能渲染图片现代终端如 Kitty扩展了转义序列允许控制大小、位置甚至动画例如\033[60C_开头\033\结尾传递图像数据。未来终端内嵌 WebView 也是大势所趋。终端的模式与属性终端的两种模式Canonical Mode (规范模式 / 按行处理):你敲击键盘时程序读不到数据终端自己提供行编辑功能比如退格修改。直到你按下回车整行数据才会被发送给程序。Non-canonical Mode (非规范模式 / 按字符处理):你按下的每一个字符都会立即发送给程序。用于实现交互式程序比如 Vim、SSH。终端属性控制 (tcgetattr / tcsetattr)这些 API 可以控制终端的各种行为回显、信号处理、特殊字符拦截等。比如当你在终端输入密码时看不见字符底层就是调用了这个 API 临时关闭了“终端回显”功能。进程管理与 Job Control (作业控制)当系统里有一大棵复杂的进程树有的在前台有的在后台这时你按下Ctrl-C到底该终止哪个进程答案终端才不管呢终端非常蠢它只管传输字符。Ctrl-C本质上只是发送了一个 End of Text (ETX) 控制字符\x03。Ctrl-D只是发送了 End of Transmission (EOT) 字符\x04。(你可以用stty -a命令看到当前终端的所有按键绑定奇怪的知识又增加了)操作系统如何处理 Ctrl-C当操作系统收到\x03这个字符时它需要对“当前进程”采取行动。但fork()产生了那么庞大的树状结构谁才是“当前进程”为了解决这个问题UNIX 引入了两个令人头疼的编号Session ID (会话大分组):子进程会继承父进程的 Session ID。一个 Session 关联一个控制终端 (controlling terminal)。当 Leader比如你的 SSH 登录窗口退出时全体进程都会收到 Hang Up (SIGHUP) 信号集体自杀。Process Group ID (进程组小分组):一个会话里可以有多个进程组但只能有一个前台进程组。当操作系统收到Ctrl-C带来的\x03字符时它会向前台进程组里的所有进程发送SIGINT(中断信号)不能误伤后台进程。无奈的妥协千疮百孔的 API为了管理这些组诞生了setsid,getpgid,tcsetpgrp等极不优雅的 API。再加上各种uid,effective uid,saved uid的权限管理……几乎任何人、任何软件都无法预知未来的走向历史的糟粕就这样成了 POSIX 标准的一部分。学会去未来回头看现在如果抛开历史包袱人机交互的方式根本不应该是这样的。现代 GUI 管理器Window Manager只需要“进程组”就够了。关掉窗口强行杀掉该组所有进程一个不留。Android 系统里干脆把每个 App 当作不同的 User 处理。Snap 甚至用AppArmorseccompnamespaces把程序关在绝对隔离的沙箱里运行。信号与 Ctrl-C 的底层代码演示当操作系统决定向前台进程发送信号时底层发生了什么注册处理程序 (signal / sigaction):告诉操作系统如果收到某个信号请去执行函数f。发送信号 (kill):强行在程序的正常执行流中插入一个向f的跳转。这就是为什么信号能打断死循环。代码演示#includestdio.h#includestdlib.h#includesignal.h#includeunistd.hvoidhandler(intsignum){switch(signum){caseSIGINT:printf(Received SIGINT!\n);break;caseSIGQUIT:printf(Received SIGQUIT!\n);exit(0);break;}}voidcleanup(){printf(atexit() cleanup\n);}intmain(){// 拦截 Ctrl-C (SIGINT) 和 Ctrl-\ (SIGQUIT)signal(SIGINT,handler);signal(SIGQUIT,handler);atexit(cleanup);while(1){charbuf[4096];intnreadread(STDIN_FILENO,buf,sizeof(buf));buf[nread-1]\0;printf([%d] Got: %s\n,getpid(),buf);if(nread0){perror(read);exit(1);}sleep(1);}}运行结果你会发现按下Ctrl-C时程序不会退出了而是打印出我们自定义的语句。只有按下Ctrl-\才会触发SIGQUIT退出。rootLAPTOP-GT06V0GS:/mnt/d/CSLab/osCourse/lec8/signal# ./signal^CReceived SIGINT![1927]Got: s[1927]Got: s ^\Received SIGQUIT!atexit()cleanupUNIX Shell 编程语言UNIX 的用户可都是 Hackers所以 UNIX Shell 被设计成了一种基于文本替换的极简编程语言。只有一种类型字符串。语言机制极其简洁预处理$(), 重定向, 顺序结构||, 管道|。底层核心揭秘重定向 () 是怎么实现的利用子进程继承文件描述符的特性配合dup2移花接木// 1. 父进程先以只读/只写的方式打开好输入/输出文件intfd_inopen(...,O_RDONLY|O_CLOEXEC);intfd_outopen(...,O_WRONLY|O_CLOEXEC);intpidfork();if(pid0){// 2. 子进程中利用 dup2 将标准输入(0) 和标准输出(1) 重定向到文件 fd 上dup2(fd_in,0);dup2(fd_out,1);// 3. 执行目标程序目标程序根本不知道自己被重定向了它只管傻傻地往 0 读往 1 写。execve(...);}else{// 4. 父进程关闭自己用不到的 fd等待子进程执行完毕close(fd_in);close(fd_out);waitpid(pid,status,0);}这几十行代码就是你敲下ls out.txt背后操作系统发生的所有秘密。Shell 的反思优点与缺点优点高效、简洁、精确。它是一种“自然编程语言”一行命令协同调度多个程序。最适合 quick dirty 的 hackers。AI 时代遇到不懂的命令直接man tcsetpgrp | ag -q 帮我生成新手友好的教程或者出了打错字的命令直接用开源神器thefuck自动纠正。缺点文本数据的“责任自负”。Shell 的设计被“1970s 的算力、算法和工程能力”严重束缚。空格 灾难文件名里带个空格Shell 脚本随时崩溃。优先级混乱ls a.txt | cat到底先执行重定向还是先执行管道bash和zsh的行为甚至都不一样致敬经典课程最后提到了xv6-riscv的 shell 源码。一个真正能用的微型操作系统 Shell没有 libc 的加持底层完全是依靠刚才讲的这些系统调用手搓出来的。这很牛逼但也告诉我们底层的系统编程没有魔法只有一层一层严谨的系统调用抽象。

相关新闻

Android逆向工程实战:从APK拆包到动态脱壳的完整指南

Android逆向工程实战:从APK拆包到动态脱壳的完整指南

2026/7/27 13:46:05

1. 项目概述:为什么我们需要一个“拆包器”?在Android应用开发与安全研究的圈子里,逆向工程是一个绕不开的话题。无论是安全研究员分析恶意软件的行为,还是开发者学习优秀应用的架构设计,亦或是测试人员验证应用的安全…

TI TPS25910负载开关EVM评估:浪涌抑制与电源时序控制实战

TI TPS25910负载开关EVM评估:浪涌抑制与电源时序控制实战

2026/7/27 13:46:05

1. 项目概述与核心价值在服务器、存储阵列或者电信设备这类对电源可靠性要求极高的系统中,你肯定遇到过这样的场景:一块新的硬盘或板卡在带电状态下插入背板,瞬间的电流冲击可能导致系统电压跌落,甚至触发保护、导致重启。又或者&…

高性能嵌入式处理器电源设计:基于TI PMIC的DRA7xxP/TDA2Pxx电源管理方案解析

高性能嵌入式处理器电源设计:基于TI PMIC的DRA7xxP/TDA2Pxx电源管理方案解析

2026/7/27 13:46:05

1. 项目概述:为高性能处理器构建稳健的电源骨架在嵌入式系统,尤其是汽车电子和工业控制这类对可靠性要求极高的领域,电源设计从来都不是简单的“供电”问题。它更像是在为整个系统的“心脏”搭建一套精密、可靠且智能的“血液循环系统”。处理…

Vue.js渐进式框架解析与最佳实践

Vue.js渐进式框架解析与最佳实践

2026/7/27 14:46:07

1. Vue框架的本质与设计哲学Vue.js(通常简称为Vue)是一个渐进式JavaScript框架,其核心定位是为现代Web开发提供高效、灵活的前端解决方案。与其它框架不同,Vue从诞生之初就特别强调"渐进式"这一概念——这意味着开发者可…

LM96080硬件监控芯片:从电压温度检测到中断系统设计全解析

LM96080硬件监控芯片:从电压温度检测到中断系统设计全解析

2026/7/27 14:46:07

1. 项目概述:硬件监控芯片的“守夜人”角色在服务器、工作站乃至高性能嵌入式系统的机箱深处,除了CPU、内存这些“明星”部件,还有一些默默无闻的“守夜人”在持续工作。它们不参与核心计算,却时刻监控着系统的生命体征&#xff1…

MERN全栈打造智能刷题系统:架构设计与实战优化

MERN全栈打造智能刷题系统:架构设计与实战优化

2026/7/27 14:46:07

1. 项目背景与核心价值四月份对于技术从业者来说是个特殊的季节——春招进入尾声,暑期实习申请即将开始,各大厂的笔试面试集中爆发。这个时间点,一套高效的刷题复盘系统就像程序员们的"考前急救包"。我开发这个"复活的刷题复盘…

ADC161S626评估板实战指南:从硬件解析到性能测试

ADC161S626评估板实战指南:从硬件解析到性能测试

2026/7/27 14:46:07

1. 项目概述与核心价值ADC161S626评估板,对于任何一个需要处理精密模拟信号的硬件工程师来说,都是一块能让你快速上手的“敲门砖”。我们常常在数据手册里看到ADC的SNR(信噪比)高达90多dB,THD(总谐波失真&a…

SQLServer连接问题全解析:pymssql常见错误与解决方案

SQLServer连接问题全解析:pymssql常见错误与解决方案

2026/7/27 14:46:07

1. 项目概述:SQLServer连接问题的典型场景 在数据驱动业务的时代,SQLServer作为企业级数据库的经典选择,与Python生态的对接已成为数据工程师的日常。但当你用pymssql这个轻量级连接库时,可能会遇到各种"神秘"的连接错误…

终极指南:gh_mirrors/fp/fpu如何成为你的Verilog IEEE 754浮点运算库首选?

终极指南:gh_mirrors/fp/fpu如何成为你的Verilog IEEE 754浮点运算库首选?

2026/7/27 14:36:07

终极指南:gh_mirrors/fp/fpu如何成为你的Verilog IEEE 754浮点运算库首选? 【免费下载链接】fpu synthesiseable ieee 754 floating point library in verilog 项目地址: https://gitcode.com/gh_mirrors/fp/fpu 在数字电路设计领域,…

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

2026/7/27 8:45:59

目标:电脑作为RTSP 服务端,循环推送 H264/H265 视频流; RDK X5 通过 rtsp2display 拉流预览,完全不需要在开发板编译 live555。 提供两套成熟方案: ✅ 方案 A:FFmpeg(最简单,优先推…

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

2026/7/27 8:42:17

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

2026/7/26 0:04:02

说实话,提到PDF拆分再压缩,我真是被折腾得够呛。 上个月公司年度合同归档,一份300多页的PDF总合同,需要按年份拆分成三个独立文件,再分别压缩到10MB以内方便邮件发送各部门确认。我心想这还不简单?先找个海…

多模态 AI 前端工程——图像上传、压缩与流式返回的协同设计

多模态 AI 前端工程——图像上传、压缩与流式返回的协同设计

2026/7/27 0:05:04

多模态 AI 前端工程——图像上传、压缩与流式返回的协同设计 一、多模态对话的「首字节延迟」:上传与流式的协同鸿沟 多模态 AI 应用的前端体验,往往卡在"首字节延迟"上。用户上传一张图片,提一个问题,然后盯着空白对…

【微科普】网红水晶香薰真相拆解:透明固体香薰并非香精结晶,一文理清各类无火香薰释香机理

【微科普】网红水晶香薰真相拆解:透明固体香薰并非香精结晶,一文理清各类无火香薰释香机理

2026/7/27 0:05:04

文章目录第一章 大众普遍存在的认知误区:水晶香薰是芳香烃结晶产物1.1 聚丙烯酸钠凝胶水晶珠体系(市面占比90%家用水晶香薰)1.2 无机盐硬质结晶载体:泻盐与钾明矾香薰原石1.3 植物多糖与PVA整块果冻型水晶香膏1.4 唯一特例&#x…

优启通3.7修改版:深度优化的PE系统维护工具

优启通3.7修改版:深度优化的PE系统维护工具

2026/7/27 0:05:04

1. 项目概述今天要跟大家分享的是一个经过深度优化的PE工具——优启通3.7(2025修改版)。这个版本是在原版基础上进行了大量功能增强和兼容性改进的12月最新版本,特别适合系统维护人员和电脑爱好者使用。作为一个长期从事IT运维的老兵&#xf…