当前位置: 首页 > news >正文

Linux 文件(2)

文章目录

  • 1. 文件描述符
    • 1.1 文件描述符是什么
    • 1.2 文件描述符如何分配
  • 2 重定向
    • 2.1 输出重定向
    • 2.2 输入重定向
    • 2.3 使用dup2进行重定向
  • 3. 文件、父子进程和进程替换

1. 文件描述符

1.1 文件描述符是什么

什么是文件描述符呢?

我们先来看之前所介绍的系统级别的文件操作函数open

在这里插入图片描述
在上图中,我们可以看到open有一个整型的返回值,而这个返回值,实际上就是文件描述符。

在Linux中,由进程打开文件,研究这些进程打开的文件,本质是研究文件与相应进程的关系。所以,在Linux中,会存在一个结构体用以描述文件:struct file。每一个被打开的文件,都会对应有一个struct file,这些struct file会被像双链表一样链接起来,就如task_struct一样。

而文件是由相应进程打开的,因此在描述进程的task_struct中,肯定要有记录其打开文件的变量,如下所示:

在这里插入图片描述
files这个指针指向一个files_struct的结构体,而在这个结构体中,存在一个指针数组,而这个指针数组中存放的就是一些struct file* 类型的变量,因此进程可以通过数组中存储的指针,找到其所打开的文件。

在这里插入图片描述

整体的关系可以如上图所示。

因此,文件描述符实质上就是上图中file* fd_array[]这个数组的下标。需要说明的是,操作系统识别进程打开的文件,是只通过这个文件描述符,即fd来识别的。

C语言中的FILE结构体用以描述文件,本质上是对struct file 的又一层封装,其中会存在文件描述符。

1.2 文件描述符如何分配

既然文件描述符就是数组的下标,那么文件描述符如何分派呢?

我们来看下面的测试程序:

在这里插入图片描述
上述程序的输出结果为:3
这有点奇怪,数组下标不都是从0开始的吗?这说明,0,1,2下标处,肯定对应的是别的文件。

实际上,一个进程启动时,会默认打开三个文件:标准输入stdin标准输出stdout标准错误 stderr。这三个文件,分别对应的就是数组下标0,1,2。

实际上,文件描述符是这样来分配的:返回从数组下标0开始,往后找到的第一个为空的数组下标处,即作为相应的文件描述符。

我们可以来测试一下,将标准输入文件关闭掉,然后再打开一个文件,此时这个文件的文件描述符应为0。

在这里插入图片描述
在这里插入图片描述
我们同样可以验证,进程启动时会默认打开stdin stdout stderr这三个文件,并且分配文件描述符0,1,2.

在这里插入图片描述

上述程序的输出结果为:

在这里插入图片描述
特别地,在上述代码中,我们拿到这三个文件的文件描述符是通过C语言中FILE这个结构体得到的,而在Linux中,文件描述的结构体是struct file,由此可见,C语言不仅对操作系统的接口做了封装,对操作系统的数据结构也会做封装。

2 重定向

什么是重定向呢?
正常情况下,我们输入是从标准输入中读取,而输出则是向标准输出中输出。重定向的核心就在于,使得输入不再从标准输入中读,输出不再向标准输出中输出。

2.1 输出重定向

我们先来看下面的示例:

在这里插入图片描述
我们来看上述程序的运行结果:

在这里插入图片描述
很奇怪,并没有显示出我们想要打印出的字符串。
printf函数默认是向标准输出中打印,实质上,我们前面讲过,操作系统层面,识别进程打开的文件,仅通过文件描述符实现。C语言中的printf本质是对系统调用write的封装,而write写入到哪里,正是通过文件描述符进行判定的。

因此,printf实质上是对该进程中,文件描述符为1的文件中写入,虽然我们先关闭了标准输出文件,但是新打开的文件,自动分配了文件描述符1,因此此时就会向这个新打开的文件中写入了。

我们查看一下log.txt中的结果,进行验证:

