什么是文件标识符?

最浅来看 文件标识符就是我们调用系统接口 open 打开文件时,open函数的返回值。

但要深入理解它,我们得知道:

1.文件打开的本质什么,操作系统如何管理被打开的文件?

2.打开的文件与打开它的进程如何建立联系,也就是进程如何找到要操作的文件的?

文件的打开与管理

先来回答第一个问题。我们假设一个场景:当我们用open打开当前路径一个文件名为test.txt的文件,然后对它进行读写时,当前进程是直接与磁盘交互吗?当然不可能,那样效率太低,又不能保证安全性。但是文件本来就是在磁盘上啊,那进程是怎么完成读写任务的呢?

答案就在操作系统 和内存 ! 当我们用open打开文件时,操作系统会先把磁盘上的文件加载到内存,以结构体的方式描述它,再以类似链表的结构统一管理所有被各种进程打开的文件。

我们进程对文件的读写就是对内存中描述文件的struct读写!再由操作系统通过驱动程序完成最终的写入磁盘。要清楚进程对文件的操作都是内存级的!是只限于内存范围内的!

被打开的文件

进程与被打开的文件连接

了解被打开的文件是被内核统一组织管理的之后,就来到了第二个问题。那进程是如何访问到它让操作系统开的文件struct呢?

我们要明白两点:

1.因为文件是以结构体的方式描述后被加载到内存 对文件操作就是对结构体操作 ->拿到这个结构体的指针?

2.进程与打开的文件可以是一对多的(同一个进程可以打开多个文件)->指针数组?文件描述符总是0 1 2 3...是不是很可疑 很像下标?

基于以上两点,基本上就可以推测出进程如何找到打开的文件。

那内核中究竟是怎么做的呢?

进程与文件

在每个进程PCB中都存有,专门为它管理打开文件的结构体指针,这个结构体内有一个指针数组,进程可以通过数组下标拿到文件结构体指针,从而对文件进行读写。

而这个数组下标就是文件描述符!内核是通过指针数组的方式与文件建立映射关系的。

文件描述符的分配规则

当打开新的文件时,会遍历指针数组,找到第一个空位置,存入文件struct的地址后返回下标

如下代码可以验证:

  #include <stdio.h>  #include<sys/stat.h>  #include <string.h>  #include <unistd.h>#include <sys/types.h>#include <fcntl.h>int main(){umask(0);int fd1 = open("test1",O_CREAT|O_TRUNC|O_WRONLY,0666);printf("fd1: %d\n",fd1);int fd2 = open("test1",O_CREAT|O_TRUNC|O_WRONLY,0666);printf("fd2: %d\n",fd2);close(fd1);printf("fd1: closed\n");int fd3 = open("test1",O_CREAT|O_TRUNC|O_WRONLY,0666);printf("fd3: %d\n",fd3);int fd4 = open("test1",O_CREAT|O_TRUNC|O_WRONLY,0666);printf("fd4:%d\n",fd4);return 0;                                                                                                                                 }      

文件标识符与上层封装

要知道 文件标识符是进程对文件操作的唯一凭证,所以常见的语言对文件的操作,如C 中struct FILE ,C++中的流对象等... 一定也是通过对文件标识符 和系统调用做封装得来的, 这是毋庸置疑的。

默认打开的文件

不难观察到上面代码中 fd1是从3开始的,说明在我们打开第一个文件之前已经有三个文件被自动打开了,他们的结构体地址分别存储在对应下标为0,1,2的位置。

它们分别是:

0 标准输入 1 标准输出 2 标准错误 

这里要引入一个"一切皆文件的概念",这个概念下文会详细讲,这里先简单理解,在Linux进程的视角下,所有对外设的输入输出 都会被当成文件来操作。

从标准输入(标识符为0的文件)读入就是从键盘读入,c语言常用的scanf就是对它做的封装

往标准输出和标准错误 写入 就是在屏幕上打印,printf,cout底层也是这么做的。

  int main(){  umask(0);  write(1,"linux so easy!\n",15);  return 0;  }

