news 2026/2/12 16:13:20

Linux 基础 IO 学习笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 基础 IO 学习笔记

。Linux 基础 IO 学习笔记

最近学习了 Linux 的基础 IO,从底层原理到实际应用,整理一下核心概念。

一、从磁盘说起

要理解文件 IO,先得知道数据存在哪。

磁盘的物理结构是这样的:多个盘片叠在一起,每个盘片有上下两个盘面,每个盘面对应一个磁头。盘面上有很多同心圆叫磁道,磁道被切成小块叫扇区,一个扇区通常 512 字节。

所有盘面上同一位置的磁道组成一个柱面。定位数据需要三个参数:柱面(磁头移到哪个位置)、磁头(选哪个盘面)、扇区(那一圈上的哪一块),简称 CHS。

但是对程序员来说,CHS 太复杂了。所以操作系统把磁盘抽象成线性结构,给所有扇区编号,变成一维数组。定位一个扇区只需要一个数字 LBA(逻辑块地址)。磁盘控制器会自动把 LBA 转换成 CHS,程序员不用关心物理结构。

二、一切皆文件

Linux 有个核心设计思想:一切皆文件。

不管是磁盘文件、键盘、显示器还是网络,都被抽象成文件,都可以用 read/write 来操作。

这样做的好处是,开发者只需要学一套 API 就能操作各种设备。但问题是,每个硬件的读写方式明明不一样,怎么做到统一的?

答案是函数指针。内核用 struct file 来描述打开的文件,里面有个 f_op 字段,是个函数指针,指向具体的操作函数。磁盘文件的 f_op->read 指向 disk_read,键盘的指向 keyboard_read,网络的指向 socket_read。

用户调用 read(fd, buf, n) 的时候,内核通过 fd 找到对应的 struct file,然后调用 file->f_op->read()。接口统一,实现各异,这其实就是多态的思想,只不过是用 C 语言的函数指针实现的。

三、文件描述符 fd

那 fd 是什么?

fd 就是进程文件描述符表的下标。每个进程都有一个文件描述符表,本质是个数组,里面的指针指向对应的 struct file。

每个进程默认打开三个文件:fd=0 是 stdin,fd=1 是 stdout,fd=2 是 stderr。

当你 open 一个文件,内核会创建一个 struct file,然后在 fd 表里找一个最小的空位,把指针填进去,返回这个下标给你,这就是 fd。

所以 fd 是连接用户程序和内核文件结构的桥梁。通过 fd 可以找到 struct file,struct file 里有缓冲区地址、读写位置、操作函数等所有信息。

四、struct file 里有什么

每次 open 都会创建一个新的 struct file,里面包含:

f_pos:当前读写位置
f_mode:打开模式(只读/读写)
f_flags:打开标志(O_APPEND 等)
f_op:操作函数指针
f_inode:指向文件的元信息
还有内核缓冲区的指针

不同进程打开同一个文件,各自有独立的 struct file,所以 f_pos、f_mode 这些是独立的。但是 f_inode 指向同一个 inode,因为是同一个物理文件。

这就解释了为什么两个进程可以用不同的模式打开同一个文件,各自的读写位置也互不影响。

五、两层缓冲区

这是重点中的重点。

缓冲区分了两层:语言层缓冲区和内核缓冲区。

语言层缓冲区在用户态,比如 C 库的 FILE 结构体里就有一个。printf、fprintf、fwrite 这些函数都是先把数据写到这个缓冲区里,不会立刻进入内核。

内核缓冲区在内核态,在 struct file 里。数据从语言缓冲区刷新到内核缓冲区后,什么时候写入磁盘就是操作系统决定的事了。

数据流向:printf 写数据 -> 语言缓冲区 -> 刷新 -> 内核缓冲区 -> 操作系统决定时机 -> 磁盘

我们认为把数据交给操作系统就相当于交给了硬件,剩下的内核负责。

六、C 库的 FILE 和内核的 struct file

这两个都叫 file,容易混淆。

C 库的 FILE 在用户态,定义在 stdio.h 里。里面包含了 fd(_fileno 字段)和用户态缓冲区的指针。

内核的 struct file 在内核态,定义在 linux/fs.h 里。里面包含了内核缓冲区指针、inode 指针、操作函数指针等。

两者通过 fd 联系。FILE 里存了 fd,刷新的时候通过 write(fd, …) 把用户态缓冲区的数据送到内核。

各自提供各自的接口:
C 库管用户态缓冲区:fopen/fread/fwrite/fflush/fclose
内核管内核缓冲区:open/read/write/fsync/close

七、缓冲区的刷新

语言缓冲区什么时候刷新到内核?

  1. 遇到 \n(行缓冲模式下,比如 stdout 指向终端时)
  2. 缓冲区满了
  3. 手动调用 fflush
  4. 进程正常退出(exit 会刷新,_exit 不会)
  5. 关闭文件 fclose

内核缓冲区什么时候刷新到磁盘?

  1. 内核定时刷新(后台线程)
  2. 缓冲区满了
  3. 手动调用 fsync
  4. 文件关闭
  5. 系统关机

语言层缓冲区存在的意义是减少系统调用次数。系统调用很贵,每次都要从用户态切换到内核态。有了缓冲区,可以攒一批数据再一起 write,比频繁小写入快得多。

