当前位置: 首页 > news >正文

bash Buffering

这是一个在 C 语言、Linux/Unix 操作系统和 Shell (如 Bash) 中都非常基础且重要的概念。理解它能帮你搞清楚很多“为什么程序A | B 不按我预想的顺序输出”的奇怪问题。


1. 什么是“缓冲” (Buffering)?

首先,我们为什么需要“缓冲”?

想象一下,你有一个程序在往磁盘上写 10000 个字符。

  • 无缓冲 (Unbuffered):写 1 个字符,就调用 1 次操作系统去写磁盘。这非常慢,因为磁盘操作很“昂贵”。
  • 有缓冲 (Buffered):程序在内存里开辟一块“缓冲区”(比如 4KB)。它先把 10000 个字符写到这个内存缓冲区里,写满 4KB 后,一次性让操作系统把这 4KB 写入磁盘。这样效率就高得多。

这个“缓冲区”就像一个**“草稿箱”“购物车”**,你先把东西(数据)放进去,攒够一定数量/条件后,再一次性“结账”(发送)。


2. 三种标准的 I/O 缓冲模式

在标准 C 库 (libc) 中,I/O(输入/输出)主要有三种模式:

  1. 不缓冲 (Unbuffered)

    • 规则:数据立刻被发送。
    • 例子:标准错误流 stderr 默认就是不缓冲的。这是为了程序一出错,你必须立刻看到错误信息,不能让它卡在缓冲区里。
  2. 全缓冲 (Fully Buffered)

    • 规则:数据被发送,当且仅当缓冲区满了(比如 4096 字节)或者程序结束了。
    • 例子:当你的程序输出是重定向到一个文件时 (./my_app > log.txt),stdout 自动切换为全缓冲,因为写文件的效率最重要。
  3. 行缓冲 (Line Buffered)

    • 规则:数据被发送,当遇到一个换行符 \n(即你按的回车键)时,或者缓冲区满了。
    • 例子:当你的程序输出是发送到一个终端 (Terminal) 时,stdout 自动切换为行缓冲。

3. Bash/Linux 中的“潜规则”:行缓冲的触发

这是最关键的部分。一个程序(比如 grep, awk 或你写的 C/Python 程序)使用哪种缓冲模式,是由它的“输出目的地”决定的。

规则 A:输出到“人”(终端屏幕)
  • 场景:你直接在 Bash 里运行 grep "hello" file.txt
  • 模式stdout (标准输出) 自动设为 行缓冲
  • 原因:这是为了交互性grep 每找到一行匹配,它就输出这一行(带着 \n),缓冲区因为 \n 而被“刷新”(flush),你就能立刻在屏幕上看到这一行结果。你不需要等 grep 找到 4KB 的结果才显示。
规则 B:输出到“非人”(文件或管道)
  • 场景 1 (文件)grep "hello" file.txt > result.txt
  • 场景 2 (管道)grep "hello" file.txt | wc -l
  • 模式stdout (标准输出) 自动切换为 全缓冲
  • 原因:这是为了效率
    • 在场景 2 中,操作系统认为 grepwc -l 之间的数据传输不需要“交互性”(反正人也看不见中间过程)。
    • grep 会把找到的结果(比如 100 行)先塞进它的 4KB 缓冲区,等缓冲区满了,才一次性把这 4KB 数据通过管道 (|) 扔给 wc -l
    • 这大大减少了两个进程间的通信次数,性能更高。

4. “行缓冲”如何导致了困惑?

这就是最常见的“坑”。

例子:你有一个监控日志的命令,它每 1 秒输出一行带时间的日志。

# a_script.sh (一个模拟脚本)
while true; doecho "LOG: $(date)"sleep 1
done

场景 1:直接运行(行缓冲)

$ ./a_script.sh
LOG: Sun Nov 16 23:20:01 JST 2025
LOG: Sun Nov 16 23:20:02 JST 2025
... (每秒正常输出一行) ...
  • echo 输出一个带 \n 的字符串。
  • stdout 目的地是终端,所以是行缓冲
  • \n 触发刷新,你每秒都能看到输出。

