尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Linux 重定向和缓冲区

Linux 重定向和缓冲区
📅 发布时间:2026/7/2 17:48:40

📁 第一篇:文件描述符与重定向

理解 fd、struct_file、dup2 和重定向的本质


一、预备知识

1.1 三个默认流

每个进程启动时,内核会默认打开三个文件:

文件流文件描述符对应设备
stdin0键盘
stdout1显示器
stderr2显示器

1.2 核心概念:fd 和 struct_file

文件描述符(fd)是什么?

fd 是一个非负整数,是操作系统为了管理已打开文件而分配的索引号。

你可以把它理解为:进程访问文件的“门牌号”。

c

int fd = open("log.txt", O_WRONLY); // fd = 3
struct_file 是什么?

struct_file是 Linux内核中描述一个已打开文件的数据结构。

当进程调用open()时,内核会:

  1. 创建一个struct_file对象,记录文件路径、偏移量、权限、操作函数指针等信息

  2. 把这个对象的地址放入进程的files_struct数组中

  3. 返回这个数组的索引——也就是fd

text

进程 PCB └── files_struct ├── fd[0] ──→ struct_file (stdin → 键盘) ├── fd[1] ──→ struct_file (stdout → 显示器) ├── fd[2] ──→ struct_file (stderr → 显示器) ├── fd[3] ──→ struct_file (log.txt → 磁盘文件) └── ...
fd 和 struct_file 的关系
概念本质比喻
fd整数(数组下标)门牌号
struct_file内核数据结构房间

进程通过 fd 找到对应的 struct_file,进而操作文件。

1.3 fd 的分配规则

当调用open()打开新文件时,内核会:

  1. 扫描files_struct中的fd数组

  2. 找到当前未被使用的最小 fd

  3. 把新建的struct_file的地址存入该位置

  4. 返回这个 fd

c

close(0); // 释放 fd=0 int fd = open("log.txt", O_WRONLY); // fd = 0(最小未使用)

二、重定向的实现原理

2.1 什么是重定向?

重定向 = 修改 fd 数组中的指针,让某个 fd 指向另一个 struct_file。

2.2 初识重定向

关闭 1(stdout),新打开的文件会占用 fd = 1:

c

int main() { close(1); int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); printf("printf, fd:%d\n", fd); fprintf(stdout, "fprintf, fd:%d\n", fd); return 0; }

运行结果:屏幕上没有输出,log.txt中写入了两行内容。

这就是重定向的本质——通过改变文件描述符的指向,改变数据的输出目标。

2.3 dup2 函数

dup2(oldfd, newfd):让newfd成为oldfd的副本,即让newfd指向oldfd所指向的文件。

c

#include <unistd.h> int dup2(int oldfd, int newfd);

示例:

c

int main() { int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); dup2(fd, 1); // 将 stdout(1) 重定向到 fd 指向的文件 printf("Hello Linux!\n"); // 写入 log.txt fprintf(stdout, "Hello World!\n"); // 写入 log.txt return 0; }

记忆技巧:dup2(fd, 1)表示“将 1 重定向到 fd”。

2.4 重定向的本质总结

text

重定向前: fd[1] ──→ struct_file (显示器) 重定向后(dup2(fd, 1)): fd[1] ──→ struct_file (log.txt)

重定向就是修改 fd 数组中某个槽位的指针,让它指向另一个 struct_file。


📦 第二篇:缓冲区深度解析

理解语言层缓冲区、内核缓冲区、刷新策略


一、引子:一个现象

c

int main() { close(1); int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); printf("printf, fd:%d\n", fd); // fflush(stdout); // 注释掉 close(fd); return 0; }

运行结果:log.txt内容为空。

加上fflush(stdout)后,内容出现。

这说明数据没有直接写入文件,而是先存在了某个地方——这个地方就是缓冲区。


二、缓冲区是什么?

2.1 本质:结构体

在 C 语言中,FILE实际上是_IO_FILE的 typedef:

c