在这里插入图片描述

2.2 输入重定向

输入重定向与输出重定向是类似的。
C语言中的scanf默认是从标准输入中读取,实际上是从文件描述符为0的文件中读取。

我们通过下述代码,实现输入重定向:

在这里插入图片描述log.txt中的内容为hello linux,所以输入重定向读取一行字符串内容后,最终输出的结果也应为hello linux

最终输出结果如下所示:
在这里插入图片描述

2.3 使用dup2进行重定向

在这里插入图片描述

在这里插入图片描述

重点关注上述的dup2函数,这是一个可以实现重定向的系统调用。其原理是,让 newfd 变为oldfd 的拷贝。
比如说,我们要实现输出重定向,如果使用dup2系统调用,就不用先关掉标准输出文件,而是直接让原本存储标准输出文件的下标1处,变为存储我们要重定向到的文件。

以下,是使用dup2进行输出重定向和输入重定向的代码示例:

输出重定向:

在这里插入图片描述

输入重定向:

在这里插入图片描述

3. 文件、父子进程和进程替换

我们知道,进程打开文件,进程与文件之间是存在紧密联系的。

父进程创建子进程,子进程会继承父进程的代码和数据,子进程的task_struct也几乎是对父进程的拷贝,那么对于父进程打开的文件,子进程如何看待呢?

子进程会继承父进程打开的文件,即一个文件会对应多个进程,某文件在父进程中是打开的,那么这个文件在相应的子进程中也是打开的。
并且,在描述文件的结构体内部,存在一个引用计数,当一个文件对应多个进程时,引用计数为所对应的进程数,当一个进程关闭该文件时,引用计数便会减去1,直到引用计数为0时,该文件才会真正关闭。这也是进程具有独立性的一种体现——一个进程关闭某文件,并不会影响另一个进程对该文件的打开。

那么,进程替换中,会影响进程打开的文件吗?
答案是,不会的。进程替换,实质上并未新创建进程,而是对当前进程的代码和数据进行替换,主要更改的是进程地址空间、页表和物理内存这三者中相关映射,而进程task_struct中的其余内容并未有什么变化。
因此,某个进程在进程替换前有怎样的文件关系,在进程替换后,依然又怎样的文件关系,这是不会发生变化的。

相关文章:

  • 电子电路:什么是静态工作点Q点?
  • 【QT】QT6添加现有.c .h文件
  • QT之绘图模块和双缓冲技术
  • CVE-2015-4553 Dedecms远程写文件
  • 光子神经网络加速器编程范式研究:光子矩阵乘法的误差传播模型构建
  • 力扣HOT100之二叉树:199. 二叉树的右视图
  • Fabric初体验(踩坑笔记)
  • 【盈达科技】AICC™系统:重新定义生成式AI时代的内容竞争力
  • 晶圆Map图芯片选择显示示例
  • 在Cursor中启用WebStorm/IntelliJ风格快捷键
  • v解锁健康密码:现代养生新主张
  • Scala:size 和 length 的区别
  • 什么是子网委派?
  • 计算机网络 第三章:运输层(一)
  • 健康生活指南:从日常细节开启养生之旅
  • 并发编程(5)
  • JAVA请求vllm的api服务报错Unsupported upgrade request、 Invalid HTTP request received.
  • CAN总线采样点不一致的危害
  • chrome因使用selenium无图模式导致不再加载图片问题解决
  • 【Java开发--对象converter转换规范实践】
  • 《歌手2025》能否“爆”下去?
  • 夜读丨为萤火虫哭泣的夜晚
  • 红星控股重整期间实控人被留置后续:重整草案不会修改,涉车建兴职责已调整
  • 马上评|房屋“注胶堵漏”骗局何以屡屡得逞
  • 永久基本农田竟沦为垃圾场,湖南湘潭回应:全面启动专项整治
  • 2025吉林市马拉松开跑,用赛道绘制“博物馆之城”动感地图