【进程IO】详细讲解文件描述符fd

简介: 【进程IO】详细讲解文件描述符fd

前言

C语言的关于文件操作的各种函数实际上是对系统调用的封装。那么从进程的角度看,一个文件到底是如何被描述的呢?又是如何被组织并管理的呢?

什么叫文件描述符

所有的I/O设备都被模型化为文件,而所有的输入输出都被当成对文件的读写操作。这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单的,低级的应用接口,成为Unix I/O,着使得所有的输入输出都能以一种统一切一致的方式来执行,这也是为什么说linux下一切皆文件。

当程序通过要求内核打开相应的文件。内核会返回一个小的非负整数。这个非负整数就叫做描述符,也叫文件描述符。文件描述符是用于唯一标识文件的号码。下面我用fd表示文件标识符。

也就意味着,我们的进程实际上并不记录文件本身,而只需要记录一个为一个文件ID。所有被打开的文件的信息都被集中在一起被内核管理,内核向进程提供文件的接口。

尝试打开多个文件并观察其文件描述符:

我们发现打开的文件其ID是从3开始的,这是属于巧合吗?

其实并不是

Linux shell 创建的每一个进程开始的时候都有三个打开的文件:标准输入流(fd=0),标准输出流(fd=1),标准错误流(fd=2)。这三个文件分别对应的硬件设备是键盘、显示器、显示器。

在c语言中也会默认给我们打开三个文件

如何证明这三个文件会默认打开呢?

对于以上结果,我们可以直接在stdout文件里写入数据,说明了stdout文件确实是被默认打开了!

再证明stdout文件的描述符是1,看代码:

这里使用系统调用的write函数直接向文件描述符为1的文件写入数据。我们发现文件标识符1指的就是stdout文件。由此,证明了进程会默认打开stdout文件,并且该文件的fd=1.其余两个文件也是同样的道理。

FILE与fd的关系

FILE是C语言用来描述文件的一个结构体,在了解了文件描述符之后,我们其实也能明白FILE的底层实现了对fd的封装

再次理解文件

以用户的视角来看,文件=属性+内容。以操作系统的视角来看,文件其实就是一个名为filestruct

每当我们打开一个文件,操作系统就会生成一个struct file记录该文件的所有信息,包括以何种方式打开,文件的标记位等。并将这个file以双链表的形式加入到所有打开文件的集合中,这个集合统一由操作系统管理

其中值得我们关注的是,struct file内部会包含一个指针,指向该文件的内核级缓存区。进程就是在这个区域里面进行读写数据。该区域的存在避免了进程直接向磁盘读写数据,提高了效率(缓冲区存在的意义)。

除此之外,struct file里还含有文件的类型以及该文件的方法列表。方法列表包括了open方法,write方法等。

为什么要有文件的方法列表呢?

首先所有的硬件都有一定的存储能力。这也就意味着我们能以看待磁盘的视角来看待其他硬件。比如显示器,键盘等。我们所说的什么键盘文件其实就是用来跟键盘的存储空间打交道的。不同硬件读写数据的操作方法可能不一样。比如键盘文件就没有向其写数据的方法,我们只能从键盘读取而不能写入!

所以方法列表中的元素其实就是一个函数指针,指向对应硬件的读写方法。

虽然各个硬件中的读写方法存在差异,但是相同功能的函数名字可以一致。

这样操作系统就能以统一的视角来看待各个硬件的读写方法。

进程和struct file的关系

一个进程可以打开多个文件,对于进程来说,被打开的文件同样也算是一种资源。

为了区分一个进程拥有的“打开文件”集合。操作系统为每个进程建立了一张

struct files_struct表,表中有一个struct file* 数组fd_array,这个数组就指向该进程所有的打开的文件。task_struct中的 struct files_struct* files指针就指向这张表。

fd_array是文件映射关系数组。fd_array数组中的每个元素都是对应操作系统管理文件集合中的某个文件。于是我们就能通过下标访问该数组,从而在操作系统管理文件集合中找到目标文件

非常合理的文件描述符就是fd_array数组的下标!!!

这样我们也就明白了,文件操作的系统调用为什么是用fd来做参数的。因为我们能通过fd准确地找到目标文件。

文件描述符的分配规则

正常来说,每个进程都会默认打开三个文件(0-2),也就导致了进程自己打开的文件的描述符从3开始。

但如果我们将默认的三个文件关闭之后呢?

假设我们关闭标准输入文件流stdin(0),那么我们之后打开的文件其文件描述符会从0开始。也就意味着,每次打开文件其文件描述符是fd_array数组中没有出现的第一个下标。

再次理解open操作

在明白文件是如何被描述以及如何被管理之后,我们也就能更加深刻的理解系统调用open打开文件的过程:

1.创建struct file(包括fd)

2.开辟文件缓存区,加载文件中的数据(延后)

3.查进程的文件描述符表(fd_array数组)

4.将file的内存地址填入到fd_array[fd]中

5.返回fd.

相关文章
|
3月前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
5月前
|
Linux 数据处理 C语言
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
77 0
|
6月前
|
开发框架 并行计算 安全
Python的GIL限制了CPython在多核下的并行计算,但通过替代解释器(如Jython, IronPython, PyPy)和多进程、异步IO可规避
【6月更文挑战第26天】Python的GIL限制了CPython在多核下的并行计算,但通过替代解释器(如Jython, IronPython, PyPy)和多进程、异步IO可规避。Numba、Cython等工具编译优化代码,未来社区可能探索更高级的并发解决方案。尽管GIL仍存在,现有策略已能有效提升并发性能。
71 3
|
5月前
|
Linux C语言 C++
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(上)
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(上)
57 0
|
7月前
|
存储 Linux 开发工具
【Linux】基础 IO(文件描述符)-- 详解(下)
【Linux】基础 IO(文件描述符)-- 详解(下)
|
7月前
|
存储 Linux C语言
【Linux】基础 IO(文件描述符)-- 详解(上)
【Linux】基础 IO(文件描述符)-- 详解(上)
|
7月前
|
程序员 调度 云计算
Python并发编程的未来趋势:协程、异步IO与多进程的融合
Python并发编程的未来趋势:协程、异步IO与多进程的融合
|
7月前
|
网络协议 Linux
【系统DFX】如何诊断占用过多 CPU、内存、IO 等的神秘进程?
【系统DFX】如何诊断占用过多 CPU、内存、IO 等的神秘进程?
163 0
|
Java Linux PHP
【Linux】基础IO——文件操作|文件描述符|重定向|缓冲区
Linux下的文件操作、C语言下的文件操作、文件描述符、重定向的原理和缓冲区的理解。
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能