ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CSAPP 导读(十):系统级 I/O

CSAPP 导读(十):系统级 I/O 本文是《深入理解计算机系统》CSAPP中文版第 3 版第 10 章的导读。CSAPP 是计算机系统经典本系列逐章导读把书的核心概念讲清楚。一句话主旨一切皆文件——fd 是号牌read/write 统一所有设备write 返回成功≠落盘这是事故裂缝也是性能设计核心概念拆解1. Unix I/O 的极简主义——一切皆文件第 1 章埋的「文件抽象」在这里兑现。模型只有五个动词动词作用备注open/close打开/关闭拿到/归还文件描述符read/write从当前位置读/写字节返回实际字节数可能比请求的少lseek移动当前位置网络流不能 seek**文件描述符fd**是内核给每个进程的「句柄小账本」里的编号0标准输入、1标准输出、2标准错误开局自动有三张。打开文件领新号通常取最小空闲号close 归还。进程能同时开的 fd 有上限ulimit -n——「Too many open files」就是这句号簿满了。被打开的文件 vs 描述符要分清两个东西描述符表每进程编号 → 打开文件表项打开文件表全系统共享文件位置偏移、引用计数、指向 inode所以 fork 后父子共享同一个文件表项、共享同一个偏移不是各读各的dup 复制描述符也是共享表项。而独立 open 同一文件是两个表项、两个偏移。shell 的重定向 file就是 fork 后把 1 号描述符关掉、重新 open 到 file——execve 继承这套描述符这就是cmd log 21的全部机制。2. 「写入成功」是谎言——缓冲链的真相write(fd, buf, n)返回 n数据在哪看完整链路内核觉得合适时(脏页回写, 通常~30s内)fsync(fd)强制刷盘, 返回才真落盘write() 返回成功内核页缓存(DRAM, 可随时丢给磁盘)磁盘**关键认知write 返回成功 ≠ 数据在磁盘上。**从 write 成功到真正落盘之间断电数据就没了。这就是数据库 WAL、关键日志必须fsync的原因——性能换持久性一次 fsync 是一次磁盘往返毫秒级「每条都 fsync」和「从不 fsync」之间是吞吐和安全性的滑杆。「进程没崩但机器断电日志丢了最后几分钟」的机制解释——数据在页缓存里进程层面一切正常。「拔内存条再插回去数据还在吗」这类问题的判据——页缓存是 DRAM断电即失。read 是镜像的内核先看页缓存命中直接返回内存速度不命中才去磁盘——第 6 章的层次结构思想在第 9 章VM和本章页缓存三次出场这是最后一次。3. 为什么要有标准 I/O 库——缓冲的三级火箭直接用 read/write 有两个痛点每次调用都是系统调用用户态↔内核态切换有固定开销第 8 章和没有结构化接口printf 怎么实现。标准 I/Ostdio在系统调用之上再包一层你的代码 → stdio 缓冲区(用户态, KB级) → write系统调用 → 页缓存(内核) → 磁盘三层缓冲各司其职stdio 缓冲攒大批量摊薄系统调用开销还记得「8KB 块大小 vs 每字节一个 syscall」的性能差异吗——机制就在这页缓存攒脏页摊薄磁盘寻道。但缓冲是双刃剑缓冲的数据「看不见」printf 之后数据在 stdio 缓冲里别的进程读文件/管道读不到——必须 fflush。调试时「日志顺序不对/少了几条」的经典原因stdout 指向终端是行缓冲每行刷重定向到文件变全缓冲4KB 才刷——同一个程序跑在终端和重定向到文件行为不同坑过无数人。混合两套 I/O 会乱同一文件既用 stdio 又用裸 fd 读写两者的「位置」不同步stdio 缓冲已经预读了fd 的偏移在别处——数据错乱。规则一个文件从头到尾只用一套。4. 用 RIO 处理「短计数」——健壮 I/O 的教训read/write 返回的字节数可能少于请求short count读到文件尾、信号打断第 8 章 EINTR、内核缓冲满……网络套接字上尤其常见。CSAPP 实现了 RIORobust I/O封装循环读写直到凑够 n 字节。这个封装的价值不在代码本身而在它示范的原则系统调用不保证一次到位健壮的代码必须循环。日常语言的库Python 的f.read(n)、recv until buffer full都内置了这个循环但你应该知道它们在防什么。5. 读取文件元数据与目录stat/fstat拿元数据大小、类型、权限、inode、时间戳——ls -l的实现就是它。目录是一种特殊文件内容是「文件名 → inode」的链接表。硬链接 同一 inode 多个名字引用计数rm 只是减计数归零才真删符号链接 一个存路径的特殊文件指向的名字可跨文件系统、可悬垂。ls -i看 inode 号能让你亲眼分辨两者。文件类型判别普通/目录/符号链接/管道/socket……「一切皆文件」的家族谱系。章末「问题→方案」总结问题——程序要与磁盘、终端、网络、管道交互设备千差万别且都慢直接操作每个设备既不可移植又低效而「写入成功」背后还藏着数据丢失的坑。方案——Unix I/O 统一模型一切皆文件open/read/write/close 五个动词走天下用 fd 句柄管理、用缓冲链stdio 缓冲摊薄系统调用 页缓存摊薄磁盘访问换性能用 fsync 在关键节点买回持久性。代价——每层缓冲都让「数据在哪」变得更难回答stdio 缓冲让数据对别人暂时不可见、页缓存让 write 的成功变成承诺而非事实——持久性、实时性、吞吐三者要用 fflush/fsync 手动调节而短计数和 fd 泄漏是使用这套模型的两个永久税。看 I/O 问题的入口永远是那张缓冲链图数据现在在哪一层下一篇导读十一网络编程。
返回列表