文件描述符表

news/2024/10/21 12:00:28/

文章目录

  • 1. 文件描述符
    • 1.1 前言
    • 1.2 理解文件
    • 1.3 文件描述附表
    • 1.4 打开文件时
    • 1.5 默认打开的三个文件
  • 2. 重定向
    • 2.1 瞅瞅
    • 2.2 dup2
    • 2.3 实现原理
  • 3. 一切皆文件

1. 文件描述符

1.1 前言

环境:Linux 2.6

在 Linux 中,有句话叫做「一切皆文件」,指的是在 Linux 中的设备,资源等几乎一切资源都抽象成了文件,然后只需要提供对文件进行操作的接口,就可以让我们用统一的方式来读取,写入等各种操作,从而来管理 Linux 中的各种资源和数据。这种设计模式不仅简化了 Linux 架构,还简化了开发人员对资源的操作

1.2 理解文件

当进程打开一个文件的时候,操作系统会为其分配一个文件描述符(先了解,下文讲),当我们获取这个文件描述符之后,就可以对这个文件进行读写等各种操作了

并且这个文件被打开后,在操作系统内核中会为其创建一个结构体 struct file 来进行管理(这和 C 语言的 FILE 结构体不一样),它记录了文件的状态,读写位置等文件信息,它是在内核空间中的,而 C 语言的 struct FILE 是在用户空间中的

并且在 struct file 中存在一个指向缓冲区的指针,可以将数据暂存在缓冲区中,而文件通常会被划分成若干个页(4KB),在打开文件 / 读取文件的时候,操作系统会读取相应的块到该文件的缓冲区中

1.3 文件描述附表

  • Linux 中,进程被描述成 task_struct 进行管理(PCB),而task_struct 中有个指针 struct files_struct* files,该指针负责描述该进程的文件相关数据信息