struct _IO_FILE { int _flags; char* _IO_read_ptr; char* _IO_read_end; char* _IO_read_base; char* _IO_write_base; char* _IO_write_ptr; char* _IO_write_end; char* _IO_buf_base; char* _IO_buf_end; int _fileno; // 对应的文件描述符 };

每个打开的文件都有自己的缓冲区,缓冲区中记录了对应的_fileno(文件描述符)。

2.2 缓冲区体系

text

用户代码 ↓ 语言层缓冲区(printf / fprintf / fflush) ↓ 内核层缓冲区(write / read) ↓ 磁盘 / 显示器 / 网络
层级作用
用户层缓冲区减少频繁的系统调用,提升用户体验
语言层缓冲区减少与内核的交互次数,提升性能
内核层缓冲区减少磁盘 I/O 次数,提升系统整体效率

三、缓冲区的刷新策略

策略说明典型场景
立即刷新每写入一点就立刻刷新极少使用
行刷新遇到换行符\n时刷新显示器(为符合人眼阅读习惯)
全缓冲缓冲区满了才刷新普通文件(如写入磁盘)
特殊情况刷新进程退出、调用fflush、exit等进程终止时

为什么显示器用行刷新?

如果一次性全部刷新出来,人眼看不完;如果 1 个字符 1 个字符地打印,体验又太差。所以显示器采用行刷新。


四、为什么缓冲区存在?

为了减少系统调用,提升效率。

直接和 OS 交互(系统调用)成本很高,因为:

  • 涉及用户态/内核态切换

  • OS 忙着调度、回收资源

所以:

  • 语言层缓冲区:把多次小写入合并成一次大写入,减少系统调用

  • 内核层缓冲区:把多次磁盘 I/O 合并成一次,减少磁盘操作

类比:自己翻山越岭送礼物,一次只能送一件;用快递公司批量运输,效率高得多。


五、综合实验:fork与缓冲区

5.1 实验代码

c

int main() { int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); dup2(fd, 1); printf("Hello Linux!\n"); fprintf(stdout, "Hello World!\n"); char* message = "Hello C++!\n"; write(1, message, strlen(message)); fork(); // 创建子进程 return 0; }

5.2 运行结果

log.txt中的内容:

text

Hello Linux! Hello World! Hello C++! Hello Linux! Hello World!

5.3 现象分析

函数写入位置打印次数原因
printf语言层缓冲区2 次子进程复制了父进程的缓冲区,进程退出时各刷新一次
fprintf语言层缓冲区2 次同上
write内核层缓冲区1 次系统调用直接写入内核,不经过语言层缓冲区

5.4 核心结论

printf/fprintf等库函数使用语言层缓冲区,write等系统调用直接写入内核层缓冲区。

fork创建子进程时,会复制父进程的语言层缓冲区内容,导致多打印一份。


六、关键结论

问题答案
缓冲区是什么?结构体(FILE/_IO_FILE),每个文件都有独立的缓冲区
缓冲区为什么存在?减少系统调用,提升效率
缓冲区如何工作?根据刷新策略(行刷新 / 全缓冲 / 立即刷新)决定何时写入内核
printf和write的区别?printf写语言层缓冲区,write直接写内核缓冲区
fork后为什么多打印?子进程复制了父进程的语言层缓冲区

七、总结

缓冲区体系图

text

用户代码 ↓ 语言层缓冲区(printf / fprintf) ↓ (fflush / exit 触发刷新) 内核层缓冲区(write / read) ↓ (OS 调度) 磁盘 / 显示器 / 网络

一句话总结

缓冲区是介于用户代码和内核之间的“中转站”,通过批量处理减少系统调用,提升 I/O 效率。不同的刷新策略适配不同的设备场景。

相关新闻

  • 终极指南:如何使用SysDVR将Switch游戏画面投屏到电脑
  • Linux打印难题终极破解:5种场景深度实战foo2zjs驱动
  • 边缘智能下的水文遥测:差异化上传机制的技术架构与核心逻辑

最新新闻

  • 大模型稀疏激活原理与MoE实战:参数量≠计算量
  • 深圳两企业同日称“大湾区首个”,“最像特斯拉”的智平方200亿估值能否实至名归?
  • SLO2016与TM4C1299KCZAD的LED驱动系统设计与优化
  • Ubuntu 16.04 手动部署 Prometheus 实战指南
  • Gemini 3.1 Pro六边形能力解析:多模态、长上下文与推理协同工作流
  • LLM数学推理工程化:四层防御体系实现可验证解题

日新闻

  • Python Playwright录制功能:从零到一构建自动化测试脚本
  • 如何用开源工具永久保存你心爱的小说:novel-downloader全攻略
  • In-Context Learning不是教知识,而是模式对齐:从5个示例到100个工业级样本的真相

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号