ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零看懂计算机(十二):IO 流与缓冲——三颗雷,一次全爆

从零看懂计算机(十二):IO 流与缓冲——三颗雷,一次全爆 本文是专栏《从零看懂计算机》的第 12 篇共 16 篇连载整个系列铺垫最久的一章。第 4 篇的顺手缓存、第 6 篇的写完了灯还在跳、第 9 篇的递一万次条子——三颗雷埋了八篇今天一次全爆。爆完你会发现你每天保存的每个文件背后都是同一场流水作业。文章目录写在前面一、一切皆流一套动作对付所有数据源二、第一颗雷爆递一万次条子第 9 篇2.1 回放案发现场2.2 两笔账三、缓冲攒一批再走3.1 快递打包3.2 读缓冲门口的小周转仓3.3 写缓冲先攒在门口再整批发3.4 和第 4 篇的分工两层各管各的四、第二、三颗雷爆页缓存与写完不落盘4.1 页缓存全机器共享的黄金缓冲带4.2 灯还在跳的真身writeback4.3 fsync程序员的插队权4.4 安全账缓冲的代价4.5 三雷合爆总图一次 write 的完整旅程五、文本与二进制给人看的 vs 原样字节5.1 两种流5.2 换行符两大门派的百年恩怨5.3 怎么选六、动手实验缓冲区大小对决6.1 实验设计6.2 提示词直接发给 AI6.3 读数指南高潮在这6.4 验证第三颗雷30 秒观察6.5 防坑表6.5 进阶给勇者的 100MB七、自测10 个问题八、本文总结写在最后附术语速查表写在前面三笔旧账先摆出来第 9 篇 4.4 节“递条子不贵但递一万次条子很贵。读文件一个字节敲一次门……和攒一车再敲一次——差百倍”第 6 篇实验后“你的程序’写完退出了’磁盘灯活动时间却还在跳——数据真的写完了吗”第 4 篇缓存思想第 9 篇影子表“文件’顺手缓存在内存空余处’——它干的第 12 篇引爆”。本篇逐笔清算。本篇你能搞懂“流”——程序跟外界交易的一套统一动作缓冲为什么能快百倍syscall 账单 物理伺候账单两笔一起算write()返回 ≠ 数据落盘——灯还在跳的真身亲手用三档缓冲区复制文件量出百倍差距。一、一切皆流一套动作对付所有数据源程序读文件、收网络数据、等你敲键盘、发打印任务——姿势千差万别计算机把它们统一成同一种抽象流Stream——想象一根水管数据从一端流进另一端。对付任何流都是三步打开 → 读/写 → 关闭。打开 登记领一个门牌号文件描述符/句柄——此后你拿着门牌办事不用每次报全名关闭 销账——忘了关 第 11 篇泄漏的流版门牌是有限资源每个进程通常几千个拿了不还迟早无牌可领Too many open files。为什么值得统一学会一套动作就通吃所有数据源——第 16 篇的看板要同时读系统指标流和网络接口用的就是同一套姿势。这颗钉子先钉在这。二、第一颗雷爆递一万次条子第 9 篇2.1 回放案发现场第 9 篇说过程序碰任何硬件都要敲门递条子系统调用一次过路费百纳秒到微秒级——单看不贵。现在把账放大读一个 100 MB 文件一个字节一个字节地读read(1)调 1 亿次。每次都是一整套用户态→陷入内核态→OS 干活→返回。就算每次只花 1 微秒光敲门费就是100 秒——还没算仓库的伺候费。2.2 两笔账账单每次成本1 字节读 100MB4KB 一车读敲门费syscall~μs 级1 亿次 →百秒级约 2.6 万次 →几十毫秒仓库伺候费HDD ~13ms/次随机第 6 篇若每次都真去仓库天荒地老同样 2.6 万次顺序读摊薄第 6 篇已经教过伺候费的解法顺序读磁头不动一路读近乎免费。但敲门费不归物理管——它是软件层的固定成本只能用软件层的办法摊攒车。于是——缓冲登场。三、缓冲攒一批再走3.1 快递打包一件件快递各自打车出门每件都要交一遍出车费打包装整车发一车摊一次——单件固定成本被凑单摊薄。缓冲区就是你的打包台。3.2 读缓冲门口的小周转仓真正发生的事你调read(1)运行库先垫一整辆卡车比如 4KB~8KB搬进你程序门口的用户态缓冲区你要的那 1 个字节从门口递给你。接下来几千次read(1)全从门口拿——敲门次数从 1 亿次降到 2.6 万次。第 9 篇的雷就此拆除引信。3.3 写缓冲先攒在门口再整批发写同理你write(1)一个字节先攒在门口缓冲攒满一车才真正敲一次门发出去。看起来皆大欢喜——但这里埋着第三颗雷的引线write()返回了货只到了门口最多到了 OS 手里还没上车没落盘。3.4 和第 4 篇的分工两层各管各的第 4 篇CPU 缓存本篇IO 缓冲层硬件层对程序透明软件层程序员可见可调缓冲区大小你自己选摊什么指令/数据的访问延迟系统调用的固定成本思想同源固定成本高的地方就用拉近距离/凑批量摊薄它第 9 篇影子表说文件顺手缓存在内存空余处——那笔顺水人情其实有两笔接下来拆开。四、第二、三颗雷爆页缓存与写完不落盘4.1 页缓存全机器共享的黄金缓冲带页缓存Page CacheOS 在内存里划的一大片全机器共享的文件缓冲带——第 5 篇说内存是墙上格子OS 把读过的文件页顺手存在这里。读第二次读同一个文件数据还在页缓存 →根本不去仓库直接从内存递给你——这就是顺手缓存在内存空余处的全貌第一颗子弹写write()把数据丢进页缓存就返回——这格内存被标记为脏页Dirty“和盘上不一致待发货”。4.2 灯还在跳的真身writebackOS 的发货策略攒一批脏页闲时或到点整批发车Linux 的周期量级是几十秒脏页攒太多也会提前触发。这个后台批量刷盘动作就是writeback。所以第 6 篇的现场程序写完退出门口还有一车货没发——OS 的 writeback 线程还在替你发货磁盘活动灯还在跳。不是 bug是设计用落盘推迟换写吞吐。第 9 篇说程序退出≠货已到仓今天补完下半句退货单要等 writeback 跑完才算结清。4.3 fsync程序员的插队权有时候你等不起几十秒数据库记账、转账落凭据——fsync(fd)强制立刻把这批脏页刷到盘上刷完才返回。是保命符也是性能杀手它亲手放弃了凑单优惠一单一单发。数据库慢但绝不丢账的底气一半来自它。4.4 安全账缓冲的代价突然断电门口的货页缓存里的脏页没了——盘上的还是旧版。所以复制完就拔 U 盘可能丢文件——不是迷信是 writeback 没跑完“安全弹出的本质就是等发车”关机走正常关机而非常按电源——第 2 篇的开机是把 OS 请回来正常关机的隐藏任务是让 OS 把没发的货全发完。4.5 三雷合爆总图一次 write 的完整旅程你的程序 用户态缓冲 页缓存(内存) 磁盘 |--write(1)----→| | | | (攒车) |--整车syscall----→| 标记脏页 | | | |--writeback批量发车--→| ← 灯在跳的就是这段 | | |←--fsync插队强制发车--|三颗雷的位置也齐了第 9 篇的雷在第一段箭头syscall 次数敲门费第 4 篇的雷在中间那个方框页缓存读缓存写缓冲同体顺手存在内存空余处第 6 篇的雷在第二段箭头writeback 异步发车灯还在跳。一根管子上三颗雷本来就是同一条流水线上的三个工位。五、文本与二进制给人看的 vs 原样字节5.1 两种流文本流给人看的字——要经过编码/解码第 8 篇的编码回勾你≠ 固定几个字节UTF-8/GBK 各有各的拼法换行符还要被翻译见 5.2二进制流原样字节一个不动——图片、视频、可执行文件必须走这条。5.2 换行符两大门派的百年恩怨打字机时代的手柄动作回车CR换行LF“两大门派分家继承Windows 存\r\n、Linux/macOS 存\n。文本模式读写时运行库默默替你翻译——跨系统互发纯文本文件偶尔多出^M/换行全无”就是翻译现场。二进制模式不翻译——所以复制图片必须rb/wb忘加那个 b图片经手一次就报废新手经典车祸。5.3 怎么选一句话内容要读给人类的 → 文本流内容要原样搬运的 → 二进制流。拿不准就用二进制——不翻译永远不会错只是辛苦你自己的眼睛。六、动手实验缓冲区大小对决6.1 实验设计复制同一份文件缓冲区三档1 字节 / 4KB / 1MB——你要亲手量出第 9 篇那句差百倍。先用 20MB 版本三档都能在几分钟内跑完再进阶。6.2 提示词直接发给 AI请用 Python 写一个缓冲区大小对决实验 1. 先生成一个 20MB 的二进制测试文件 test.bin随机字节即可 2. 写函数 copy_with_buffer(src, dst, chunk) open(src, rb, buffering0) 和 open(dst, wb, buffering0) buffering0 关掉 Python 自带的用户态缓冲让每次 read/write 都真敲一次系统调用的门——这样才能量出缓冲本身的差距 循环 f.read(chunk) / g.write(data) 直到读完 3. 分别用 chunk 1、4096、1048576 三档复制并计时打印每档耗时 和等效速度MB/s 4. 最后加一行对照shutil.copyfile() 同样复制一遍计时——这是 专业搬运工的答案打印完提醒我对比它落在哪一档。6.3 读数指南高潮在这看比值不看绝对值老规矩1 字节档 vs 4KB 档差多少倍——每一倍差距都是敲门费你每档少敲 4096 倍的门4KB → 1MB 为什么只快一点点边际递减4KB 时敲门费已摊得很薄剩下的耗时是搬运本身——车再大路只有那么长第 6 篇的顺序吞吐是天花板shutil 落在哪专业搬运工用的缓冲是 64KB 量级Windows 上 1MB——不是越大越好是够摊成本就行车太大占内存每辆车都是你的堆房第 11 篇、还让 fsync 更心疼两种机器两种主导SSD 机器上三档差距主要是敲门费HDD 机器上小 chunk 还会踩随机伺候第 6 篇 13ms 账单——差距更夸张。你的数字是哪种主导只有跑了才知道。防幻觉纪律第十二条AI 不许报你的机器复制 20MB 应该花几秒。它没有你的盘、你的文件系统、你的页缓存状态——第八件实测才作数前七件跑分、盘参数、帧率、网络归因、进程清单、语言倍数、内存曲线。6.4 验证第三颗雷30 秒观察请写一个灯还在跳验证脚本 1. 用 wb, buffering0 往 slow.bin 写 50MB每 1MB write 一次 2. 写完立刻打印write 返回了开始观察 10 秒…… 3. 每 2 秒打印一次 psutil.disk_io_counters() 的读写字节数 4. sleep 10 秒后结束提示我对照任务管理器→性能→磁盘的 活动时间第 6 篇说的数字版硬盘灯。看输出write 全部返回之后磁盘写入字节数还在涨——那就是 writeback 在替你发货。第 6 篇的现象今天你亲手复现了它。6.5 防坑表症状原因怎么办1 字节档跑了三四分钟2000 万次×两次 syscall正常耐心或先跑 10MB 版——比值不变三档差距只有几倍OS 页缓存SSD 太快放大了物理底噪把文件加大一倍再跑或对比 1 字节 vs 1MB计时抖动大后台有别的程序也在搬货第 9 篇侦探技能上场关掉大户跑三轮取中位shutil 快得反常它可能直接走系统级复制接口正常——“专业搬运工连抄答案都嫌慢”1 字节档中途想放弃恭喜你正在亲身支付敲门费CtrlC 终止用已跑时间估算全程AI 说应该 0.5 秒左右违反纪律十二把实测三行数字发回去只准解读6.5 进阶给勇者的 100MB三档全跑 100MB1 字节档预计十几分钟起步——不是必须但跑过的人对敲门费的体感和没跑过的人不是一个物种。也可以只跑 4KB 和 1MB 两档看大文件下的边际递减。七、自测10 个问题✅ 答案折叠先自己过一遍再展开点开流的三步动作打开→读/写→关闭——打开领门牌关闭销账忘了关流的后果门牌泄漏文件描述符是有限资源迟早 Too many open files——泄漏的流版1 字节读 100MB 慢的两笔账敲门费1 亿次 syscall×微秒级伺候费若真去仓库HDD 随机 13ms/次缓冲摊薄的是哪笔、怎么摊的摊固定成本——软件层凑批量门口攒一车再敲一次门页缓存是什么、脏页是什么OS 在内存划的全机器共享文件缓冲带写入页缓存但还没落盘的页待发货程序写完退出了灯还在跳的真身writebackOS 攒脏页后台批量刷盘——设计而非 bugfsync 换来什么、付出什么强制立刻落盘数据安全放弃凑单优惠性能三颗雷分别在这条流水线的哪个工位第 9 篇用户态缓冲→内核的 syscall第 4 篇页缓存方框第 6 篇页缓存→盘的 writeback 箭头复制图片为什么必须 “rb”/“wb”文本模式会翻译换行符——二进制数据经手即报废专业搬运工 shutil 的缓冲多大、说明了什么64KB 量级/Windows 1MB——够摊成本就行不是越大越好八、本文总结① 一切皆流打开→读写→关闭一套动作通吃文件/网络/键盘——D16 看板的两种数据源届时同用此姿势。② 两笔账两种解敲门费syscall用软件缓冲摊、伺候费HDD 物理用顺序读摊——4KB 是黄金档更大只买边际。③ write() 返回 ≠ 落盘页缓存脏页writeback 一条链fsync 是插队权、安全弹出是等发车——三颗雷在一条流水线的三个工位上今天全拆完。写在最后单机的流到此收官。但有一类流对岸不是你自己的仓库是别人的机器——网页请求、聊天消息、API 调用。它还是打开→读写→关闭同一套姿势只是敲门之后货要穿过第 8 篇那四层信封、跨越第 8 篇算过的物理距离。下一篇《网络调用Socket 与 HTTP》——程序开口说话的完整现场一个 socket 是怎么诞生的、为什么打电话和发微信是两种等待姿势、以及那道经典面试题浏览器输入网址后发生了什么的完整版答案你已经集齐了 12 篇的零件去拼它。专栏《从零看懂计算机》共 16 篇目录见置顶评论附术语速查表术语一句话流Stream统一的水管抽象打开→读写→关闭文件描述符/句柄打开流领的门牌号有限资源缓冲区buffer门口的攒车台——凑批量摊薄固定成本用户态缓冲程序侧的打包台程序员可调页缓存Page CacheOS 的全机器共享文件缓冲带读缓存写缓冲同体脏页Dirty Page页缓存里和盘不一致、待发货的页writebackOS 后台批量把脏页刷盘灯在跳的真身fsync插队权强制这批数据立刻落盘数据库的保命符文本流 / 二进制流要翻译编码换行符/ 原样字节不动buffering0关掉 Python 自带用户态缓冲——实验专用档shutil标准库的专业搬运工缓冲 64KB/Windows 1MB常见问题Q为什么要 4KB 这么小不用 1MB 起步4KB 恰好对齐文件系统块/页大小第 6 篇敲门费已摊到可忽略再大买的是边际、付的是内存和 fsync 精度——shutil 的选择印证了这个工程共识。Q断电丢的是我保存的文档吗绝大多数软件保存时会 fsync 或至少显式 flush——日常文档基本安全高风险场景是复制完立刻物理拔盘。正常关机OS 发完全部货再走。Q页缓存越来越大是不是内存泄漏不是——军规五同款那是 OS 的工作资产缓存越多越快内存紧张时它会自动让位。别用清理内存把黄金缓冲带扫了。Qwrite() 慢慢攒着程序崩了怎么办脏页在 OS 手里不在程序手里——程序崩了 writeback 照跑货已交给 OS真正丢数据的是断电不是程序崩溃。Q网络下载的进度条卡 99% 在等什么可能就是 fsync/flush货已到齐正在最后一批发车——第 13 篇展开网络侧的同款现象。Q一行print()也算 IO 吗算stdout 就是一条流默认行缓冲——你现在知道它为什么一行一刷了。
返回列表