news 2026/4/23 14:27:08

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/4/21 16:57:56

GetQzonehistory:一键解锁你的QQ空间时光宝盒

GetQzonehistory:一键解锁你的QQ空间时光宝盒 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 还记得那些年发过的第一条说说吗?那些青涩的文字、搞笑的配图、深夜…

作者头像 李华
网站建设 2026/4/23 13:15:59

Glyph长上下文处理实战,论文精读辅助工具

Glyph长上下文处理实战,论文精读辅助工具 1. 引言:长文本建模的挑战与新路径 在当前大模型广泛应用的背景下,长上下文建模已成为智能体、文档问答、法律分析、科研辅助等任务的核心能力。然而,传统基于Transformer架构的语言模型…

作者头像 李华
网站建设 2026/4/23 13:09:40

米家API:解锁智能家居控制新境界

米家API:解锁智能家居控制新境界 【免费下载链接】mijia-api 米家API 项目地址: https://gitcode.com/gh_mirrors/mi/mijia-api 清晨,当第一缕阳光透过窗帘,你躺在温暖的被窝里,轻声说一句"小爱同学,打开卧…

作者头像 李华
网站建设 2026/4/16 10:12:37

如何用Zotero快速配置GB/T 7714标准:3分钟搞定学术论文格式

如何用Zotero快速配置GB/T 7714标准:3分钟搞定学术论文格式 【免费下载链接】Chinese-STD-GB-T-7714-related-csl GB/T 7714相关的csl以及Zotero使用技巧及教程。 项目地址: https://gitcode.com/gh_mirrors/chi/Chinese-STD-GB-T-7714-related-csl 你是不是…

作者头像 李华
网站建设 2026/4/23 13:46:28

BepInEx插件框架:5分钟快速上手Unity游戏模组开发

BepInEx插件框架:5分钟快速上手Unity游戏模组开发 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 还在为Unity游戏模组开发的复杂配置而头疼?想知道如何绕过…

作者头像 李华
网站建设 2026/4/18 7:37:59

终极指南:如何使用acados实现非线性最优控制的快速求解

终极指南:如何使用acados实现非线性最优控制的快速求解 【免费下载链接】acados Fast and embedded solvers for nonlinear optimal control 项目地址: https://gitcode.com/gh_mirrors/ac/acados 在当今的自动化系统中,非线性最优控制问题无处不…

作者头像 李华