Linux 基础IO

目录

理解"⽂件"

狭义理解

• ⽂件在磁盘⾥

• 磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的

• 磁盘是外设(既是输出设备也是输⼊设备)

• 磁盘上的⽂件本质是对⽂件的所有操作,都是对外设的输⼊和输出简称IO

⼴义理解

• Linux下⼀切皆⽂件(键盘、显⽰器、⽹卡、磁盘......这些都是抽象化的过程)

⽂件操作的归类认知

文件=属性(元数据)+内容

• 对于0KB的空⽂件是占⽤磁盘空间的(因为还有属性)

• ⽂件是⽂件属性(元数据)和⽂件内容的集合(⽂件=属性(元数据)+内容)

• 所有的⽂件操作本质是⽂件内容操作和⽂件属性操作

系统⻆度

• 对⽂件的操作本质是进程对⽂件的操作

• 磁盘的管理者是操作系统

• ⽂件的读写本质不是通过C语⾔/C++的库函数来操作的(这些库函数只是为⽤⼾提供⽅便),⽽是通过⽂件相关的系统调⽤接⼝来实现的

linux内核中传递标记位的方法

系统文件IO

c/c++操作文件的相关接口,本质上底层都是封装了文件操作相关的系统调用。

编程语言通过抽象层屏蔽不同操作系统的底层差异,通过语言接口在不同系统映射对应的系统调用,实现跨平台性,使开发者无需关注系统调用接口。

所以c++/c中描述打开文件的类/结构体中一定封装了底层文件描述符(fd),因为系统调用的接口只匹配fd

系统调用open

系统接口中使用open函数打开文件,open函数的函数原型如下:

int open(const char *pathname, int flags, mode_t mode);

  1. 第一个参数:
    路径+文件名,不加路径直接在当前路径查找文件
  2. 第二个参数:标记位
    open第二个参数是指定打开方式,通过位图的原理,将多种打开方式用一个32位的整数表示。
    选项如下:
    可以通过按位或(|)传入多个选项,如下
    O_WRONLY | O_CREAT
  3. 第三个参数:权限
    传入八进制数表示如果创建新文件的初始权限。结合umask权限掩码可得到最终权限
    返回值
    open的返回值是文件描述符(文件描述符表的下标),失败返回-1

close

系统接口中使用close函数关闭文件,close函数的函数原型如下:

int close(int fd);

使用close函数时传入需要关闭文件的文件描述符即可,若关闭文件成功则返回0,若关闭文件失败则返回-1。

read

系统接口中使用read函数从文件读取信息,read函数的函数原型如下:

ssize_t read(int fd, void *buf, size_t count);

我们可以使用read函数,从文件描述符为fd的文件读取count字节的数据到buf位置当中。

如果数据读取成功,实际读取数据的字节个数被返回。

如果数据读取失败,-1被返回。

内核中打开文件的组织形式

与进程描述符(struct task_struct)相似已打开文件都会有一个对应的文件描述(struct file)结构体记录文件的动态信息。

进程操作文件的途径

操作文件的本质是进程对文件的操作,一个进程可能操作多个文件

进程操作文件的途径:

  1. 进程描述符(task_struct)中存在一个文件描述符表(struct files_struct),
  2. 文件描述符表中记录进程打开的文件的信息,包括打开文件的进程描述结构体指针数组(struct file *fd),指向文件的进程描述,由此管理进程打开的文件。
  3. 文件描述(struct file)结构体记录文件的动态信息。
  4. 文件描述符(fd)就是该数组的下标,是最典型的 "句柄" ------ 它本质上是操作系统分配给进程的、用于标识打开文件 / IO 资源的整数型句柄
    类比结合内存描述符理解

不同进程打开同一个文件的struct file问题

struct file中包含打开进程的一些信息(文件位置指针等),所以不同进程打开同一个文件,有各自独立的struct file,而struct file中的引用计数是对于单个进程或父子进程内文件描述符表中指向该struct file的个数。(父子进程子进程通过继承父进程的文件描述符,可以共享指向同一个struct file,引用计数++)

本质:struct file 包含了大量进程特定的状态信息,所以每个进程打开文件时需要独立的实例,而 struct inode 才是真正共享的文件元数据。

文件描述符的分配规则

  1. 进程打开时会默认打开0、1、2,对应标准输入流、标准输出流、标准错误流,0对应键盘,12对应显示器。
    而键盘和显示器都属于硬件,属于硬件就意味着操作系统能够识别到,当某一进程创建时,操作系统就会根据键盘、显示器、显示器形成各自的struct file,将这3个struct file连入文件双链表当中,并将这3个struct file的地址分别填入fd_array数组下标为0、1、2的位置,至此就默认打开了标准输入流、标准输出流和标准错误流。
    由此可以理解linux下一切(资源)皆(抽象为)文件
  2. 自己打开的文件的描述符一般从下标3开始,如果关闭012从关闭位置分配(实现重定向)
  3. 本质是从最小但是没有被使用的fd_array数组下标开始进行分配的。

重定向

重定向的原理