struct task_struct {volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */void *stack;...struct files_struct *files;   // 负责描述进程的文件信息数据...
}
  • struct files_struct* files 指向的结构体中有一个成员 struct file* fd_array,就是「文件描述符表」(新版本的内核版本可能称为 fdt

在这里插入图片描述

  • 这个文件描述符表,的下标(准确来说是索引)就称为「文件描述符」,而文件描述符表下标对应的对象fd_array[i]就是 struct file*,是指向内核中用来描述被打开文件的结构体
  • 并且在修改相关配置的情况下,一个进程默认可以打开 32 或者 64 个文件,在修改的情况下,可以达到 10w 个,也就是文件描述符表的大小可以达到的大小

注意:

  • 每个进程都会有文件描述符表,并且子进程的创建也会拷贝父进程的文件描述符表,打开相应的文件

综上所述,这里画个图:

在这里插入图片描述
⭐实际上,Linux 在操作文件时,大部分是通过文件描述符表中的下标来进行操作的,而这个下标就是文件描述符 fd,这个很重要,比如 wrtiereadsend 等系统调用接口都是对 fd 进行操作的。

1.4 打开文件时

当进程打开一个文件的时候,在内核中,会创建这个文件对应的 struct file ,然后在文件描述符表中分配一个没有被使用下标 chosenIndex,然后让 fd_array[chosenIndex] 中填入这个 struct file 的地址,再将这个下标 chosenIndex 返回给用户

  • 分配原则:文件描述符表会从头开始遍历,找到一个最小,没被使用的文件描述符并返回。
  • 在 Linux 中,大部分对文件的操作,都是对文件描述符 fd 作操作

在这里插入图片描述

1.5 默认打开的三个文件

Linux 中所有的进程都会默认打开 3 个文件,分别是 stdin(标准输入),stdout(标准输出),stderr(标准错误),它们所占用的文件描述符分别是 0,1,2

一般情况下,标准输入就是键盘,标准输出和标准错误都是和显示器绑定,所以向标准输出和标准错误中输出,都会在显示屏上显示

标准输出一般是接收程序的正常打印结果,标准错误一般是接收程序的错误或者异常结果

这里可以试试,我直接往 1 号和 2 号文件描述符中写入数据:

在这里插入图片描述可以看出,向 1 号和 2 号打印的数据都往显示屏上打印了,

所以我们在程序中,打开文件获取到的文件描述符通常是从 3 开始

在这里插入图片描述

2. 重定向

2.1 瞅瞅

先了解什么是重定向:是一种改变输入源或者输出目标的方式,允许就输入或输出从默认的位置转移到其他位置。还是晦涩的话,这么理解就好了:本来应该写到 xxx 中,现在写到了 yyy 中

举例子:echo 是向显示屏中打印数据:

在这里插入图片描述
这直接打印到了屏幕上对吧

我们现在使用 > ,它在 Linux 命令行中是输出重定向(类似的>> 是追加重定向)
现在我将上面那段输出 重定向 到文件中:

在这里插入图片描述

可以看出本来是打印到显示屏上的,现在写到了 hello.c 文件中,这也就是重定向了

2.2 dup2

首先我们先了解关于重定向的一个接口 dup2

在这里插入图片描述
这个接口要求传入两个文件描述符,这个函数的作用简单来说可以这么理解:

  • fd_array[newfd] = fd_array[oldfd],就是将文件描述符表中,oldfd 对应的内容拷贝给 newfd 对应的位置。也就是会有两个文件描述符指向同一个文件

比如下面这个代码,我打开了一个文件,然后执行 dup2(fd, 1),那么文件描述符表发生了啥 o.0?

int main()
{int fd = open("hello.c", O_RDONLY);if (fd < 0)     return 0;dup2(fd, 1);return 0;
}

如下图,

  1. 打开文件的时候,在内核为这个文件创建 struct file 数据结构,然后为其分配文件描述符,并放到 fd_array 中管理,再返回文件描述符
  2. fd_array[1] 中的指针指向这个文件的 struct file

在这里插入图片描述
⭐关键点就是将 fd_array[newfd] 的指针改成成指定的 struct file

然后看看下面代码演示一下:重定向之后,原本写在 1 号文件(也就是显示屏)中的数据现在写到了文件 hello.c 文件中。

在这里插入图片描述其实到这里,重定向的实现原理就七七八八了,我们下面再梳理一下应该就晓得了

2.3 实现原理

  • 当执行 dup2(fd, 1) 的时候,将文件描述符 fd 的内容拷贝给 1 号,这时候 fd1 对应的指针都指向 hello.c
  • 文章开头说过,Linux 中大部分对文件的操作都是对文件描述符的操作
  • ⭐在这个例子中,write / read同样是对文件描述符作操作,通俗点,就是操作系统只会对 fd 下标操作,并不关心fd_array[fd]里面是谁
  • 所以当执行 write(1, str, strlen(str)) 的时候,操作系统就直接往 1 号下标对应的文件进行写入了,它并不关心 1 号下标的文件是不是stdout (强调)
  • 所以最终表现出来的情况就是「本该向显示屏中输出,现在输出到了 hello.c 文件中」

在这里插入图片描述

3. 一切皆文件

众所周知,电脑可以有很多外设,比如键盘,鼠标,声卡,音响,显示屏,网卡,磁盘…,而 Linux 怎么将这些看起来就离谱的硬件也抽象成文件的

这些外设,大都有自己的读操作和写操作,而 Linux 不应该专门为每个硬件设计相应的读写方法,甚至设计相应的数据结构,这样不便于维护和管理,而且很麻烦,如果统一起来就好了

  • 实际上在操作系统和硬件之间,还有一层软件层 —— 驱动,每个硬件通常都有配置一个特定的驱动,驱动可以和硬件交互控制,也可以将操作系统的指令翻译成硬件可以理解的指令 / 命令
  • 所以驱动可以理解成操作系统和硬件交互的桥梁
    它可以为操作系统提供统一的接口(比如读写接口,下文也以读写接口举例子),借此操作系统可以实现对硬件的间接控制,以及完成两者之间的交互
    在这里插入图片描述

还记得 struct file 结构体吗,前面说了这是一个操作系统内核中描述 / 管理文件的结构体,那么要怎么利用这个结构体来将底层硬件结合 / 统一起来?

可以在 struct file 中提供若干个函数指针,比如 ssize_t (*read)()ssize_t (*write)(),然后这些函数指针直接指向驱动层提供的读写接口,指针不必关心自己到底指向了哪个函数,对于 Linux 来说,它只知道只要调用 struct file 中的 write 函数,就一定会执行对应驱动提供的写操作接口,从而以管理文件的方式,实现从底层硬件的控制

在这里插入图片描述


http://www.ppmy.cn/news/262195.html

相关文章

2docker

一、引言 1.1 环境不一致 我本地运行没问题啊&#xff1a;由于环境不一致&#xff0c;导致相同的程序&#xff0c;运行结果却不一致。 1.2 隔离性 哪个哥们又写死循环了&#xff0c;怎么这么卡&#xff1a;在多用户的操作系统下&#xff0c;会因为其他用户的操作失误影响到你自…

管理项目-查询数据

人事管理项目-查询数据模块 后端实现配置文件实体类Dao层测试前端实现1&#xff0e;创建Dept页面2&#xff0e;修改路由3 测试 后端实现 配置文件 在application.yml文件中配置数据库连接、JPA及端口等信息&#xff0c;代码如下&#xff1a; 实体类 配置完成后建立和表结构…

【集群】LVS负载均衡群集

文章目录 前言一、企业群集应用概述1. 群集的含义1.1 群集的特点1.2 扩展服务器的方式 2. 群集的类型2.1 负载均衡群集&#xff08;Load Balance Cluster&#xff09;2.2 高可用群集&#xff08;High Availability Cluster&#xff09;2.3 高性能运算群集&#xff08;High Perf…

正态总体下常见的抽样分布

1.正态总体下常见的抽样分布 本人博客&#xff1a;总体分布、样本分布、抽样分布的区别 本人博客&#xff1a;三大抽样分布 正态总体下常见的抽样分布意思是&#xff1a;样本来自服从正态分布的总体中&#xff0c;从样本中抽样后得到的分布 1.1 单个正态总体下的抽样分布&…

《爱玛》配色分享

电影时长&#xff1a;124分钟 电影心得&#xff1a;高级莫兰迪中和了整体的庄重感&#xff0c;每一帧画面都像油画的质感&#xff0c;人物和景色的构图&#xff0c;色彩的冲撞都令人赏心悦目。

%d,%05d,%-5d,%.5d的区分

%d,%05d,%-5d,%.5d的区分 %d是普通的输出 %5d是将数字按宽度为5&#xff0c;采用右对齐方式输出&#xff0c;若数据位数不到5位&#xff0c;则左边补空格 %-5d就是左对齐 %05d&#xff0c;和%5d差不多&#xff0c;只不过左边补0 %.5d从执行效果来看&#xff0c;和%05d一样 #i…

吴恩达老师《机器学习》课后习题1之线性回归

在学习这些内容之前&#xff0c;需要学习python数据分析相关内容&#xff1a; numpy&#xff1a;科学计算库&#xff0c;处理多维数组&#xff0c;进行数据分析 pandas&#xff1a;基于numpy的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的 matplotlib&#xff1a…

智能车——三轮差速

在之前摄像头组都是用的4轮的车&#xff0c;我之前也是用四轮车练手的&#xff0c;后来比赛规则出来了&#xff0c;变成要用三轮车了&#xff0c;三轮的前轮就是个摆设&#xff0c;任何移动转弯都要靠后边的两个轮&#xff0c;所以差速就尤为重要了&#xff0c;之前在学校写了2…