八、read 和 write 的本质

理解了缓冲区,就能理解 read/write 的本质:拷贝。

write 是把用户缓冲区的内容拷贝到内核缓冲区。
read 是把内核缓冲区的内容拷贝到用户缓冲区。

数据在各层之间流动,本质都是拷贝。这也是为什么 IO 操作相对慢,后来才有了 mmap、零拷贝等技术来减少拷贝次数。

还有一点要注意:read 只是把数据读到内存里,不会自动显示在屏幕上。要显示还得再 write 到 stdout。比如 cat 命令,内部就是 read 从文件读到 buf,然后 write 把 buf 写到 fd=1。

九、重定向原理

理解了 fd,重定向就很好理解了。

close(1);// 关闭 stdoutintfd=open("log.txt",O_WRONLY|O_CREAT,0666);// fd 会分配为 1,因为 1 是最小可用的printf("hello");// 写到 log.txt 而不是屏幕

因为 fd=1 现在指向 log.txt,printf 默认写到 fd=1,所以就写到文件里了。shell 里的 ./a.out > log.txt 就是这么实现的。

重定向还会改变缓冲模式:stdout 指向终端时是行缓冲,\n 会触发刷新;重定向到文件后变成全缓冲,\n 不再触发刷新。

十、fork 和缓冲区的经典问题

这个问题很经典,面试常考:

printf("hello");// 没有 \nfork();

直接运行,输出一次 hello。重定向到文件,输出两次。

原因:重定向后 stdout 变成全缓冲,\n 不触发刷新。fork 的时候,用户态缓冲区里还有 hello 没刷新。fork 会复制整个用户空间内存,包括 C 库的缓冲区。所以父子进程各有一份 hello,各刷新一次,就输出两遍了。

write 是系统调用,直接写到内核,没有用户态缓冲区,所以不受影响,只输出一次。

解决方法:加 \n、fork 前 fflush、用 write 代替 printf。

十一、其他细节

open 的标志位:O_RDONLY、O_WRONLY 这些是宏定义,底层是数字,用位运算组合。用 | 组合多个选项,内核用 & 判断设置了哪些。

文件的三个时间:atime(访问时间)、mtime(修改时间)、ctime(属性改变时间)。

引用计数:struct file 有引用计数,用来决定什么时候释放资源。这也是为什么删除一个正在被使用的文件,它不会立刻消失,等最后一个进程关闭才真正删除。

总结

学完这些,对文件系统的理解清晰多了:

从磁盘的物理结构开始,理解数据是怎么存储的。然后是一切皆文件的设计思想,通过函数指针实现统一接口。fd 是连接用户态和内核态的桥梁。两层缓冲区各有各的作用,语言层减少系统调用,内核层减少磁盘 IO。数据在各层之间流动,本质都是拷贝。fork 会复制用户态缓冲区,这是很多坑的来源。

理解了这些底层原理,很多以前觉得奇怪的现象就都能解释了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/11 11:22:48

TwitchLink技术解析:构建专业级Twitch内容采集解决方案

TwitchLink技术解析:构建专业级Twitch内容采集解决方案 【免费下载链接】TwitchLink Twitch Stream & Video & Clip Downloader/Recorder. The best GUI utility to download/record Broadcasts/VODs/Clips. 项目地址: https://gitcode.com/gh_mirrors/tw…

作者头像 李华
网站建设 2026/2/9 12:25:31

300亿参数StepVideo-T2V:AI视频生成新范式

300亿参数StepVideo-T2V:AI视频生成新范式 【免费下载链接】stepvideo-t2v 项目地址: https://ai.gitcode.com/StepFun/stepvideo-t2v 导语 StepFun公司正式发布300亿参数文本到视频生成模型StepVideo-T2V,通过创新的深度压缩视频VAE和3D全注意…

作者头像 李华
网站建设 2026/2/9 6:14:03

通义千问3-Embedding-4B性能测试:不同硬件对比

通义千问3-Embedding-4B性能测试:不同硬件对比 1. 引言 随着大模型在语义理解、检索增强生成(RAG)和跨模态搜索等场景的广泛应用,高质量文本向量化模型的重要性日益凸显。阿里云推出的 Qwen3-Embedding-4B 是 Qwen3 系列中专为「…

作者头像 李华
网站建设 2026/2/5 7:35:29

Unsloth零成本微调Gemma 3:12B模型极速优化指南

Unsloth零成本微调Gemma 3:12B模型极速优化指南 【免费下载链接】gemma-3-12b-it-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/gemma-3-12b-it-GGUF 导语 Unsloth推出零成本微调方案,让开发者可在普通硬件上高效优化Gemma 3 12B…

作者头像 李华
网站建设 2026/2/12 1:06:07

TradingView图表库实战指南:从基础集成到高级定制

TradingView图表库实战指南:从基础集成到高级定制 【免费下载链接】charting-library-examples Examples of Charting Library integrations with other libraries, frameworks and data transports 项目地址: https://gitcode.com/gh_mirrors/ch/charting-librar…

作者头像 李华
网站建设 2026/2/9 14:27:50

BepInEx完整入门教程:Unity游戏模组开发终极指南

BepInEx完整入门教程:Unity游戏模组开发终极指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 想要为Unity游戏添加自定义功能却不知从何入手?BepInEx作为…

作者头像 李华