运行结果往终端输出

如何理解一切皆文件

为什么往磁盘文件写入能存储在磁盘上往标准输出写入怎么就打印到屏幕上了?这是如何做到的?不是都调用的同一个write接口吗?

操作系统在将文件加载到内存时,

给不同的文件创建同样的结构体类型,但是里面保存不同的方法!

不同外设在上层视角下变成了相同的 文件struct,只需要以相同的方式(如调用write)进行操作!这就是“一切皆文件的理解”

重定向的理解

基于以上包括“一切皆文件”认识,重定向的本质就很好理解了。

输出重定向:把文件标识符1对应的地址覆盖为自己指定文件,进程还是向1里写入,但原本向屏幕打印的内容就被写入到指定文件了。

输入重定向:把文件标识符0对应的地址覆盖为自己指定文件,进程还是从0里读入,但原本从键盘读入就变成从指定文件里读入了。

追加重定向类似,只是打开文件时以append方式打开。

如何实现:这里介绍一个系统调用 : dup2(int oldfd , int newfd)

意思是把newfd 数组对应的文件地址覆盖为oldfd下标处的。使得它们映射同一个文件。

总结

由此再来从头梳理一遍,分析如下代码是如何从调用open函数,文件被打开,到与进程建立连接,再到调用write写入后 关闭的:

 #include<stdio.h>                                                                                                                                                #include<sys/stat.h>  #include<sys/types.h>  #include<fcntl.h>  #include<unistd.h>  #include<string.h>  int main(){  umask(0);  int fd  =open("test.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);  const char*buffer="linux so easy!!\n";  write(fd,buffer,strlen(buffer));  close(fd);  return 0;  }

1.调用open函数打开文件

此函数为Linux的系统调用,操作系统会先在数据结构(类似于链表)中查找文件是否已经被打开,若没有,会创建结构体,把文件的部分内容和属性以及操作方法储存在结构体内,加载到内存,并与其他被打开的文件统一管理起来。

2.文件与进程建立映射关系

确保文件的结构体被加载到内存以后,操作系统会找到进程PCB中管理打开文件的struct中存储文件struct指针的数组,从头遍历空位,将打开文件的结构体指针存入空位,并返回下标(文件描述符)。

3.调用write接口

通过参入传递的文件描述符,找到对应的文件内核结构体,通过结构体内保存的写入方法,调用它完成写入。

4.调用close接口

将文件描述符对应的位置置为空,等待下一个打开的文件结构体地址填入。

进程不用考虑文件结构体的释放,那是操作系统干的,一个文件可能被多个进程打开,一个进程也可能打开多个文件,进程只用管理自己打开了的文件。而操作系统有引用计数的方式来决定是否释放内存文件结构体。

end~希望大家能有收获,欢迎留言讨论。