场景 2:通过管道(全缓冲)

$ ./a_script.sh | grep "LOG"
  • 你运行这个命令,会发现终端什么也不输出,或者等了很久(比如几分钟)才突然爆发式地输出一大堆。
  • 为什么?
    1. a_script.shstdout 目的地不再是终端,而是管道 |
    2. 它的 stdout 自动切换为全缓冲(比如 4KB)。
    3. echo 的每一行输出(大概 30 字节)都被塞进了缓冲区,但没有 \n 并不足以触发刷新(因为不是行缓冲模式了)。
    4. 程序必须持续运行,直到塞满了 4KB (大概 100 多行日志) 的缓冲区,才会一次性把这 4KB 数据发给 grep

5. 如何强制修改缓冲模式?

你可以使用 stdbuf (Set STanDard BUFfer) 命令来强制改变一个程序的缓冲模式。

  • stdbuf -i0:把**输入(stdin)**设为不缓冲
  • stdbuf -o0:把**输出(stdout)**设为不缓冲
  • stdbuf -oL:把**输出(stdout)**设为行缓冲

解决上面的问题:

我们强制 a_script.shstdout 即使在管道中也使用行缓冲 (-oL)。

$ stdbuf -oL ./a_script.sh | grep "LOG"
LOG: Sun Nov 16 23:25:10 JST 2025
LOG: Sun Nov 16 23:25:11 JST 2025
... (现在每秒都能正常通过 grep 输出了) ...

总结:回到你的 nc 例子

  • 你运行 nc localhost 2008
  • 你的终端(Terminal)本身就在行缓冲模式下工作(这叫 “canonical mode”)。
  • 你按 8 8 8 8,这些字符被放进终端的输入缓冲区(“草稿纸”)。nc 程序毫不知情
  • 你按 Enter(即 \n)。
  • 终端的行缓冲规则被触发,它把“草稿纸”上的所有内容 8888\n 一次性交给了 nc 程序的标准输入 (stdin)
  • nc 收到数据,立刻通过网络发送。
http://www.dtcms.com/a/618403.html

相关文章:

  • 区块链论文速读 CCF A--USENIX Security 2025(6)
  • SAP FICO工单成本明细报表
  • 网站建设需要看什么书长沙专业企业建站联系人
  • 金华永康网站建设手机优化怎么关闭
  • 斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Lecture 9: Scaling laws 1
  • 银川网站网站建设数据库网站建设公司
  • MyBatis-Plus 三种数据库操作方式详解 + 常用方法大全
  • C++鼠标滑块轨迹 - 解决“京东滑块验证码”
  • 中山做网站的wordpress官网流量统计插件下载
  • 出口贸易公司网站怎么做煤棚网架加工厂
  • 如何自学建网站国内免备案网站空间
  • VS CMake报错Vbin32缺失?三步搞定
  • 用Rust实现一个简易的rsync(远程文件同步)工具
  • 如何用ps做照片模板下载网站哪里有网站建设
  • 【MIT-OS6.S081作业1.5】Lab1-utilities xargs
  • 文档抽取技术:通过OCR、NLP和机器学习技术,将非结构化的合同、发票等文档转化为结构化数据
  • 网站调研怎样做装修公司设计图
  • 西安网站制作优化顺德网站建设itshunde
  • 46 修改购物车数据
  • VUE的创建与配置
  • 44_FastMCP 2.x 中文文档之FastMCP集成:AWS Cognito 指南
  • 旅游网站建设市场分析公司就两个开发
  • 武昌手机网站59网一起做网站
  • 对抗拖库 —— Web 前端慢加密
  • BMAD-METHOD 开发方法论实践指南
  • MVC 模型
  • 【图像处理基石】如何从色彩的角度分析一张图是否是好图?
  • 从 1.56% 到 62.9%:SFT 推理微调优化实战
  • Java 实战:图书管理系统(ArrayList 应用)
  • 网站建设客户资料收集清单普洱茶网站建设