标准IO概念
01 Linux 操作系统分层结构
一、四层分层模型(从上到下)
- 应用层:自己写的程序、各类软件(app)
- C 库函数层:标准 IO(printf/fopen),封装底层系统调用
- 内核层:系统调用(open/read/write)、驱动程序,操作系统核心
- 硬件层:磁盘、键盘、显示器、网卡等物理设备
通俗理解
你写printf("hello") → 调用 C 标准库printf → 库内部调用内核系统调用write → 内核驱动控制显卡把文字打印到屏幕。
二、32 位系统 4G 虚拟内存分区(通俗拆解)
32 位程序总共拥有 4GB 虚拟地址空间,分为两大块:
1. 内核空间(3G ~ 4G,高地址)
- 存放操作系统内核代码、驱动、内核数据
- 用户程序完全不能直接访问,想要操作硬件必须通过系统调用切换到内核态
- 所有进程共享同一份内核空间
2. 用户空间(0G ~ 3G,低地址,每个进程独立)
从低地址到高地址依次划分:
- 代码段 .text:程序编译后的机器指令(只读)
- 已初始化数据段 .data:定义时赋值的全局 / 静态变量
int a=10; - 未初始化数据段 .bss:只声明不赋值的全局 / 静态变量
int b; - 堆 heap:
malloc/free动态申请释放内存,地址向上增长 - 栈 stack:局部变量、函数参数、函数返回地址,地址向下增长
- 空白未使用虚拟内存区间
补充知识点
64 位系统虚拟内存空间极大,分区逻辑一致,只是地址范围更大; 每个进程拥有独立用户空间,进程之间内存隔离,互不干扰。
02 标准 IO 介绍
2.1 IO 基础概念
IO 定义
IO = Input/Output,输入输出;程序和外部设备(文件、键盘、屏幕、网络)之间传输数据。
IO 作用
- 输入:程序读取外部数据(键盘输入、读取本地文件、接收网络数据)
- 输出:程序向外输出数据(打印屏幕、写入日志文件、发送网络消息)
2.2 IO 两大分类
分类 1:按实现层级(核心考点)
- 系统调用 IO(文件 IO) 底层内核原生接口:
open/read/write/close/lseek
- 运行在内核空间,每次读写都要切换用户态→内核态,开销大
- 无用户层缓冲区,操作精细,实时性高
- 平台不兼容:Linux 和 Windows 系统调用完全不一样
- 库函数 IO(标准 IO) C 标准库封装:
printf/scanf/fopen/fgets/fwrite
- 运行在用户空间,自带缓冲区,减少系统调用次数,效率更高
- ANSI C 统一标准,Linux、Windows、macOS 代码通用,移植性强
分类 2:按应用场景
- 文件 IO:读写磁盘 txt、日志、配置文件
- 设备 IO:读写键盘、显示器、鼠标;Linux「一切皆文件」,硬件全部抽象成文件,读写硬件和读写文件代码写法一致
- 网络 IO:浏览器、APP 网络通信,收发网络数据包
2.3 系统调用 vs 标准库函数对比表(原文 + 通俗补充)
表格
| 对比维度 | 系统调用 (open/read/write) | 标准 IO 库函数 (fopen/printf) |
|---|---|---|
| 层级 | 内核底层接口 | 用户态高层封装,基于系统调用实现 |
| 运行空间 | 内核态,需要 CPU 特权切换 | 用户态,无需频繁切换内核 |
| 性能开销 | 每次读写都切换内核,频繁操作开销大 | 自带缓冲区,攒一批数据再调用一次系统调用,开销更低 |
| 移植性 | 依赖操作系统,跨系统无法直接使用 | ANSI C 标准,全平台通用 |
| 核心特点 | 无用户缓冲区,实时性强 | 三层缓冲机制,减少硬件交互,开发简单 |
2.4 标准 IO 详细介绍
定义
标准 IO 是<stdio.h>提供的一套输入输出函数,由 C 语言国际标准 ANSI C 规定,所有操作系统都实现这套接口。
三大优势
- 跨平台:一套代码 Windows/Linux/macOS 直接编译运行
- 缓冲提速:用户内存开辟缓冲区,攒满数据再调用系统调用,减少磁盘交互,大幅提升 IO 速度
- 开发简单:屏蔽硬件底层细节,不用关心磁盘、显卡驱动,一行函数完成读写
标准 IO 完整执行流程
程序调用printf(用户态库函数)→ 数据存入用户态缓冲区 触发刷新条件(缓冲区满 / 换行 / 程序结束)→ 调用write系统调用(切换内核态) 内核把数据存入内核页缓存 → 内核异步写入硬件屏幕 / 磁盘
03 流 (stream)
3.1 流定义
所有 IO 操作都抽象成字节流动,数据从外部流入程序 = 输入流,程序数据流出到外部 = 输出流,统一称为流。
通俗比喻
流就是一根水管,字节就是水流;读数据 = 水流入程序,写数据 = 水流出程序。
3.2 流两种分类
- 文本流 按 ASCII/UTF-8 字符处理,适合 txt、日志、代码文件;换行符
\n会根据系统自动转换。 - 二进制流 直接读取原始二进制字节,不做任何字符转换;适合图片、视频、可执行程序、压缩包。
重点:复制图片必须用二进制流,用文本流会损坏文件。
3.3 程序默认自动打开 3 个标准流(进程启动自带,无需 fopen)
表格
| 流名称 | 指针常量 | 绑定设备 | 缓冲特性 | 作用 |
|---|---|---|---|---|
| 标准输入流 | stdin | 键盘 | 行缓冲 | scanf、getchar 读取用户输入 |
| 标准输出流 | stdout | 终端屏幕 | 行缓冲 | printf 打印普通信息 |
| 标准错误流 | stderr | 终端屏幕 | 无缓冲 | perror、错误打印,立刻输出不缓存 |
通俗解释缓冲差异
- stdout 行缓冲:不加
\n文字不会立刻打印,等到换行才输出; - stderr 无缓冲:报错信息马上显示,不会卡住看不到,专门用来打印错误。
04 FILE 对象
4.1 FILE 对象是什么
FILE 是一个结构体,用来存储当前打开文件 / 流的全部信息:文件描述符、缓冲区起始 / 结束地址、读写指针、文件状态标志、EOF 结束标记、错误标记等。 简化内部结构(glibc 真实源码):
c
运行
struct _IO_FILE {
int _fileno; // 底层文件描述符(数字0/1/2对应标准流)
char* _IO_buf_base; // 缓冲区开头
char* _IO_buf_end; // 缓冲区末尾
char* _IO_read_ptr; // 当前读位置
char* _IO_write_ptr; // 当前写位置
int _flags; // 读写、错误、结束标志
};
typedef struct _IO_FILE FILE;
4.2 FILE 指针核心规则
- 结构体本身体积大,标准 IO不直接操作结构体变量,全部使用
FILE*指针间接访问; - 创建 FILE 指针:
FILE *fp = fopen("a.txt", "r"); - 销毁、释放资源:
fclose(fp);(同时刷新缓冲区) - 所有标准 IO 函数参数都需要 FILE 指针:
fread/fwrite/fgets/ftell等
4.3 FILE 与底层系统调用的关系
- FILE 是用户态标准库抽象;
- 内核用数字文件描述符 fd管理文件(0=stdin,1=stdout,2=stderr);
- FILE 结构体内部保存 fd,标准 IO 函数底层会通过 fd 调用
read/write系统调用; - 用户缓冲区存在 FILE 结构体内部,是标准 IO 提速的核心。
拓展章节:系统头文件 tags 生成 & vim 代码跳转
作用
系统头文件存放在/usr/include,里面有 printf、FILE、fork 等标准定义;生成 tags 索引后,vim 可以一键跳转到函数 / 结构体源码定义,方便阅读底层库代码。
完整操作步骤(Ubuntu)
1. 安装 ctags 工具
bash
运行
sudo apt install exuberant-ctags
# 验证安装
ctags --version
2. 进入系统头文件目录,生成全局 tags(需要 sudo)
bash
运行
cd /usr/include
sudo ctags -R --c-kinds=+p --fields=+iaS --extra=+q .
参数释义:
-R:递归扫描所有子目录头文件--c-kinds=+p:额外索引函数原型声明--fields=+iaS:记录变量作用域、结构体继承信息--extra=+q:增强宏、类型定义检索
3. 配置 vim 永久加载 tags 索引
bash
运行
echo "set tags+=/usr/include/tags" >> ~/.vimrc
4. vim 跳转使用方法
- 光标放在
FILE/printf上,快捷键Ctrl+]跳转到定义 Ctrl+o:退回跳转前代码位置- 命令行直接跳转:
:tag FILE - 终端直接打开并跳转:
vim -t FILE
通俗用处
看代码时不知道 FILE 结构体内部有什么,光标放到 FILE 按Ctrl+],直接跳转到 stdio.h 的源码定义,不用手动翻文件。
全文补充拓展(课本缺失重点)
1. 三种缓冲区完整规则(标准 IO 核心考点)
- 全缓冲(磁盘文件默认) 缓冲区填满 /fflush 强制刷新 /fclose 关闭 / 程序正常退出,才会调用 write 刷数据;默认缓冲区 4096/8192 字节。
- 行缓冲(stdin/stdout 终端) 遇到换行
\n、缓冲区满、程序退出,立刻刷新打印。 - 无缓冲(stderr 错误流) 数据写入瞬间直接输出,不缓存,保证报错立刻可见。
2. 标准 IO 与文件 IO 混合使用坑
标准 IO 自带用户缓冲区,文件 IO 无缓冲;混合读写同一个文件会出现数据错乱,禁止混用。
3. fflush 强制刷新缓冲区
fflush(stdout); 手动把缓冲区数据立刻刷入内核,不用等换行,解决 printf 不打印问题。
4. 流关闭注意事项
fclose(fp)会自动刷新缓冲区,进程崩溃时缓冲区未刷盘会丢失数据,重要文件建议主动 fflush。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)