Linux下一切皆文件? -- 理解文件标识符fd相关推荐

  1. linux一切对象皆文件,为什么说Linux下“一切皆文件”?

    前言:接触Linux的同志们都听过一句话:"Linux下一切皆文件"."一切皆是文件"是 Unix/Linux 的基本哲学之一,那么为什么Linux在一切皆文件 ...

  2. 【已阅】Linux下一切皆文件与指令的本质(可执行程序),which指令等

    Linux下一切皆文件 在Linux下的话,一切皆文件.主要是看待诸如软硬件设备与磁盘文件的看法:一切皆文件,比如说显示器它也是文件,键盘也是文件,普通文件肯定是文件. 首先就是显示器这个东西,它其实 ...

  3. linux里c库和gnu c库,Linux下的C的库文件和头文件有什么区别-

    Linux下的C的库文件和头文件有什么区别- (2012-04-10 01:16:57) 标签: linux 杂谈 Linux下的C的库文件和头文件有什么区别?刚刚接触 这里有点没搞清楚 如果按我的理 ...

  4. Linux下使用exec命令将文件与文件描述符关联

    在shell脚本下exec为系统自带的脚本命令,其作用是用来执行其他程序: 语法格式: exec (选项)(参数) 选项: -c:在当前环境下执行命令,即在当前shell脚本下执行,而不是打开一个新的 ...

  5. Linux下如何生成core dump 文件(解决segment fault段错误的问题)

    Linux下如何生成core dump 文件(解决segment fault段错误的问题) 参考文章: (1)Linux下如何生成core dump 文件(解决segment fault段错误的问题) ...

  6. linux中rm删除的文件是否可以恢复,Linux下用rm删除的文件的恢复方法

    Linux下用rm删除的文件的恢复方法_Linux教程_Linux公社-Linux系统门户网站 https://www.linuxidc.com/Linux/2008-08/14744.htm lin ...

  7. linux mysql 数据文件,Linux下修改MySQL数据库数据文件路径的步骤

    使用rpm安装方式安装完MySQL数据库后,数据文件的默认路径为/var/lib/mysql,然而根目录并不适合用于存储数据文件. 原路径:/var/lib/mysql 目标路径:/home/mysq ...

  8. 搜索linux中大于m文件,linux 下查找大于100M的文件(转)

    命令行如下 find . -type f -size +1000000k Linux系统下查找大文件或目录的技巧 当硬盘空间不够时,我们就很关心哪些目录或文件比较大,看看能否干掉一些了,怎么才能知道呢 ...

  9. linux下执行mysql的sql文件

    linux下执行mysql的sql文件 mysql -uroot -proot 进入到mysql 然后执行source /var/ftp/pub/sogoodsoft.sql; 即可. www.2ct ...

  10. linux不同机器之间的拷贝,Linux下不同机器之间的文件拷贝

    通过 scp 命令实现不同机器之间的文件拷贝. (1)本机考到目标机器:scp 本机文件 目的地: (2)其他机器考到本机:scp 其他机器上的文件 本机路径 20160701补充:通过scp与win ...

最新文章

  1. php mvc实例下载,php实现简单的MVC框架实例
  2. MySQL—数据库表的完整性约束(非外键约束)
  3. 三丰三坐标编程基本步骤_数控车床编程,经典实例教程
  4. WT32-SC01是ESP32驱动3.5彩屏开发板方案适合用arduino方式开发吗?因为需要彩屏和电容触摸的驱动的
  5. LINQ to Tree - A Generic Technique for Querying Tree-like Structures,包含遍历WPF VisualTree
  6. php mysql购物车实现原理_PHP实现购物车的思路和源码分析
  7. no connection could be made because the target machine actively refused it.问题解决
  8. linux循环控制结构,Linux Shell 之 Shell 基本控制结构(二)(循环结构)
  9. java根据url下载文件
  10. marqueeview更改字体颜色_安卓手机上可以编辑字体的便签软件哪个好?
  11. xshell官网链接打不开了
  12. abaqus的python安装文件在哪_Abaqus2016安装教程【附软件下载地址】
  13. 微信小程序picker-view中的view的高度修改问题,只能用px单位
  14. 十载寒冰,难凉热血——2020CSDN年度博客之星Top10心路历程
  15. 利用C++,设置输入某年某月某日,判断这一天是这一年的第几天。
  16. clang-format的使用
  17. Day6——yaml简介
  18. systemd服务详解
  19. “程序员”眼中的中秋节
  20. 超声波测距传感器模块在畜牧业的应用

热门文章

  1. SCORM标准课件中SCO的制作方法
  2. Dijkstra算法入门
  3. 用游戏设计的方式教小朋友学习珠心算
  4. 关于笨蛋式病毒创作(CMD式)C++
  5. unity 一个UI和模型的需求,要求模型位于两个ui之间
  6. 用arduino写一个人体感应灯的代码
  7. python人工智能入门优达视频_看优达学城python入门视频学习C++
  8. centos7 asterisk11.7 realtime mysql配置
  9. 高等数学A(下)小整理——级数
  10. 11月11日大师献给各位染成茜色的坂道FANS的礼物—— あかね色に染まる坂是怎么破解的?