《深入理解计算机系统(原书第3版)》第10章:系统级 I/O
!note 阅读范围与适用边界
本笔记依据本地附件中的第 10 章整理,以 Linux/Unix 与 C 语言为背景,用自己的话解释原书。这里的
open、read、dup2、文件描述符和stat都是 Unix 风格接口;Windows 的 API 名称与细节不同,但"用句柄代表已打开资源、读写返回实际结果"的思路仍可借鉴。
!tip 一句话抓住本章程序不会直接和磁盘、键盘或网络"说话":它先向内核拿到一张小小的取件号------文件描述符,再用同一套打开、读、写、关闭操作访问各种设备;真正可靠的程序还要认真处理 EOF、短计数、共享位置和缓冲。
!warning 先记住
read或write没有完成请求的全部字节数,不一定是错误。终端、管道和网络套接字上尤其常见;程序要以返回的实际字节数为准。
1 本章学习导航
| 阶段 | 建议用时(估计) | 学什么 | 通过标准 |
|---|---|---|---|
| 0. 建立直觉 | 20 分钟 | I/O、文件、文件描述符、标准输入输出 | 能说出 fd 0/1/2 的用途 |
| 1. 打开与定位 | 35 分钟 | 路径、open、权限位、close、文件位置 |
能读懂常见 open 参数 |
| 2. 正确读写 | 50 分钟 | read、write、EOF、短计数 |
不会把短计数直接当错误 |
| 3. RIO | 60 分钟 | 无缓冲/带缓冲 RIO、重复读写、缓冲区 | 能说明网络 I/O 为什么常用 RIO |
| 4. 文件信息 | 35 分钟 | stat、fstat、目录流 |
能区分内容、元数据和目录项 |
| 5. 共享与重定向 | 55 分钟 | 三张表、fork、dup2 |
能解释父子进程为何共享文件位置 |
| 6. 选择工具 | 35 分钟 | Unix I/O、RIO、标准 I/O 的边界 | 能为磁盘、二进制、网络选接口 |
!tip 推荐顺序
第一次先抓住"描述符 →
read/write→ 短计数 → RIO → 共享/重定向"这条主线。stat与目录遍历是实用补充;把标准 I/O 放到最后学,更容易理解它在网络上为什么麻烦。
2 本章学习目标
- 解释 I/O 是"主存与外部设备之间复制数据"。
- 说明 Linux 为什么能把普通文件、目录、终端和网络套接字纳入统一模型。
- 说清文件描述符不是文件内容,而是进程访问"已打开文件"的小整数编号。
- 读懂
open的访问方式、常见标志位、创建权限与umask的关系。 - 根据
read返回值区分:读到数据、EOF、出错。 - 解释短计数为什么会发生,以及网络程序为何不能只调用一次
read或write。 - 区分无缓冲 RIO 与带缓冲 RIO,并知道哪些函数可在同一描述符上交错使用。
- 用
stat/fstat获取文件类型、大小和权限等元数据。 - 用"描述符表 → 打开文件表 → v-node 表"解释
open、fork、dup2。 - 说明磁盘/终端通常优先标准 I/O,而网络套接字优先 RIO 的原因。
3 整章知识地图
#mermaid-svg-WbCIPkfg86wGECuU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WbCIPkfg86wGECuU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WbCIPkfg86wGECuU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WbCIPkfg86wGECuU .error-icon{fill:#552222;}#mermaid-svg-WbCIPkfg86wGECuU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WbCIPkfg86wGECuU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WbCIPkfg86wGECuU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WbCIPkfg86wGECuU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WbCIPkfg86wGECuU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WbCIPkfg86wGECuU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WbCIPkfg86wGECuU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WbCIPkfg86wGECuU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WbCIPkfg86wGECuU .marker.cross{stroke:#333333;}#mermaid-svg-WbCIPkfg86wGECuU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WbCIPkfg86wGECuU p{margin:0;}#mermaid-svg-WbCIPkfg86wGECuU .edge{stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .section--1 rect,#mermaid-svg-WbCIPkfg86wGECuU .section--1 path,#mermaid-svg-WbCIPkfg86wGECuU .section--1 circle,#mermaid-svg-WbCIPkfg86wGECuU .section--1 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section--1 text{fill:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth--1{stroke-width:17;}#mermaid-svg-WbCIPkfg86wGECuU .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-0 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-0 path,#mermaid-svg-WbCIPkfg86wGECuU .section-0 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-0 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-WbCIPkfg86wGECuU .section-0 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-0{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-0{stroke-width:14;}#mermaid-svg-WbCIPkfg86wGECuU .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-1 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-1 path,#mermaid-svg-WbCIPkfg86wGECuU .section-1 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-1 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-1 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-1{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-1{stroke-width:11;}#mermaid-svg-WbCIPkfg86wGECuU .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-2 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-2 path,#mermaid-svg-WbCIPkfg86wGECuU .section-2 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-2 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-2 text{fill:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-2{stroke-width:8;}#mermaid-svg-WbCIPkfg86wGECuU .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-3 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-3 path,#mermaid-svg-WbCIPkfg86wGECuU .section-3 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-3 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-3 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-3{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-3{stroke-width:5;}#mermaid-svg-WbCIPkfg86wGECuU .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-4 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-4 path,#mermaid-svg-WbCIPkfg86wGECuU .section-4 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-4 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-4 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-4{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-4{stroke-width:2;}#mermaid-svg-WbCIPkfg86wGECuU .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-5 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-5 path,#mermaid-svg-WbCIPkfg86wGECuU .section-5 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-5 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-5 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-5{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-5{stroke-width:-1;}#mermaid-svg-WbCIPkfg86wGECuU .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-6 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-6 path,#mermaid-svg-WbCIPkfg86wGECuU .section-6 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-6 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-6 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-6{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-6{stroke-width:-4;}#mermaid-svg-WbCIPkfg86wGECuU .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-7 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-7 path,#mermaid-svg-WbCIPkfg86wGECuU .section-7 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-7 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-7 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-7{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-7{stroke-width:-7;}#mermaid-svg-WbCIPkfg86wGECuU .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-8 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-8 path,#mermaid-svg-WbCIPkfg86wGECuU .section-8 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-8 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-8 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-8{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-8{stroke-width:-10;}#mermaid-svg-WbCIPkfg86wGECuU .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-9 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-9 path,#mermaid-svg-WbCIPkfg86wGECuU .section-9 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-9 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-9 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-9{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-9{stroke-width:-13;}#mermaid-svg-WbCIPkfg86wGECuU .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-10 rect,#mermaid-svg-WbCIPkfg86wGECuU .section-10 path,#mermaid-svg-WbCIPkfg86wGECuU .section-10 circle,#mermaid-svg-WbCIPkfg86wGECuU .section-10 polygon,#mermaid-svg-WbCIPkfg86wGECuU .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-10 text{fill:black;}#mermaid-svg-WbCIPkfg86wGECuU .node-icon-10{font-size:40px;color:black;}#mermaid-svg-WbCIPkfg86wGECuU .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .edge-depth-10{stroke-width:-16;}#mermaid-svg-WbCIPkfg86wGECuU .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-WbCIPkfg86wGECuU .disabled,#mermaid-svg-WbCIPkfg86wGECuU .disabled circle,#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:lightgray;}#mermaid-svg-WbCIPkfg86wGECuU .disabled text{fill:#efefef;}#mermaid-svg-WbCIPkfg86wGECuU .section-root rect,#mermaid-svg-WbCIPkfg86wGECuU .section-root path,#mermaid-svg-WbCIPkfg86wGECuU .section-root circle,#mermaid-svg-WbCIPkfg86wGECuU .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-WbCIPkfg86wGECuU .section-root text{fill:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .section-root span{color:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .section-2 span{color:#ffffff;}#mermaid-svg-WbCIPkfg86wGECuU .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-WbCIPkfg86wGECuU .edge{fill:none;}#mermaid-svg-WbCIPkfg86wGECuU .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-WbCIPkfg86wGECuU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第10章:系统级 I/O
统一模型
文件
文件描述符
打开 读写 关闭
文件位置
文件与路径
普通文件
目录
套接字
权限与 umask
可靠读写
EOF
短计数
RIO 无缓冲
RIO 带缓冲
文件信息
stat 和 fstat
目录流
共享与重定向
描述符表
打开文件表
v-node 表
fork 与 dup2
工具选择
Unix I/O
标准 I/O
网络使用 RIO
3.1 前置知识与后续关系
#mermaid-svg-kg6rBrD0YzMAXy2M{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kg6rBrD0YzMAXy2M .error-icon{fill:#552222;}#mermaid-svg-kg6rBrD0YzMAXy2M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kg6rBrD0YzMAXy2M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kg6rBrD0YzMAXy2M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kg6rBrD0YzMAXy2M .marker.cross{stroke:#333333;}#mermaid-svg-kg6rBrD0YzMAXy2M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kg6rBrD0YzMAXy2M p{margin:0;}#mermaid-svg-kg6rBrD0YzMAXy2M .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster-label text{fill:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster-label span{color:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster-label span p{background-color:transparent;}#mermaid-svg-kg6rBrD0YzMAXy2M .label text,#mermaid-svg-kg6rBrD0YzMAXy2M span{fill:#333;color:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M .node rect,#mermaid-svg-kg6rBrD0YzMAXy2M .node circle,#mermaid-svg-kg6rBrD0YzMAXy2M .node ellipse,#mermaid-svg-kg6rBrD0YzMAXy2M .node polygon,#mermaid-svg-kg6rBrD0YzMAXy2M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kg6rBrD0YzMAXy2M .rough-node .label text,#mermaid-svg-kg6rBrD0YzMAXy2M .node .label text,#mermaid-svg-kg6rBrD0YzMAXy2M .image-shape .label,#mermaid-svg-kg6rBrD0YzMAXy2M .icon-shape .label{text-anchor:middle;}#mermaid-svg-kg6rBrD0YzMAXy2M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kg6rBrD0YzMAXy2M .rough-node .label,#mermaid-svg-kg6rBrD0YzMAXy2M .node .label,#mermaid-svg-kg6rBrD0YzMAXy2M .image-shape .label,#mermaid-svg-kg6rBrD0YzMAXy2M .icon-shape .label{text-align:center;}#mermaid-svg-kg6rBrD0YzMAXy2M .node.clickable{cursor:pointer;}#mermaid-svg-kg6rBrD0YzMAXy2M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kg6rBrD0YzMAXy2M .arrowheadPath{fill:#333333;}#mermaid-svg-kg6rBrD0YzMAXy2M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kg6rBrD0YzMAXy2M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kg6rBrD0YzMAXy2M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kg6rBrD0YzMAXy2M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kg6rBrD0YzMAXy2M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kg6rBrD0YzMAXy2M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster text{fill:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M .cluster span{color:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kg6rBrD0YzMAXy2M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kg6rBrD0YzMAXy2M rect.text{fill:none;stroke-width:0;}#mermaid-svg-kg6rBrD0YzMAXy2M .icon-shape,#mermaid-svg-kg6rBrD0YzMAXy2M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kg6rBrD0YzMAXy2M .icon-shape p,#mermaid-svg-kg6rBrD0YzMAXy2M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kg6rBrD0YzMAXy2M .icon-shape .label rect,#mermaid-svg-kg6rBrD0YzMAXy2M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kg6rBrD0YzMAXy2M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kg6rBrD0YzMAXy2M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kg6rBrD0YzMAXy2M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} \[第1章_计算机系统漫游]
设备、操作系统与网络
第10章
统一的系统级 I/O
\[第2章_信息的表示和处理\]
字节、字符与二进制数据
进程、异常与虚拟内存
第11章:网络编程
第12章:并发编程
原书把本章作为网络编程与并发编程的基础:网络套接字在 Linux 看来也属于文件,所以同样要面对读写、短计数和共享资源。
3.2 零基础词汇表
| 词 | 先用一句话记住 | 不要误解成 |
|---|---|---|
| I/O | 输入/输出;在主存与外部设备之间复制数据 | 只有键盘输入和屏幕输出 |
| 文件 | Linux 对可读写对象使用的统一抽象 | 必然是磁盘上的文本 |
| 文件描述符(fd) | 进程手里的小整数"取件号" | 文件内容、文件名或全局唯一 ID |
| 文件位置 | 本次读/写从文件的第几个字节开始 | 路径中的位置 |
| EOF | 已到达可读数据末尾的状态 | 文件尾部的一个特殊字符 |
| 短计数 | 实际传送量小于请求量 | 一定是系统调用失败 |
| 元数据 | 类型、大小、权限等"描述文件的信息" | 文件正文 |
| 目录流 | 按顺序读取目录条目的抽象 | 网络的数据流 |
| 标准 I/O | C 库用 FILE * 表示的带缓冲流接口 |
Unix I/O 的同义词 |
| RIO | 原书给出的健壮 I/O 包 | 内核又一套文件系统 |
4 核心知识点逐个讲
4.1 把设备当成文件:Unix I/O 与文件描述符(⭐⭐⭐⭐⭐)
4.1.1 ① 一句话
Unix I/O 把磁盘文件、终端和网络套接字等对象统一看成"文件";程序得到 fd 后,就能用 read、write、close 等接口操作它。
4.1.2 ② 大白话解释(含三组生活类比)
fd 不是文件本身,而是内核发给程序的一张访问凭证。程序记住编号,内核替它保存访问方式、当前位置等详细状态。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 寄存柜取件号 | fd → 取件号;内核记录 → 后台记录 | 程序只需拿编号 | 内核共享关系比寄存柜复杂 |
| 医院挂号单 | open → 挂号;fd → 就诊号 |
"找到对象"和"建立一次访问"是两步 | fd 不是排队顺序 |
| 遥控器按钮 | write(1, ...) → 按输出按钮 |
调用者不必了解设备内部 | fd 的指向可被 dup2 改写 |
新进程通常已有三项打开文件:
| 名称 | 宏 | 常见 fd | 默认用途 |
|---|---|---|---|
| 标准输入 | STDIN_FILENO |
0 | 从键盘或管道读取 |
| 标准输出 | STDOUT_FILENO |
1 | 写普通结果 |
| 标准错误 | STDERR_FILENO |
2 | 写诊断信息 |
4.1.3 ③ 为什么会有它
- 一套低级接口能服务多种设备。
- 内核集中保存访问方式、当前位置和引用计数。
- 应用程序只携带小整数,接口简单一致。
4.1.4 ④ 工作流程
文件或设备 已打开文件状态 Linux 内核 应用程序 文件或设备 已打开文件状态 Linux 内核 应用程序 #mermaid-svg-ymTIhEbmNuAENS5K{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ymTIhEbmNuAENS5K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ymTIhEbmNuAENS5K .error-icon{fill:#552222;}#mermaid-svg-ymTIhEbmNuAENS5K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ymTIhEbmNuAENS5K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ymTIhEbmNuAENS5K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ymTIhEbmNuAENS5K .marker.cross{stroke:#333333;}#mermaid-svg-ymTIhEbmNuAENS5K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ymTIhEbmNuAENS5K p{margin:0;}#mermaid-svg-ymTIhEbmNuAENS5K .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ymTIhEbmNuAENS5K text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ymTIhEbmNuAENS5K .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ymTIhEbmNuAENS5K .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ymTIhEbmNuAENS5K .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ymTIhEbmNuAENS5K .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ymTIhEbmNuAENS5K #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ymTIhEbmNuAENS5K .sequenceNumber{fill:white;}#mermaid-svg-ymTIhEbmNuAENS5K #sequencenumber{fill:#333;}#mermaid-svg-ymTIhEbmNuAENS5K #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ymTIhEbmNuAENS5K .messageText{fill:#333;stroke:none;}#mermaid-svg-ymTIhEbmNuAENS5K .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ymTIhEbmNuAENS5K .labelText,#mermaid-svg-ymTIhEbmNuAENS5K .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ymTIhEbmNuAENS5K .loopText,#mermaid-svg-ymTIhEbmNuAENS5K .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ymTIhEbmNuAENS5K .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ymTIhEbmNuAENS5K .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ymTIhEbmNuAENS5K .noteText,#mermaid-svg-ymTIhEbmNuAENS5K .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ymTIhEbmNuAENS5K .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ymTIhEbmNuAENS5K .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ymTIhEbmNuAENS5K .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ymTIhEbmNuAENS5K .actorPopupMenu{position:absolute;}#mermaid-svg-ymTIhEbmNuAENS5K .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ymTIhEbmNuAENS5K .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ymTIhEbmNuAENS5K .actor-man circle,#mermaid-svg-ymTIhEbmNuAENS5K line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ymTIhEbmNuAENS5K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open(路径, 方式)建立打开状态返回 fdread/write(fd, 缓冲区, n)查询当前文件位置执行数据复制数据或完成状态实际字节数 / EOF / 错误close(fd)
4.1.5 ⑤ 图示说明
先看 open 产生 fd;之后所有读写都带 fd;最后 close 结束此次访问。文件名用于打开 ,描述符用于已经打开后的操作。
4.1.6 ⑥ 容易混淆
| 容易混淆 | 正确理解 |
|---|---|
| fd 就是文件 | fd 是进程内编号,指向内核保存的打开状态 |
| 文件只能在磁盘上 | 终端、网络套接字等也能作为文件处理 |
| fd 0/1/2 永远连着固定设备 | 它们是默认编号,可被重定向 |
4.1.7 ⑦ 面试 / 表达题
为什么 Linux 用 fd,而不是每次 read 都传路径?
先答"fd 表示一次已打开的访问";再答内核已保存访问状态与位置;最后举例:read(fd, ...) 无需每次按路径查找对象。
4.1.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|文件描述符
文件描述符是进程访问已打开文件或设备的本地编号;它不是文件本身,却是一切 Unix I/O 操作的入口。
4.2 文件、路径、open 与权限(⭐⭐⭐⭐)
4.2.1 ① 一句话
文件有普通文件、目录和套接字等类型;程序用路径找到它,用 open 指定读写方式,必要时创建它,最后用 close 结束访问。
4.2.2 ② 大白话解释(含三组生活类比)
路径回答"去哪里找",open 回答"我要怎样用",权限回答"我有没有资格这样用"。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 城市地址 | 绝对路径从 / 开始;相对路径从当前目录开始 |
当前工作目录影响相对路径 | 还会受权限与链接影响 |
| 图书馆借书 | open → 办手续;close → 结束借阅 |
找到书与建立访问是两步 | 文件共享规则更复杂 |
| 酒店门卡 | 权限位 → 读、写、执行资格 | 权限不只是"能否看见" | Unix 还区分用户、组、其他人 |
| 类型 | 是什么 | 初学者先记住 |
|---|---|---|
| 普通文件 | 任意字节序列 | 对内核而言,文本/二进制同属普通文件 |
| 目录 | 文件名到文件的映射 | 目录也是一种文件 |
| 套接字 | 和另一个进程跨网络通信的对象 | 网络也落进同一读写模型 |
常见 open 标志:
| 标志 | 大白话 | 常见用途 |
|---|---|---|
O_RDONLY |
只读 | 打开输入文件 |
O_WRONLY |
只写 | 打开输出文件 |
O_RDWR |
可读可写 | 两种操作都需要 |
O_CREAT |
不存在就创建 | 生成输出文件 |
O_TRUNC |
已存在就截成空文件 | 重写完整结果 |
O_APPEND |
每次写前移到末尾 | 追加日志 |
4.2.3 ③ 为什么会有它
目录层次组织大量文件;open 用标志位表达访问意图;创建时最终权限由 mode & ~umask 得到,避免默认开放过多权限。
4.2.4 ④ 工作流程
#mermaid-svg-O3QJmpHTH2a1AGFs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O3QJmpHTH2a1AGFs .error-icon{fill:#552222;}#mermaid-svg-O3QJmpHTH2a1AGFs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O3QJmpHTH2a1AGFs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O3QJmpHTH2a1AGFs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O3QJmpHTH2a1AGFs .marker.cross{stroke:#333333;}#mermaid-svg-O3QJmpHTH2a1AGFs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O3QJmpHTH2a1AGFs p{margin:0;}#mermaid-svg-O3QJmpHTH2a1AGFs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster-label text{fill:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster-label span{color:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster-label span p{background-color:transparent;}#mermaid-svg-O3QJmpHTH2a1AGFs .label text,#mermaid-svg-O3QJmpHTH2a1AGFs span{fill:#333;color:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs .node rect,#mermaid-svg-O3QJmpHTH2a1AGFs .node circle,#mermaid-svg-O3QJmpHTH2a1AGFs .node ellipse,#mermaid-svg-O3QJmpHTH2a1AGFs .node polygon,#mermaid-svg-O3QJmpHTH2a1AGFs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-O3QJmpHTH2a1AGFs .rough-node .label text,#mermaid-svg-O3QJmpHTH2a1AGFs .node .label text,#mermaid-svg-O3QJmpHTH2a1AGFs .image-shape .label,#mermaid-svg-O3QJmpHTH2a1AGFs .icon-shape .label{text-anchor:middle;}#mermaid-svg-O3QJmpHTH2a1AGFs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-O3QJmpHTH2a1AGFs .rough-node .label,#mermaid-svg-O3QJmpHTH2a1AGFs .node .label,#mermaid-svg-O3QJmpHTH2a1AGFs .image-shape .label,#mermaid-svg-O3QJmpHTH2a1AGFs .icon-shape .label{text-align:center;}#mermaid-svg-O3QJmpHTH2a1AGFs .node.clickable{cursor:pointer;}#mermaid-svg-O3QJmpHTH2a1AGFs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-O3QJmpHTH2a1AGFs .arrowheadPath{fill:#333333;}#mermaid-svg-O3QJmpHTH2a1AGFs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-O3QJmpHTH2a1AGFs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-O3QJmpHTH2a1AGFs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O3QJmpHTH2a1AGFs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-O3QJmpHTH2a1AGFs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O3QJmpHTH2a1AGFs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster text{fill:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs .cluster span{color:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-O3QJmpHTH2a1AGFs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-O3QJmpHTH2a1AGFs rect.text{fill:none;stroke-width:0;}#mermaid-svg-O3QJmpHTH2a1AGFs .icon-shape,#mermaid-svg-O3QJmpHTH2a1AGFs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O3QJmpHTH2a1AGFs .icon-shape p,#mermaid-svg-O3QJmpHTH2a1AGFs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-O3QJmpHTH2a1AGFs .icon-shape .label rect,#mermaid-svg-O3QJmpHTH2a1AGFs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O3QJmpHTH2a1AGFs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-O3QJmpHTH2a1AGFs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-O3QJmpHTH2a1AGFs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
路径名
绝对路径?
从根目录 / 查找
从当前工作目录查找
找到目标或创建位置
open 的 flags
返回最小可用 fd
读写后 close(fd)
4.2.5 ⑤ 图示说明
先分清"路径解析"和"访问方式"两步;O_CREAT 才使创建权限 mode 有意义。close 不只是礼貌动作,它会释放此次打开占用的内核资源。
4.2.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
| 相对路径总相对源代码位置 | 它相对当前工作目录 |
O_CREAT 指定的权限就是最终权限 |
最终还会被 umask 过滤 |
| 文本文件与二进制文件在内核中完全不同 | 对内核都是普通文件,差别主要是应用怎样解释字节 |
4.2.7 ⑦ 面试 / 表达题
O_APPEND 的核心意图是什么?
每次写之前把文件位置放到文件末尾;它表达"追加"而不是"在文件名后面加文字"。
4.2.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|打开文件
路径负责定位,
open负责建立一次访问,close负责结束访问;创建文件时的最终权限还要经过umask。
4.3 read、write、EOF 与短计数(⭐⭐⭐⭐⭐)
4.3.1 ① 一句话
read 与 write 请求传送 n 个字节,但返回值表示实际 传送量:read 返回 0 是 EOF,返回 -1 是错误,少于请求数的短计数常常正常。
4.3.2 ② 大白话解释(含三组生活类比)
把参数 n 看作愿望,把返回值看作回执:程序必须相信回执。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 接水桶 | 想取 50 升,水箱只剩 20 升 | 读取量可小于请求量 | 网络数据之后可能继续到达 |
| 快递分批发货 | 想寄 100 件,本次只交接 60 件 | 写操作可能分多次完成 | 字节并不是真实包裹 |
| 候车队列 | 想等 10 人,这趟只来 3 人 | 数据供给节奏不由调用者决定 | I/O 没有人类排队语义 |
| 调用 | 返回值 | 应怎样理解 |
|---|---|---|
read(fd, buf, n) |
> 0 | 实际读到这么多字节 |
read(fd, buf, n) |
0 | EOF:当前位置已无可读数据 |
read(fd, buf, n) |
-1 | 出错 |
write(fd, buf, n) |
> 0 | 实际写出这么多字节,未必等于 n |
write(fd, buf, n) |
-1 | 出错 |
4.3.3 ③ 为什么会有它
EOF 是状态,不是额外字符;终端按行、网络时延、内部缓冲和管道都会造成短计数。若网络程序忽略短计数,就可能把半条业务消息误当完整消息。
4.3.4 ④ 工作流程
#mermaid-svg-IdGmL5ByC1w7T5dY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IdGmL5ByC1w7T5dY .error-icon{fill:#552222;}#mermaid-svg-IdGmL5ByC1w7T5dY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IdGmL5ByC1w7T5dY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IdGmL5ByC1w7T5dY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IdGmL5ByC1w7T5dY .marker.cross{stroke:#333333;}#mermaid-svg-IdGmL5ByC1w7T5dY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IdGmL5ByC1w7T5dY p{margin:0;}#mermaid-svg-IdGmL5ByC1w7T5dY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster-label text{fill:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster-label span{color:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster-label span p{background-color:transparent;}#mermaid-svg-IdGmL5ByC1w7T5dY .label text,#mermaid-svg-IdGmL5ByC1w7T5dY span{fill:#333;color:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY .node rect,#mermaid-svg-IdGmL5ByC1w7T5dY .node circle,#mermaid-svg-IdGmL5ByC1w7T5dY .node ellipse,#mermaid-svg-IdGmL5ByC1w7T5dY .node polygon,#mermaid-svg-IdGmL5ByC1w7T5dY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IdGmL5ByC1w7T5dY .rough-node .label text,#mermaid-svg-IdGmL5ByC1w7T5dY .node .label text,#mermaid-svg-IdGmL5ByC1w7T5dY .image-shape .label,#mermaid-svg-IdGmL5ByC1w7T5dY .icon-shape .label{text-anchor:middle;}#mermaid-svg-IdGmL5ByC1w7T5dY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IdGmL5ByC1w7T5dY .rough-node .label,#mermaid-svg-IdGmL5ByC1w7T5dY .node .label,#mermaid-svg-IdGmL5ByC1w7T5dY .image-shape .label,#mermaid-svg-IdGmL5ByC1w7T5dY .icon-shape .label{text-align:center;}#mermaid-svg-IdGmL5ByC1w7T5dY .node.clickable{cursor:pointer;}#mermaid-svg-IdGmL5ByC1w7T5dY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IdGmL5ByC1w7T5dY .arrowheadPath{fill:#333333;}#mermaid-svg-IdGmL5ByC1w7T5dY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IdGmL5ByC1w7T5dY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IdGmL5ByC1w7T5dY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IdGmL5ByC1w7T5dY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IdGmL5ByC1w7T5dY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IdGmL5ByC1w7T5dY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster text{fill:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY .cluster span{color:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IdGmL5ByC1w7T5dY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IdGmL5ByC1w7T5dY rect.text{fill:none;stroke-width:0;}#mermaid-svg-IdGmL5ByC1w7T5dY .icon-shape,#mermaid-svg-IdGmL5ByC1w7T5dY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IdGmL5ByC1w7T5dY .icon-shape p,#mermaid-svg-IdGmL5ByC1w7T5dY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IdGmL5ByC1w7T5dY .icon-shape .label rect,#mermaid-svg-IdGmL5ByC1w7T5dY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IdGmL5ByC1w7T5dY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IdGmL5ByC1w7T5dY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IdGmL5ByC1w7T5dY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} -1
0
0 < k < n
k = n
需要
不需要
调用 read(fd, buf, n)
返回值
错误:处理或上报
EOF:停止读取
短计数:已得到 k 字节
本次读满
业务还需要剩余字节?
处理已得到的数据
4.3.5 ⑤ 图示说明
只要返回正数,调用本身成功;但 0 < k < n 是否要继续,取决于业务是否必须拿够 n 字节。最容易错的是只区分"成功/失败",漏掉短计数。
4.3.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
| 短计数一定是错误 | EOF、终端、网络、管道都可能正常造成短计数 |
EOF 是 0x00 或换行符 |
EOF 是 read 到达结尾时报告的状态 |
read 返回 0 说明文件大小为 0 |
只说明当前位置已经到结尾 |
write 返回正数就表示全部写完 |
它只承诺本次实际写出的量 |
4.3.7 ⑦ 面试 / 表达题
为什么网络程序不能只调用一次 read?
网络套接字可能短计数;返回值只代表当前实际收到了多少。若协议需要固定长度,程序应累计已读量、继续读取剩余量,直到收满、EOF 或错误。
4.3.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|短计数
I/O 的返回值是事实回执:请求 n 字节不等于得到 n 字节;EOF、短计数和错误必须分开处理。
4.4 RIO:把"反复读写"固定成可靠习惯(⭐⭐⭐⭐⭐)
4.4.1 ① 一句话
RIO(Robust I/O)是原书给出的包装:它通过重复调用 read/write、处理短计数与被信号中断后的重试,让网络 I/O 更可靠。
4.4.2 ② 大白话解释(含三组生活类比)
RIO 不会让网络突然一次传完数据;它把"还没传完就继续"的正确动作封装起来,减少遗漏。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 快递追踪单 | rio_writen 不断处理剩余包裹 |
请求量和已完成量要分开记 | 网络字节不是独立包裹 |
| 量杯补水 | rio_readn 累计接收,直到目标量或 EOF |
用剩余量驱动循环 | EOF 不表示永远没有新数据 |
| 自助餐托盘 | 带缓冲 RIO 先取一批,再少量多次交给用户 | 缓冲减少频繁系统调用 | 缓冲有限且带有自身状态 |
| 类别 | 代表函数 | 适用对象 | 关键性质 |
|---|---|---|---|
| 无缓冲 | rio_readn、rio_writen |
原始二进制数据 | 直接在用户内存与 fd 间传送 |
| 带缓冲输入 | rio_readinitb、rio_readlineb、rio_readnb |
文本行、文本与二进制混合输入 | rio_t 保存内部读缓冲状态 |
4.4.3 ③ 为什么会有它
短计数与中断处理容易漏写;逐字节直接 read 效率低。带缓冲 RIO 可以一次向内核取一批字节,再按行或按请求长度交给应用。
4.4.4 ④ 工作流程
#mermaid-svg-qjYuVqOWgaB6VYtJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qjYuVqOWgaB6VYtJ .error-icon{fill:#552222;}#mermaid-svg-qjYuVqOWgaB6VYtJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qjYuVqOWgaB6VYtJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .marker.cross{stroke:#333333;}#mermaid-svg-qjYuVqOWgaB6VYtJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qjYuVqOWgaB6VYtJ p{margin:0;}#mermaid-svg-qjYuVqOWgaB6VYtJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster-label text{fill:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster-label span{color:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster-label span p{background-color:transparent;}#mermaid-svg-qjYuVqOWgaB6VYtJ .label text,#mermaid-svg-qjYuVqOWgaB6VYtJ span{fill:#333;color:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .node rect,#mermaid-svg-qjYuVqOWgaB6VYtJ .node circle,#mermaid-svg-qjYuVqOWgaB6VYtJ .node ellipse,#mermaid-svg-qjYuVqOWgaB6VYtJ .node polygon,#mermaid-svg-qjYuVqOWgaB6VYtJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .rough-node .label text,#mermaid-svg-qjYuVqOWgaB6VYtJ .node .label text,#mermaid-svg-qjYuVqOWgaB6VYtJ .image-shape .label,#mermaid-svg-qjYuVqOWgaB6VYtJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-qjYuVqOWgaB6VYtJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .rough-node .label,#mermaid-svg-qjYuVqOWgaB6VYtJ .node .label,#mermaid-svg-qjYuVqOWgaB6VYtJ .image-shape .label,#mermaid-svg-qjYuVqOWgaB6VYtJ .icon-shape .label{text-align:center;}#mermaid-svg-qjYuVqOWgaB6VYtJ .node.clickable{cursor:pointer;}#mermaid-svg-qjYuVqOWgaB6VYtJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .arrowheadPath{fill:#333333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qjYuVqOWgaB6VYtJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qjYuVqOWgaB6VYtJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qjYuVqOWgaB6VYtJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster text{fill:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ .cluster span{color:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qjYuVqOWgaB6VYtJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qjYuVqOWgaB6VYtJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-qjYuVqOWgaB6VYtJ .icon-shape,#mermaid-svg-qjYuVqOWgaB6VYtJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qjYuVqOWgaB6VYtJ .icon-shape p,#mermaid-svg-qjYuVqOWgaB6VYtJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qjYuVqOWgaB6VYtJ .icon-shape .label rect,#mermaid-svg-qjYuVqOWgaB6VYtJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qjYuVqOWgaB6VYtJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qjYuVqOWgaB6VYtJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qjYuVqOWgaB6VYtJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始字节 / 写满
读行或带缓冲字节
有
没有
需要读取或写出 n 字节
选择 RIO 类型
rio_readn / rio_writen
rio_readinitb
rio_readlineb / rio_readnb
累计完成量;必要时重试
内部缓冲有未读字节?
从内部缓冲复制
调用 read 填充内部缓冲
4.4.5 ⑤ 图示说明
rio_readn遇到 EOF 时可能返回不足量;rio_writen的目标是把请求数据全部写出,否则报告错误。rio_readlineb读取一行并补字符串结尾;最多交付maxlen - 1个数据字节。- 同一 fd 上,
rio_readlineb与rio_readnb可以交错;带缓冲读取不要与无缓冲rio_readn混用。
4.4.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
| RIO 会消除所有网络错误 | 它处理不足量/中断,不替代协议、超时和错误处理 |
| 带缓冲等于一次读完整个文件 | 它只维护有限内部读缓冲,按需填充 |
rio_readlineb 适合任意二进制 |
它按文本行设计,二进制可能含有 0x0a |
| 任意 RIO 调用可随意交错 | 仅两个带缓冲读取接口明确可交错 |
4.4.7 ⑦ 面试 / 表达题
RIO 的"健壮"体现在哪里?
它把短计数的循环处理与被信号中断后的重试收进函数;带缓冲版本还避免了逐字节频繁陷入内核。
4.4.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|RIO
RIO 是对 Unix
read/write的可靠包装:无缓冲版本累计完成量,带缓冲版本把系统调用得到的字节高效地分次交付。
4.5 文件元数据与目录内容(⭐⭐⭐⭐)
4.5.1 ① 一句话
想知道文件"多大、是什么类型、权限怎样"时,用 stat/fstat 读取元数据;想列出目录成员时,用 opendir、readdir、closedir。
4.5.2 ② 大白话解释(含三组生活类比)
正文像一本书的内容,元数据像目录卡,目录像楼层导览牌;找类型和大小时,不需要把正文全读一遍。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 图书馆目录卡 | stat 结果 → 类型、大小、时间、权限 |
元数据不等于正文 | 字段随系统而异 |
| 小区导览牌 | 目录 → 名称到文件的映射 | 目录是可逐项读取的对象 | 目录项不等于文件详情 |
| 房产档案 | st_mode → 类型与权限;st_size → 大小 |
一个结构能保存多种属性 | 与真实产权规则无关 |
| 接口 | 输入 | 主要结果 |
|---|---|---|
stat(path, &st) |
路径 | 指定路径的元数据 |
fstat(fd, &st) |
fd | 已打开对象的元数据 |
opendir(path) |
目录路径 | 目录流 DIR * |
readdir(dirp) |
目录流 | 下一个目录项 |
closedir(dirp) |
目录流 | 释放目录流 |
常用字段/宏:st_size 表示字节数;st_mode 编码类型与权限;S_ISREG、S_ISDIR、S_ISSOCK 可分别判断普通文件、目录、套接字。
4.5.3 ③ 为什么会有它
读完整文件来判断大小/类型既慢又不正确。服务器程序经常先看类型、大小、权限;目录流让程序不必自行解析底层目录格式。
4.5.4 ④ 工作流程
#mermaid-svg-Ueypo1BeU24rFpHe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ueypo1BeU24rFpHe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ueypo1BeU24rFpHe .error-icon{fill:#552222;}#mermaid-svg-Ueypo1BeU24rFpHe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ueypo1BeU24rFpHe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ueypo1BeU24rFpHe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ueypo1BeU24rFpHe .marker.cross{stroke:#333333;}#mermaid-svg-Ueypo1BeU24rFpHe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ueypo1BeU24rFpHe p{margin:0;}#mermaid-svg-Ueypo1BeU24rFpHe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ueypo1BeU24rFpHe .cluster-label text{fill:#333;}#mermaid-svg-Ueypo1BeU24rFpHe .cluster-label span{color:#333;}#mermaid-svg-Ueypo1BeU24rFpHe .cluster-label span p{background-color:transparent;}#mermaid-svg-Ueypo1BeU24rFpHe .label text,#mermaid-svg-Ueypo1BeU24rFpHe span{fill:#333;color:#333;}#mermaid-svg-Ueypo1BeU24rFpHe .node rect,#mermaid-svg-Ueypo1BeU24rFpHe .node circle,#mermaid-svg-Ueypo1BeU24rFpHe .node ellipse,#mermaid-svg-Ueypo1BeU24rFpHe .node polygon,#mermaid-svg-Ueypo1BeU24rFpHe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ueypo1BeU24rFpHe .rough-node .label text,#mermaid-svg-Ueypo1BeU24rFpHe .node .label text,#mermaid-svg-Ueypo1BeU24rFpHe .image-shape .label,#mermaid-svg-Ueypo1BeU24rFpHe .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ueypo1BeU24rFpHe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ueypo1BeU24rFpHe .rough-node .label,#mermaid-svg-Ueypo1BeU24rFpHe .node .label,#mermaid-svg-Ueypo1BeU24rFpHe .image-shape .label,#mermaid-svg-Ueypo1BeU24rFpHe .icon-shape .label{text-align:center;}#mermaid-svg-Ueypo1BeU24rFpHe .node.clickable{cursor:pointer;}#mermaid-svg-Ueypo1BeU24rFpHe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ueypo1BeU24rFpHe .arrowheadPath{fill:#333333;}#mermaid-svg-Ueypo1BeU24rFpHe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ueypo1BeU24rFpHe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ueypo1BeU24rFpHe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ueypo1BeU24rFpHe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ueypo1BeU24rFpHe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ueypo1BeU24rFpHe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ueypo1BeU24rFpHe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ueypo1BeU24rFpHe .cluster text{fill:#333;}#mermaid-svg-Ueypo1BeU24rFpHe .cluster span{color:#333;}#mermaid-svg-Ueypo1BeU24rFpHe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ueypo1BeU24rFpHe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ueypo1BeU24rFpHe rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ueypo1BeU24rFpHe .icon-shape,#mermaid-svg-Ueypo1BeU24rFpHe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ueypo1BeU24rFpHe .icon-shape p,#mermaid-svg-Ueypo1BeU24rFpHe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ueypo1BeU24rFpHe .icon-shape .label rect,#mermaid-svg-Ueypo1BeU24rFpHe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ueypo1BeU24rFpHe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ueypo1BeU24rFpHe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ueypo1BeU24rFpHe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 类型 / 大小 / 权限
目录中的名字
是
否
拿到路径或 fd
需要什么?
stat(path) 或 fstat(fd)
opendir(path)
反复 readdir(dirp)
得到目录项?
读取 d_name / d_ino
检查 errno 区分结束与错误
closedir(dirp)
4.5.5 ⑤ 图示说明
属性问题用 stat/fstat,目录列举问题用目录流。readdir 返回 NULL 可能表示结束,也可能表示错误,所以需要结合 errno 判断。
4.5.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
stat 会读取文件正文 |
它读取描述文件的元数据 |
fstat 与 stat 无关 |
两者目标相似,输入分别是 fd 与路径 |
readdir 返回 NULL 一定正常结束 |
也可能是错误 |
4.5.7 ⑦ 面试 / 表达题
已经有 fd 时,为何使用 fstat?
它直接查询该已打开对象的元数据,无需再从路径重新定位。
4.5.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|元数据
内容回答"文件里有什么",元数据回答"它是什么、多少、谁能用";路径用
stat,已打开的 fd 用fstat。
4.6 文件共享与 I/O 重定向(⭐⭐⭐⭐⭐)
4.6.1 ① 一句话
文件共享由三层结构解释:每个进程私有的描述符表、所有进程共享的打开文件表、保存文件信息的 v-node 表;fork 和 dup2 通过改变表项指向关系带来共享与重定向。
4.6.2 ② 大白话解释(含三组生活类比)
同一个磁盘文件不代表同一个"阅读进度"。关键是多个 fd 是否最终指向同一条打开文件表项,因为当前位置保存在那里。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 两张书签 | 两次独立 open 同一本书 |
各自有独立阅读进度 | 真实位置按字节而非页码 |
| 亲子共用一张书签 | fork 后父子共享打开文件表项 |
一方读过后另一方从新位置开始 | 父子还有其他资源差异 |
| 铁路道岔 | dup2(oldfd, newfd) 改接 newfd |
标准输出可转向文件 | dup2 不搬运文件字节 |
#mermaid-svg-YP7JdcZMBNvtsCAA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YP7JdcZMBNvtsCAA .error-icon{fill:#552222;}#mermaid-svg-YP7JdcZMBNvtsCAA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YP7JdcZMBNvtsCAA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YP7JdcZMBNvtsCAA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YP7JdcZMBNvtsCAA .marker.cross{stroke:#333333;}#mermaid-svg-YP7JdcZMBNvtsCAA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YP7JdcZMBNvtsCAA p{margin:0;}#mermaid-svg-YP7JdcZMBNvtsCAA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster-label text{fill:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster-label span{color:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster-label span p{background-color:transparent;}#mermaid-svg-YP7JdcZMBNvtsCAA .label text,#mermaid-svg-YP7JdcZMBNvtsCAA span{fill:#333;color:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA .node rect,#mermaid-svg-YP7JdcZMBNvtsCAA .node circle,#mermaid-svg-YP7JdcZMBNvtsCAA .node ellipse,#mermaid-svg-YP7JdcZMBNvtsCAA .node polygon,#mermaid-svg-YP7JdcZMBNvtsCAA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YP7JdcZMBNvtsCAA .rough-node .label text,#mermaid-svg-YP7JdcZMBNvtsCAA .node .label text,#mermaid-svg-YP7JdcZMBNvtsCAA .image-shape .label,#mermaid-svg-YP7JdcZMBNvtsCAA .icon-shape .label{text-anchor:middle;}#mermaid-svg-YP7JdcZMBNvtsCAA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YP7JdcZMBNvtsCAA .rough-node .label,#mermaid-svg-YP7JdcZMBNvtsCAA .node .label,#mermaid-svg-YP7JdcZMBNvtsCAA .image-shape .label,#mermaid-svg-YP7JdcZMBNvtsCAA .icon-shape .label{text-align:center;}#mermaid-svg-YP7JdcZMBNvtsCAA .node.clickable{cursor:pointer;}#mermaid-svg-YP7JdcZMBNvtsCAA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YP7JdcZMBNvtsCAA .arrowheadPath{fill:#333333;}#mermaid-svg-YP7JdcZMBNvtsCAA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YP7JdcZMBNvtsCAA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YP7JdcZMBNvtsCAA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YP7JdcZMBNvtsCAA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YP7JdcZMBNvtsCAA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YP7JdcZMBNvtsCAA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster text{fill:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA .cluster span{color:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YP7JdcZMBNvtsCAA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YP7JdcZMBNvtsCAA rect.text{fill:none;stroke-width:0;}#mermaid-svg-YP7JdcZMBNvtsCAA .icon-shape,#mermaid-svg-YP7JdcZMBNvtsCAA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YP7JdcZMBNvtsCAA .icon-shape p,#mermaid-svg-YP7JdcZMBNvtsCAA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YP7JdcZMBNvtsCAA .icon-shape .label rect,#mermaid-svg-YP7JdcZMBNvtsCAA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YP7JdcZMBNvtsCAA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YP7JdcZMBNvtsCAA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YP7JdcZMBNvtsCAA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 所有进程共享:v-node 表
所有进程共享:打开文件表
进程 B:私有描述符表
进程 A:私有描述符表
fd 1
fd 4
fd 4
表项 X
位置、访问方式、引用计数
表项 Y
位置、访问方式、引用计数
文件元数据
类型、大小等
4.6.3 ③ 为什么会有它
每个进程都需要自己的 fd 编号,内核又需要共享底层文件信息。引用计数用于判断何时释放打开状态;shell 的 > 和 < 则能只改 fd 指向、不改程序本身。
4.6.4 ④ 工作流程
#mermaid-svg-OpCSvQnM4txDSjga{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OpCSvQnM4txDSjga .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OpCSvQnM4txDSjga .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OpCSvQnM4txDSjga .error-icon{fill:#552222;}#mermaid-svg-OpCSvQnM4txDSjga .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OpCSvQnM4txDSjga .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OpCSvQnM4txDSjga .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OpCSvQnM4txDSjga .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OpCSvQnM4txDSjga .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OpCSvQnM4txDSjga .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OpCSvQnM4txDSjga .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OpCSvQnM4txDSjga .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OpCSvQnM4txDSjga .marker.cross{stroke:#333333;}#mermaid-svg-OpCSvQnM4txDSjga svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OpCSvQnM4txDSjga p{margin:0;}#mermaid-svg-OpCSvQnM4txDSjga .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OpCSvQnM4txDSjga .cluster-label text{fill:#333;}#mermaid-svg-OpCSvQnM4txDSjga .cluster-label span{color:#333;}#mermaid-svg-OpCSvQnM4txDSjga .cluster-label span p{background-color:transparent;}#mermaid-svg-OpCSvQnM4txDSjga .label text,#mermaid-svg-OpCSvQnM4txDSjga span{fill:#333;color:#333;}#mermaid-svg-OpCSvQnM4txDSjga .node rect,#mermaid-svg-OpCSvQnM4txDSjga .node circle,#mermaid-svg-OpCSvQnM4txDSjga .node ellipse,#mermaid-svg-OpCSvQnM4txDSjga .node polygon,#mermaid-svg-OpCSvQnM4txDSjga .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OpCSvQnM4txDSjga .rough-node .label text,#mermaid-svg-OpCSvQnM4txDSjga .node .label text,#mermaid-svg-OpCSvQnM4txDSjga .image-shape .label,#mermaid-svg-OpCSvQnM4txDSjga .icon-shape .label{text-anchor:middle;}#mermaid-svg-OpCSvQnM4txDSjga .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OpCSvQnM4txDSjga .rough-node .label,#mermaid-svg-OpCSvQnM4txDSjga .node .label,#mermaid-svg-OpCSvQnM4txDSjga .image-shape .label,#mermaid-svg-OpCSvQnM4txDSjga .icon-shape .label{text-align:center;}#mermaid-svg-OpCSvQnM4txDSjga .node.clickable{cursor:pointer;}#mermaid-svg-OpCSvQnM4txDSjga .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OpCSvQnM4txDSjga .arrowheadPath{fill:#333333;}#mermaid-svg-OpCSvQnM4txDSjga .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OpCSvQnM4txDSjga .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OpCSvQnM4txDSjga .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OpCSvQnM4txDSjga .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OpCSvQnM4txDSjga .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OpCSvQnM4txDSjga .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OpCSvQnM4txDSjga .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OpCSvQnM4txDSjga .cluster text{fill:#333;}#mermaid-svg-OpCSvQnM4txDSjga .cluster span{color:#333;}#mermaid-svg-OpCSvQnM4txDSjga div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OpCSvQnM4txDSjga .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OpCSvQnM4txDSjga rect.text{fill:none;stroke-width:0;}#mermaid-svg-OpCSvQnM4txDSjga .icon-shape,#mermaid-svg-OpCSvQnM4txDSjga .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OpCSvQnM4txDSjga .icon-shape p,#mermaid-svg-OpCSvQnM4txDSjga .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OpCSvQnM4txDSjga .icon-shape .label rect,#mermaid-svg-OpCSvQnM4txDSjga .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OpCSvQnM4txDSjga .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OpCSvQnM4txDSjga .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OpCSvQnM4txDSjga :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同一路径 open 两次
通常得到两个打开文件表项
文件位置彼此独立
调用 fork
子进程复制描述符表
父子仍指向原打开文件表项
父子共享文件位置
dup2(oldfd, newfd)
必要时先关闭 newfd 的旧指向
newfd 指向 oldfd 所指的表项
两个 fd 共享位置
4.6.5 ⑤ 图示说明
沿"fd → 打开文件表 → v-node"从左到右看:fd 是进程私有索引;打开文件表保存位置和引用计数;v-node 保存文件类型和大小等信息。同一 v-node 不保证同一位置,同一打开文件表项才保证。
4.6.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
同路径 open 两次一定共享位置 |
通常是不同打开文件表项,各有位置 |
fork 后父子读进度独立 |
继承 fd 指向同一打开文件表项,位置共享 |
dup2 复制文件内容 |
它复制描述符表项的指向关系 |
dup2 不影响旧 newfd |
若 newfd 已打开,其旧指向会被关闭 |
4.6.7 ⑦ 面试 / 表达题
为什么 fork 后父子可能"接力"读取不同字符?
子进程复制的是描述符表,但父子 fd 仍指向同一打开文件表项;一个进程读后位置推进,另一个从推进后的位置读。
4.6.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|共享与重定向
是否共享文件位置,关键看多个 fd 是否指向同一打开文件表项;
fork会形成这种共享,dup2则能改变 fd 指向。
4.7 标准 I/O、Unix I/O 与网络上的选择(⭐⭐⭐⭐⭐)
4.7.1 ① 一句话
标准 I/O 用 FILE * 和流缓冲区提供 printf、fgets、fread 等便利接口,底层仍基于 Unix I/O;原书建议磁盘/终端尽量优先标准 I/O,网络套接字优先 RIO。
4.7.2 ② 大白话解释(含三组生活类比)
Unix I/O 像基础运输通道,标准 I/O 像带格式化与收纳服务的前台,RIO 像专为"数量必须核对清楚"的工作准备的助手。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 餐厅前台与后厨 | 标准 I/O → 前台;Unix I/O → 基础操作 | printf 更适合常规文本任务 |
真实餐厅不存在 fd 对应 |
| 大包装零食 | 流缓冲一次取一批、分次使用 | 减少频繁系统调用 | 缓冲会改变可见时机 |
| 对讲机双向通话 | 套接字是双向流,RIO 管理收发字节 | 网络必须重视实际完成量 | 网络协议更复杂 |
#mermaid-svg-k7hkBxj4aiM2RdtX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k7hkBxj4aiM2RdtX .error-icon{fill:#552222;}#mermaid-svg-k7hkBxj4aiM2RdtX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k7hkBxj4aiM2RdtX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k7hkBxj4aiM2RdtX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k7hkBxj4aiM2RdtX .marker.cross{stroke:#333333;}#mermaid-svg-k7hkBxj4aiM2RdtX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k7hkBxj4aiM2RdtX p{margin:0;}#mermaid-svg-k7hkBxj4aiM2RdtX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster-label text{fill:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster-label span{color:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster-label span p{background-color:transparent;}#mermaid-svg-k7hkBxj4aiM2RdtX .label text,#mermaid-svg-k7hkBxj4aiM2RdtX span{fill:#333;color:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX .node rect,#mermaid-svg-k7hkBxj4aiM2RdtX .node circle,#mermaid-svg-k7hkBxj4aiM2RdtX .node ellipse,#mermaid-svg-k7hkBxj4aiM2RdtX .node polygon,#mermaid-svg-k7hkBxj4aiM2RdtX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k7hkBxj4aiM2RdtX .rough-node .label text,#mermaid-svg-k7hkBxj4aiM2RdtX .node .label text,#mermaid-svg-k7hkBxj4aiM2RdtX .image-shape .label,#mermaid-svg-k7hkBxj4aiM2RdtX .icon-shape .label{text-anchor:middle;}#mermaid-svg-k7hkBxj4aiM2RdtX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-k7hkBxj4aiM2RdtX .rough-node .label,#mermaid-svg-k7hkBxj4aiM2RdtX .node .label,#mermaid-svg-k7hkBxj4aiM2RdtX .image-shape .label,#mermaid-svg-k7hkBxj4aiM2RdtX .icon-shape .label{text-align:center;}#mermaid-svg-k7hkBxj4aiM2RdtX .node.clickable{cursor:pointer;}#mermaid-svg-k7hkBxj4aiM2RdtX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-k7hkBxj4aiM2RdtX .arrowheadPath{fill:#333333;}#mermaid-svg-k7hkBxj4aiM2RdtX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k7hkBxj4aiM2RdtX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k7hkBxj4aiM2RdtX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k7hkBxj4aiM2RdtX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-k7hkBxj4aiM2RdtX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k7hkBxj4aiM2RdtX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster text{fill:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX .cluster span{color:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-k7hkBxj4aiM2RdtX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-k7hkBxj4aiM2RdtX rect.text{fill:none;stroke-width:0;}#mermaid-svg-k7hkBxj4aiM2RdtX .icon-shape,#mermaid-svg-k7hkBxj4aiM2RdtX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k7hkBxj4aiM2RdtX .icon-shape p,#mermaid-svg-k7hkBxj4aiM2RdtX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-k7hkBxj4aiM2RdtX .icon-shape .label rect,#mermaid-svg-k7hkBxj4aiM2RdtX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k7hkBxj4aiM2RdtX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-k7hkBxj4aiM2RdtX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-k7hkBxj4aiM2RdtX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} C 应用程序
标准 I/O
fopen / fgets / fprintf / fread
RIO
rio_readn / rio_writen / rio_readlineb
Unix I/O 系统调用
open / read / write / lseek / close / stat
Linux 内核与文件/设备
| 情况 | 原书的选择建议 | 原因 |
|---|---|---|
| 磁盘文件、终端 | 只要可能,优先标准 I/O | 接口完整、带缓冲、格式化方便 |
| 查询文件属性 | stat/fstat |
标准 I/O 没有对应的元数据接口 |
| 二进制文件 | 不用 scanf 或 rio_readlineb |
它们按文本格式/文本行设计 |
| 网络套接字 | 使用 RIO | 短计数常见,标准 I/O 的流规则与套接字冲突 |
4.7.3 ③ 为什么会有它
标准 I/O 的缓冲和格式化很方便,但同一流交替输入输出有规则;套接字不能像普通文件那样自由 lseek。给同一套接字包装两个标准 I/O 流又会使两个 fclose 都想关闭同一个底层 fd,因此原书建议网络使用 RIO。
4.7.4 ④ 工作流程
#mermaid-svg-HOs745PIAgVQrsPj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HOs745PIAgVQrsPj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HOs745PIAgVQrsPj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HOs745PIAgVQrsPj .error-icon{fill:#552222;}#mermaid-svg-HOs745PIAgVQrsPj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HOs745PIAgVQrsPj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HOs745PIAgVQrsPj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HOs745PIAgVQrsPj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HOs745PIAgVQrsPj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HOs745PIAgVQrsPj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HOs745PIAgVQrsPj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HOs745PIAgVQrsPj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HOs745PIAgVQrsPj .marker.cross{stroke:#333333;}#mermaid-svg-HOs745PIAgVQrsPj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HOs745PIAgVQrsPj p{margin:0;}#mermaid-svg-HOs745PIAgVQrsPj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HOs745PIAgVQrsPj .cluster-label text{fill:#333;}#mermaid-svg-HOs745PIAgVQrsPj .cluster-label span{color:#333;}#mermaid-svg-HOs745PIAgVQrsPj .cluster-label span p{background-color:transparent;}#mermaid-svg-HOs745PIAgVQrsPj .label text,#mermaid-svg-HOs745PIAgVQrsPj span{fill:#333;color:#333;}#mermaid-svg-HOs745PIAgVQrsPj .node rect,#mermaid-svg-HOs745PIAgVQrsPj .node circle,#mermaid-svg-HOs745PIAgVQrsPj .node ellipse,#mermaid-svg-HOs745PIAgVQrsPj .node polygon,#mermaid-svg-HOs745PIAgVQrsPj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HOs745PIAgVQrsPj .rough-node .label text,#mermaid-svg-HOs745PIAgVQrsPj .node .label text,#mermaid-svg-HOs745PIAgVQrsPj .image-shape .label,#mermaid-svg-HOs745PIAgVQrsPj .icon-shape .label{text-anchor:middle;}#mermaid-svg-HOs745PIAgVQrsPj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HOs745PIAgVQrsPj .rough-node .label,#mermaid-svg-HOs745PIAgVQrsPj .node .label,#mermaid-svg-HOs745PIAgVQrsPj .image-shape .label,#mermaid-svg-HOs745PIAgVQrsPj .icon-shape .label{text-align:center;}#mermaid-svg-HOs745PIAgVQrsPj .node.clickable{cursor:pointer;}#mermaid-svg-HOs745PIAgVQrsPj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HOs745PIAgVQrsPj .arrowheadPath{fill:#333333;}#mermaid-svg-HOs745PIAgVQrsPj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HOs745PIAgVQrsPj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HOs745PIAgVQrsPj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HOs745PIAgVQrsPj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HOs745PIAgVQrsPj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HOs745PIAgVQrsPj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HOs745PIAgVQrsPj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HOs745PIAgVQrsPj .cluster text{fill:#333;}#mermaid-svg-HOs745PIAgVQrsPj .cluster span{color:#333;}#mermaid-svg-HOs745PIAgVQrsPj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HOs745PIAgVQrsPj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HOs745PIAgVQrsPj rect.text{fill:none;stroke-width:0;}#mermaid-svg-HOs745PIAgVQrsPj .icon-shape,#mermaid-svg-HOs745PIAgVQrsPj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HOs745PIAgVQrsPj .icon-shape p,#mermaid-svg-HOs745PIAgVQrsPj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HOs745PIAgVQrsPj .icon-shape .label rect,#mermaid-svg-HOs745PIAgVQrsPj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HOs745PIAgVQrsPj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HOs745PIAgVQrsPj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HOs745PIAgVQrsPj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 磁盘文件 / 终端
网络套接字
需要类型、大小、权限
是
否
我要访问什么?
对象类型
优先标准 I/O
优先 RIO
stat / fstat
数据是二进制吗?
按字节/块读取;不用 scanf 或按行函数
可用格式化或按行接口
按实际返回量处理
4.7.5 ⑤ 图示说明
选择接口前问四件事:对象是不是网络?数据是文本还是二进制?要不要元数据?是否必须传够固定长度?答案会把你导向 RIO、按字节读取、stat/fstat 或标准 I/O。
4.7.6 ⑥ 容易混淆
| 错误理解 | 正确理解 |
|---|---|
| 标准 I/O 和 Unix I/O 只能二选一 | 标准 I/O 构建在 Unix I/O 之上 |
| 标准 I/O 缓冲总是纯好事 | 它提高效率,也带来刷新与读写方向状态 |
套接字也是文件,就应优先 fgets/fprintf |
本章建议网络使用 RIO |
| 二进制没有"文本行" | 二进制中可能碰巧出现 0x0a,按行读取会误判边界 |
4.7.7 ⑦ 面试 / 表达题
为什么原书建议网络套接字使用 RIO?
套接字常短计数,RIO 自动处理重复读写;标准 I/O 的读写方向限制与套接字难以协调,两个流包装同一 fd 又会引出重复关闭问题。
4.7.8 ⑧ 一句话总结
!abstract 📌 一句话卡片|接口选择
标准 I/O 适合大多数磁盘和终端任务,RIO 适合网络套接字;二进制数据不要交给文本行或格式化文本读取函数。
5 本章完整核心过程:一次输出到底发生了什么
下面用"程序把一行文本写到标准输出"为主线,把本章概念串起来。
终端或重定向文件 内核 打开文件表 进程描述符表 程序 终端或重定向文件 内核 打开文件表 进程描述符表 程序 #mermaid-svg-UIt5TkqwSf6vhZ0A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UIt5TkqwSf6vhZ0A .error-icon{fill:#552222;}#mermaid-svg-UIt5TkqwSf6vhZ0A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UIt5TkqwSf6vhZ0A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UIt5TkqwSf6vhZ0A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UIt5TkqwSf6vhZ0A .marker.cross{stroke:#333333;}#mermaid-svg-UIt5TkqwSf6vhZ0A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UIt5TkqwSf6vhZ0A p{margin:0;}#mermaid-svg-UIt5TkqwSf6vhZ0A .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UIt5TkqwSf6vhZ0A text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UIt5TkqwSf6vhZ0A .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-UIt5TkqwSf6vhZ0A .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-UIt5TkqwSf6vhZ0A #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-UIt5TkqwSf6vhZ0A .sequenceNumber{fill:white;}#mermaid-svg-UIt5TkqwSf6vhZ0A #sequencenumber{fill:#333;}#mermaid-svg-UIt5TkqwSf6vhZ0A #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-UIt5TkqwSf6vhZ0A .messageText{fill:#333;stroke:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UIt5TkqwSf6vhZ0A .labelText,#mermaid-svg-UIt5TkqwSf6vhZ0A .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .loopText,#mermaid-svg-UIt5TkqwSf6vhZ0A .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UIt5TkqwSf6vhZ0A .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-UIt5TkqwSf6vhZ0A .noteText,#mermaid-svg-UIt5TkqwSf6vhZ0A .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-UIt5TkqwSf6vhZ0A .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UIt5TkqwSf6vhZ0A .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UIt5TkqwSf6vhZ0A .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UIt5TkqwSf6vhZ0A .actorPopupMenu{position:absolute;}#mermaid-svg-UIt5TkqwSf6vhZ0A .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-UIt5TkqwSf6vhZ0A .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UIt5TkqwSf6vhZ0A .actor-man circle,#mermaid-svg-UIt5TkqwSf6vhZ0A line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-UIt5TkqwSf6vhZ0A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 若 k < n,网络或管道场景应继续处理剩余字节 选择 fd 1(标准输出)找到对应打开文件表项write(1, buf, n)读取访问方式和当前文件位置从主存复制实际可写字节返回实际完成量 k返回 k 或 -1
如果 shell 先做了 > out.txt 重定向,流程前面只多了一步:
#mermaid-svg-MX94TQjo3UsOyhjf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MX94TQjo3UsOyhjf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MX94TQjo3UsOyhjf .error-icon{fill:#552222;}#mermaid-svg-MX94TQjo3UsOyhjf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MX94TQjo3UsOyhjf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MX94TQjo3UsOyhjf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MX94TQjo3UsOyhjf .marker.cross{stroke:#333333;}#mermaid-svg-MX94TQjo3UsOyhjf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MX94TQjo3UsOyhjf p{margin:0;}#mermaid-svg-MX94TQjo3UsOyhjf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MX94TQjo3UsOyhjf .cluster-label text{fill:#333;}#mermaid-svg-MX94TQjo3UsOyhjf .cluster-label span{color:#333;}#mermaid-svg-MX94TQjo3UsOyhjf .cluster-label span p{background-color:transparent;}#mermaid-svg-MX94TQjo3UsOyhjf .label text,#mermaid-svg-MX94TQjo3UsOyhjf span{fill:#333;color:#333;}#mermaid-svg-MX94TQjo3UsOyhjf .node rect,#mermaid-svg-MX94TQjo3UsOyhjf .node circle,#mermaid-svg-MX94TQjo3UsOyhjf .node ellipse,#mermaid-svg-MX94TQjo3UsOyhjf .node polygon,#mermaid-svg-MX94TQjo3UsOyhjf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MX94TQjo3UsOyhjf .rough-node .label text,#mermaid-svg-MX94TQjo3UsOyhjf .node .label text,#mermaid-svg-MX94TQjo3UsOyhjf .image-shape .label,#mermaid-svg-MX94TQjo3UsOyhjf .icon-shape .label{text-anchor:middle;}#mermaid-svg-MX94TQjo3UsOyhjf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MX94TQjo3UsOyhjf .rough-node .label,#mermaid-svg-MX94TQjo3UsOyhjf .node .label,#mermaid-svg-MX94TQjo3UsOyhjf .image-shape .label,#mermaid-svg-MX94TQjo3UsOyhjf .icon-shape .label{text-align:center;}#mermaid-svg-MX94TQjo3UsOyhjf .node.clickable{cursor:pointer;}#mermaid-svg-MX94TQjo3UsOyhjf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MX94TQjo3UsOyhjf .arrowheadPath{fill:#333333;}#mermaid-svg-MX94TQjo3UsOyhjf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MX94TQjo3UsOyhjf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MX94TQjo3UsOyhjf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MX94TQjo3UsOyhjf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MX94TQjo3UsOyhjf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MX94TQjo3UsOyhjf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MX94TQjo3UsOyhjf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MX94TQjo3UsOyhjf .cluster text{fill:#333;}#mermaid-svg-MX94TQjo3UsOyhjf .cluster span{color:#333;}#mermaid-svg-MX94TQjo3UsOyhjf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MX94TQjo3UsOyhjf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MX94TQjo3UsOyhjf rect.text{fill:none;stroke-width:0;}#mermaid-svg-MX94TQjo3UsOyhjf .icon-shape,#mermaid-svg-MX94TQjo3UsOyhjf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MX94TQjo3UsOyhjf .icon-shape p,#mermaid-svg-MX94TQjo3UsOyhjf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MX94TQjo3UsOyhjf .icon-shape .label rect,#mermaid-svg-MX94TQjo3UsOyhjf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MX94TQjo3UsOyhjf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MX94TQjo3UsOyhjf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MX94TQjo3UsOyhjf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} shell 打开 out.txt
得到某个 fd,例如 4
dup2(4, 1)
fd 1 改指向 out.txt
程序仍然 write(1, ...)
字节写入 out.txt,而非终端
读者该抓住什么:
- 应用程序的代码可以保持
write(STDOUT_FILENO, ...)不变。 - 真正决定目标的是 fd 1 当前指向的打开文件表项。
- 一次
write的返回值k才能证明写了多少;若需要完整传输,剩余n - k不能丢。
6 重点概念关系图:谁保存"当前位置"?
#mermaid-svg-JhmgFf3PtVQCvlm5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JhmgFf3PtVQCvlm5 .error-icon{fill:#552222;}#mermaid-svg-JhmgFf3PtVQCvlm5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JhmgFf3PtVQCvlm5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .marker.cross{stroke:#333333;}#mermaid-svg-JhmgFf3PtVQCvlm5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JhmgFf3PtVQCvlm5 p{margin:0;}#mermaid-svg-JhmgFf3PtVQCvlm5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster-label text{fill:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster-label span{color:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster-label span p{background-color:transparent;}#mermaid-svg-JhmgFf3PtVQCvlm5 .label text,#mermaid-svg-JhmgFf3PtVQCvlm5 span{fill:#333;color:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .node rect,#mermaid-svg-JhmgFf3PtVQCvlm5 .node circle,#mermaid-svg-JhmgFf3PtVQCvlm5 .node ellipse,#mermaid-svg-JhmgFf3PtVQCvlm5 .node polygon,#mermaid-svg-JhmgFf3PtVQCvlm5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .rough-node .label text,#mermaid-svg-JhmgFf3PtVQCvlm5 .node .label text,#mermaid-svg-JhmgFf3PtVQCvlm5 .image-shape .label,#mermaid-svg-JhmgFf3PtVQCvlm5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JhmgFf3PtVQCvlm5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .rough-node .label,#mermaid-svg-JhmgFf3PtVQCvlm5 .node .label,#mermaid-svg-JhmgFf3PtVQCvlm5 .image-shape .label,#mermaid-svg-JhmgFf3PtVQCvlm5 .icon-shape .label{text-align:center;}#mermaid-svg-JhmgFf3PtVQCvlm5 .node.clickable{cursor:pointer;}#mermaid-svg-JhmgFf3PtVQCvlm5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .arrowheadPath{fill:#333333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JhmgFf3PtVQCvlm5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JhmgFf3PtVQCvlm5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JhmgFf3PtVQCvlm5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster text{fill:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 .cluster span{color:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JhmgFf3PtVQCvlm5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JhmgFf3PtVQCvlm5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JhmgFf3PtVQCvlm5 .icon-shape,#mermaid-svg-JhmgFf3PtVQCvlm5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JhmgFf3PtVQCvlm5 .icon-shape p,#mermaid-svg-JhmgFf3PtVQCvlm5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JhmgFf3PtVQCvlm5 .icon-shape .label rect,#mermaid-svg-JhmgFf3PtVQCvlm5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JhmgFf3PtVQCvlm5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JhmgFf3PtVQCvlm5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JhmgFf3PtVQCvlm5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 某个进程
描述符表
fd → 打开文件表项
打开文件表
当前位置、访问方式、引用计数
v-node 表
文件类型、大小等元数据
底层文件或设备
stat / fstat
read / write
dup2 / fork
阅读路径:
read/write最关心打开文件表中的当前位置,所以共享同一个表项就共享读写进度。stat/fstat最终关心 v-node 中的文件类型、大小等元数据。fork/dup2不复制文件字节;它们复制或改写"描述符表如何指向打开文件表"的关系。
7 易混淆概念与常见误区
| 错误理解 | 正确理解 | 为什么易错 | 如何判断 |
|---|---|---|---|
| "文件"就是磁盘文本 | Unix 将多种 I/O 对象抽象为文件 | 日常用词太窄 | 问它能否通过 fd 读写 |
| fd 是全局唯一号码 | fd 是进程私有描述符表中的索引 | 0/1/2 看起来像系统常量 | 不同进程都可能有 fd 3 |
| EOF 是一个字符 | EOF 是读到结尾时的返回状态 | 文本结束符常与 EOF 混说 | 看 read 是否返回 0 |
| 短计数就是失败 | 它可能是正常传输节奏 | 磁盘测试常恰好读满 | 看返回值是否为 -1 |
两次 open 必然共享偏移 |
通常各有打开文件表项 | 底层 v-node 相同容易误导 | 看是否独立 open、继承或 dup2 |
dup2 是复制文件 |
它改写描述符指向 | 函数名像"复制" | 看输出目标是否变了 |
stat 是读文件内容 |
它读的是元数据 | 名称有"统计"意味 | 看结果是否为 st_size、st_mode |
| 标准 I/O 适合一切文件 | 网络套接字应优先 RIO | 套接字也被称为文件 | 看对象是不是网络 |
!warning 两个实战边界
- 不要用
scanf或rio_readlineb读取二进制文件:二进制中的0x0a不必表示一行文本结束。- 不要在同一 fd 上混用带缓冲 RIO 读取与无缓冲
rio_readn:两边可能对"下一个未读字节"持有不同状态。
8 自测与实践 🧪
8.1 基础题(5 题)
- I/O 在本章中指哪两类位置之间的数据复制?
- 新进程默认打开的三个文件描述符通常是什么?
read返回 0 表示什么?它和读取到字节值0x00是一回事吗?stat与fstat最主要的输入差别是什么?dup2(oldfd, newfd)大致改变了哪一层关系?
8.2 理解题(5 题)
- 为什么"同一个文件名"不足以判断两个 fd 是否共享文件位置?
- 网络套接字上,
write(fd, buf, 100)返回 40 后,为什么不能认为 100 字节已发完? O_CREAT、mode和umask如何共同影响新文件的最终权限?- 为什么
rio_readlineb不能与无缓冲rio_readn随意交错使用? - shell 的
cmd > out.txt为什么不需要修改cmd内部的输出语句?
8.3 思考题(3 题)
- 程序把日志写到标准输出。它在终端运行正常,但重定向到文件后内容去了哪里?请从 fd 指向关系解释。
- 父进程
fork后,子进程先从继承 fd 读取一个字节;父进程再读一个字节。为什么它们可能读到相邻而非相同的字节? - 一个程序先读 HTTP 文本行头部,再读指定长度的二进制正文。带缓冲 RIO 中哪两个读取接口可搭配?为什么?
8.4 初级面试 / 表达题(5 题)
- 请用"取件号"类比解释文件描述符,并说明类比的边界。
- 请区分 EOF、短计数和错误三种
read结果。 - 请解释描述符表、打开文件表、v-node 表各自属于谁、保存什么。
- 请说明
fork和两次独立open对文件位置共享的差异。 - 请给出"磁盘文件、二进制文件、网络套接字"三种场景下的 I/O 选择理由。
点击查看答案与评分点
8.4.1 基础题
- 答案: I/O 是主存与外部 I/O 设备之间复制数据;输入从设备到主存,输出从主存到设备。
评分点: 说出"复制数据"和两个方向。 - 答案: 标准输入 0、标准输出 1、标准错误 2。
评分点: 名称与编号对应,并最好说明用途。 - 答案: 返回 0 表示当前位置达到 EOF;它不是读到数值为
0x00的数据字节。
评分点: 区分状态与字节内容。 - 答案:
stat输入路径,fstat输入已打开文件的 fd;两者都读取元数据。
评分点: 路径与 fd 的区别。 - 答案:
dup2改写newfd的描述符表项,使其指向oldfd所指的打开文件表项。
评分点: 不是复制文件字节。
8.4.2 理解题
- 答案: 同一路径可以独立
open两次,得到不同打开文件表项,因此各自有文件位置;只有共享同一打开文件表项才共享位置。
评分点: "打开文件表项""当前位置"。 - 答案: 40 只表示本次实际写出 40;网络可能短计数。若业务要求完整发送 100,程序还要处理余下 60 或在错误时中止。
评分点: 不用请求值代替实际回执。 - 答案:
O_CREAT表示允许创建;mode是程序提出的权限;最终权限是mode & ~umask。
评分点:umask会遮掉一部分权限。 - 答案: 带缓冲 RIO 可能已从内核取走一批字节保存在内部缓冲;无缓冲读取不知道这批字节,混用会破坏读取位置的一致性。
评分点: 内部缓冲与未读字节状态。 - 答案: shell 先用
dup2改写 fd 1 的指向;命令仍写标准输出,但标准输出已连到out.txt。
评分点: 改的是 fd 指向,不是命令源码。
8.4.3 思考题
- 答案: 重定向后 fd 1 指向
out.txt的打开文件表项,因此原有标准输出的字节被写入该文件。
评分点: fd 1 的目标发生变化。 - 答案:
fork后父子 fd 指向同一打开文件表项;子进程读取会推进共享位置,父进程随后从新位置读取。
评分点: 不是文件内容复制了一份。 - 答案: 用
rio_readlineb读文本行头部,再用rio_readnb读正文;两者同属带缓冲读取接口,原书说明可在同一描述符上交错调用。
评分点: 两者共享同一个rio_t读取状态。
8.4.4 面试 / 表达题
- 答案要点: fd 像取件号,程序持编号、内核保存打开状态;边界是 fd 可共享或被重定向,不能当成静态票据。
- 答案要点: -1 是错误,0 是 EOF,正数是实际读到的字节;正数小于请求量是短计数,未必错误。
- 答案要点: 描述符表每个进程独有;打开文件表和 v-node 表为进程共享结构;前者保存位置/引用计数,后者保存类型、大小等。
- 答案要点: 两次独立
open通常各有位置;fork让父子描述符指向相同打开文件表项,位置共享。 - 答案要点: 磁盘/终端优先标准 I/O;二进制用按字节/块接口;网络套接字使用 RIO 处理短计数与流限制。
8.5 实践 1|观察标准输出重定向
- 目标: 观察程序不改输出代码,仅改变 fd 1 指向就能把终端输出写入文件。
- 前提/环境: Linux、WSL 或课程提供的 Unix 环境;一个临时测试目录;无需管理员权限。
- 预计时间: 15 分钟(估计)。
- 风险与提醒: 在临时目录操作;
>会覆盖同名out.txt,不要在重要文件目录执行。
步骤
- 新建空的临时目录并进入,例如:
mkdir csapp-io-lab && cd csapp-io-lab。 - 执行:
printf 'hello I/O\n',观察文本显示在终端。 - 执行:
printf 'hello I/O\n' > out.txt。 - 执行:
cat out.txt,记录屏幕内容与文件内容。
预期观察: 第二次 printf 的文本不直接显示,而是在随后 cat 时出现。程序仍向标准输出写,只是 shell 已把 fd 1 重定向。
复盘问题
- 哪个符号触发了重定向?它改的是标准输入、标准输出还是标准错误?
- 若把
>换成<,你预计改变哪个默认描述符?
8.6 实践 2|手工追踪"剩余字节"循环
- 目标: 看懂"累计已完成量、继续处理剩余量"的控制结构。
- 前提/环境: 能阅读基础 C 语法;不要求真的连接网络。
- 预计时间: 20 分钟(估计)。
- 风险与提醒: 以下是教学伪代码,不是完整网络协议实现;真实程序还需处理超时、协议边界和资源释放。
c
size_t left = wanted;
char *p = buffer;
while (left > 0) {
ssize_t got = read(fd, p, left);
if (got < 0) {
/* 错误:退出或交给上层处理 */
break;
}
if (got == 0) {
/* EOF:无法再补足 wanted 字节 */
break;
}
p += got;
left -= (size_t)got;
}
步骤
- 假设
wanted = 10,第一次read返回 4。 - 手工填写第一次循环后
p前进多少、left还剩多少。 - 假设第二次返回 6,判断循环是否结束。
- 再把第一次返回改成 0 或 -1,写出应走分支。
预期观察: 只有累计到 10 才叫完整拿到 10 字节;返回 4 是成功读取,但尚未完成业务目标。
复盘问题
- 为什么
read的返回值要用有符号类型接收? - 这段思路与
rio_readn要解决的问题有什么关系?
9 本章速查表
| 概念 / 接口 | 一句话解释 | 为什么重要 | 容易混淆什么 |
|---|---|---|---|
open |
按路径建立一次访问并返回 fd | 一切 Unix I/O 的起点 | 文件名与 fd |
close |
结束对 fd 的使用 | 释放打开状态与资源 | 以为进程退出才需处理 |
read |
从当前文件位置读取实际可得字节 | 区分数据、EOF、错误 | 请求长度与实际长度 |
write |
向当前位置写实际可写字节 | 网络/管道可能短写 | 返回正数就写满 |
| EOF | 当前读位置到结尾 | 正确停止读取 | 0x00、换行 |
| RIO | 处理短计数/中断的包装 | 网络 I/O 更可靠 | 自动解决所有错误 |
stat / fstat |
读取文件元数据 | 判断类型、大小、权限 | 读取正文内容 |
readdir |
逐项读取目录流 | 遍历目录名称 | NULL 一定正常结束 |
| 描述符表 | 进程私有 fd 索引表 | 解释 fd 与重定向 | 全系统共享表 |
| 打开文件表 | 保存位置、访问方式、引用计数 | 决定是否共享位置 | v-node 表 |
dup2 |
让 newfd 指向 oldfd 的目标 |
I/O 重定向核心 | 复制文件内容 |
| 标准 I/O | C 库 FILE * 流接口 |
常规磁盘/终端更方便 | 对网络同样合适 |
10 本章总结与下一步
10.1 今天真正学会了什么
- Linux 用"文件"统一表达多种 I/O 对象,用 fd 代表一次已打开的访问。
read/write返回的是实际完成量;EOF、短计数和错误是三件不同的事。- RIO 用循环与缓冲封装可靠读写,特别适合网络。
stat/fstat看元数据,readdir看目录条目。- 只有理解描述符表、打开文件表、v-node 表,才能看懂独立打开、
fork共享与dup2重定向。 - 标准 I/O 是便利的高层接口;网络套接字有自己的边界,应优先使用 RIO。
10.2 下一步怎么学
原书下一章进入网络编程。开始前,建议不看笔记回答:
- 为什么套接字也能用 fd 表示?
- 为什么网络上一次
read不保证收到完整应用消息? - fd 1 被 shell 重定向到文件后,
write(1, ...)为什么不必修改?
!tip 30 秒复述挑战
不用术语也讲清:程序先拿"取件号"访问设备;每次传多少以回执为准;共享、
fork和重定向的关键不是复制字节,而是谁指向同一份"已打开状态"。
11 待巩固清单
- 我能解释 fd 0、1、2 的默认含义,并知道它们能被重定向。
- 我能从
read的返回值区分数据、EOF 和错误。 - 我不会把短计数误判为系统一定失败。
- 我能说出独立
open与fork后继承 fd 的文件位置差异。 - 我能解释
dup2(oldfd, newfd)为什么能实现>。 - 我能为文本、二进制、网络套接字选择合适的 I/O 思路。