重定向的本质就是修改文件描述符下标对应的struct file*的内容。

  1. 输出重定向:将本应该输出到一个文件的数据重定向输出到另一个文件中。

  2. 追加重定向:和输出重定向相同但唯一区别是,输出重定向是覆盖式输出数据,而追加重定向是追加式输出数据。虽然都对应显示器,但其中一个重定向不会影响另一个。

    分别对应传递open标志位参数的两个宏,追加、覆盖

  3. 输入重定向:将本应该从一个文件读取数据,现在重定向为从另一个文件读取数据。

dup

完成重定向我们只需进行fd_array数组当中元素的拷贝即可,系统调用dup可以实现重定向

原型:int dup2(int oldfd, int newfd);

参数:将第一个参数下标的指针拷贝到第二个参数下标的指针

返回值:成功返回newfd,失败返回-1

注意这里关闭一个fd不会关闭文件,因为文件描述采用类似智能指针的引用计数

stdout和stderr为什么分开

主要原因是为了区分程序的正常输出和错误信息,确保错误信息优先输出。

一切皆文件

"一切皆文件" 的本质是通过统一的抽象接口,将复杂的系统资源转化为可通过文件操作逻辑管理的实体。

统一抽象的文件接口,与底层实现分开,

不同外设都有自己的功能实现,但file提供统一的接口

由此将所有资源用抽象的文件接口进行管理,

这便是一切皆文件

之所以文件系统可以封装设备管理也就是抽象操作方法,本质是因为外设无非也是资源,无非也是I/O数据流动所以可以当作文件

通过在file结构体中存储对应实现方法的指针(与类似c++虚函数表指针)实现多态思想,将接口抽象,与实现解耦,实现一切皆文件

缓冲区

缓冲区是内存空间的⼀部分。也就是说,在内存空间中预留了⼀定的存储空间,这些存储空间⽤来缓冲输⼊或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输⼊设备还是输出设备,分为输⼊缓冲区和输出缓冲区。

常见的缓冲区类型:

  1. 标准 I/O 缓冲区(C 语言库层面)
    由 stdio.h 提供(如 printf、fwrite 使用的缓冲区),在FILE中设置指针进行管理,分为三种模式:
  • 全缓冲:缓冲区满时刷新(如普通文件)。
  • 行缓冲:遇到 \n 或缓冲区满时刷新(如终端 stdout)。
  • 无缓冲:数据立即输出(如 stderr 错误流)。
    FILE中的关键部分:fd(系统文件描述符)、缓冲区相关指针
  1. 内核缓冲区(操作系统层面)
    即使应用程序的缓冲区已刷新,数据也不会直接写入物理设备,而是先进入内核缓冲区,由操作系统统一调度写入(如 Linux 的页缓存)。
  2. 硬件缓冲区
    外设自带的缓冲区(如磁盘的缓存、网卡的接收缓冲区),进一步减少与内存的交互次数。

缓冲区的刷新时机:

  • 主动触发:调用 fflush()(标准 I/O)、sync()(内核缓冲)等函数。
  • 条件满足:缓冲区写满、行缓冲遇到 \n(仅终端)。
  • 被动触发:进程正常退出、关闭文件(fclose() 会自动刷新)。

缓冲区的核心作用:

  1. 平衡速度:让快速设备(如 CPU)不用等待慢速设备(如磁盘),提高整体效率。
  2. 减少开销:批量处理 I/O 操作,降低设备访问频率。
  3. 数据暂存:应对突发的大量数据(如网络峰值流量),避免数据丢失。

标准I/O缓冲区与操作系统内核缓冲区的关系

标准I/O库缓冲区将用户程序中的输入数据暂存,避免频繁调用系统调用(调用成本),当缓冲区刷新时才会将数据发送到下一层(内核缓冲区)。

内核中有内核缓冲区,操作系统有刷新方案进行缓冲区管理,来提升磁盘、网络等设备的 I/O 性能

实例综合理解

相关推荐
智慧地球(AI·Earth)3 小时前
在Linux上使用Claude Code 并使用本地VS Code SSH远程访问的完整指南
linux·ssh·ai编程
老王熬夜敲代码4 小时前
解决IP不够用的问题
linux·网络·笔记
zly35004 小时前
linux查看正在运行的nginx的当前工作目录(webroot)
linux·运维·nginx
QT 小鲜肉4 小时前
【Linux命令大全】001.文件管理之file命令(实操篇)
linux·运维·前端·网络·chrome·笔记
问道飞鱼5 小时前
【Linux知识】Linux 虚拟机磁盘扩缩容操作指南(按文件系统分类)
linux·运维·服务器·磁盘扩缩容
egoist20235 小时前
【Linux仓库】超越命令行用户:手写C语言Shell解释器,解密Bash背后的进程创建(附源码)
linux·c语言·bash·xshell·环境变量·命令行参数·内建命令
Lenyiin5 小时前
《 Linux 修炼全景指南: 八 》别再碎片化学习!掌控 Linux 开发工具链:gcc、g++、GDB、Bash、Python 与工程化实践
linux·python·bash·gdb·gcc·g++·lenyiin
莲华君5 小时前
Bash Shell:从入门到精通
linux
风雨飘逸5 小时前
【shell&bash进阶系列】(二十一)向脚本传递参数(shift和getopts)
linux·运维·服务器·经验分享·bash