【文件fd】深入理解和实现Linux底下一切皆文件 | 系统和语言文件操作二者关系_封装 | 系统调用为什么&怎样封装成库函数

目录

1.系统调用的打开/读/写文件操作

2.如何理解Linux底下一切皆文件

2.1设备属性

2.2设备的操作方法 

3.如何实现Linus底下一切皆文件

4.源码查看

5.系统和语言文件操作二者关系 

5.1 flags选项和C语言的"w""a"方式 二者的关系

5.2 系统的文件描述符fd和语言的FILE类型 二者的关系

5.3 封装

5.3.1 为什么要封装

5.3.2 怎样封装

6.联系&拓展


1.系统调用的打开/读/写文件操作

  • man 2 read
  • man 2 write
  • 读read的本质:是将内核级缓存的内容 拷贝到 用户级缓存区。

  • 若内核级缓存没有内容呢❓

  1. 如果没有数据,把当前进程放到磁盘设备的等待队列里面。
  2. 磁盘开始加载数据到内核级缓存中。
  3. 加载完毕,把进程唤醒,放入运行队列。
  • 写/修改write的本质:是将用户级缓存区的内容 拷贝到 内核级缓存当中,由OS定时刷新到外设。

  • 若原来内核级缓存区有内容呢❓

  • 即便内核级缓存有内容,需要再写入和修改,OS都必须先把内容从磁盘读到内存缓存中

  • write、read函数本质是拷贝函数

综上所述:无论读写操作,都必须在合适的时候,让OS把文件的内容读到文件缓冲区。

所以,对文件的修改是内核级的修改,不是磁盘级别。无论是全部修改完,只修改一个字节,都必须先加载到内核级缓存,在内存中修改完,由OS定期刷新到外设。


  • man 2 open
  • open是在干什么❓数据什么时候被刷新到外设(不管,OS安排)❓

  1. 创建文件对象struct file

  2. 开辟文件缓冲区的空间,加载文件数据(延后)

  3. 查询进程所对应的文件描述符表

  4. 文件对象struct file的地址,填入对应的文件描述符表中(下标对应)

  5. 返回文件描述符表的下标☞文件描述符

此刻就完成了,文件的打开工作。所以在应用层/用户层 读写数据,就是把应用层的数据和内核级的数据的相互交换/拷贝。

2.如何理解Linux底下一切皆文件

  • 磁盘中的文件,被打开的文件,OS会在内核中创建对应的文件对象struct file,给每个文件对象分配的数组下标(文件描述符),是从3开始。3456.....
  • OS默认打开设备文件,设备文件的文件描述符是0/1/2,底层是硬件,和磁盘上的普通文件不是一回事。如何理解呢硬件是文件❓
  • 这些硬件被打开,也必须在内核中创建对应的struct file,才能被归类到文件描述符表中,理解硬件<->文件的问题❓本质是:Linux底下一切皆文件。
  • 什么叫一切皆文件呢❓硬件也是文件。但是很明显,键盘/显示器/鼠标等是设备,不是文件。
  • 站在Linux系统角度,把硬件当作文件。这是怎么做到的❓
  • 0:标准输入,键盘。
  • 1:标准输出,显示器。
  • 2:标准错误,显示器。
  • 冯诺依曼体系中,所有的设备,叫做外设,IO设备。
  • 设备=设备的属性+设备的操作方法(重点先放在操作方法上)
  • OS是硬件的管理者,所以OS对每一种设备做管理。(先描述在组织)
  • 每一种设备在OS内核中都要存在对应的PCB☞struct device

2.1设备属性

  • 设备文件struct file☞有文件的属性和指针☞指向设备的属性☞struct device
  • struct device中的设备属性/类别都是一样(设备名/设备厂家等等),只是属性值是不一样的。
  • struct file☞文件相关属性+设备文件相关属性struct device

 

2.2设备的操作方法 

  • 每种设备都有设备各自的操作方法
  • 注❗键盘没有写方法write。显示器没有读方法read。可以将设备没有的方法设置为NULL即可。
  • 不同的设备的操作方法一定是不一样的❗
  • 设备的操作方法,都是由驱动层来完成的。
  • 驱动层:工程师来完成的,键盘工程师/显示器工程师等。
  • 每种工程师要写出来访问外设的操作方法☞函数 void k_read() 
  • 设备文件对象struct file里面还会包含设备操作方法的函数指针void (*read)(......) 函数 void k_read() 

站在OS角度,每一个被打开的设备,都必须在OS内核中创建一个设备对应的设备文件对象struct file。(在打开这个设备的时候,就必须创建struct file)

  • 属性角度:struct file里面包含一些指针☞指向设备文件相关的属性以及文件相关属性。
  • 操作方法角度:struct file里面包含函数指针☞指向外设的操作方法(函数)
  • 用户就不需要再考虑硬件的底层的差异。因为全抽象成 设备文件struct file结构体对象中☞struct file包含设备操作方法的函数指针void (*read)(......) 
  • 虽然底层设备的操作方法(函数实现)一定都是不一样的。
  • 同样不同的设备的不同操作方法(函数实现)可以抽象成一样的 返回值和参数
  • 实际上抽象struct file里面的函数指针(读方法/写方法等等)
  • 打开设备文件的本质:是创建了struct file后,将其中的函数指针指向这个设备的底层操作方法的函数。

综上所述:访问一个外设,只要找到这个设备的文件,直接调用函数指针指向的函数(操作方法),既可以访问设备了。同时也获取了设备文件的属性和文件的相关属性。

  • 所以,struct file可以是键盘设备文件/显示设备文件等等,也有存在内核级缓存,写和读就是内核级缓存和键盘/显示器硬件交互。(图)

  • 所以,每一个被打开的文件除了拥有内核级缓存,还需要一张操作底层方法的指针表。根本不需要知道这个struct file文件代表什么。它底层都会指向各自的属性和操作方法的。(图)

3.如何实现Linus底下一切皆文件

  • Linux底下一切皆文件的技术被称为:多态
  • struct file可以找到不同设备的属性和操作方法。(用C语言来实现类)
  • 类=属性+方法
  • 用C语言实现类:C语言结构体是只能放属性不能放方法,但是可以放函数指针(相当于放方法☞指向函数)。所以属性+函数指针(指向方法)
  • C语言实现的多态技术❗(图)
  • 虚拟文件系统:在Linux/软件工程领域 的问题 都可以直接或间接添加软件层来解决。图

4.源码查看

【设备文件的操作方法函数指针】

【设备文件的操作方法函数】

5.系统和语言文件操作二者关系 

5.1 flags选项和C语言的"w""a"方式 二者的关系

  • 在C语言中,使用的fopen/fclose等接口是库函数。

  • 语言层面上学习文件时,对应的库函数接口,底层都是系统调用。只不过做了封装。

  • 所有的C语言上的文件操作函数,本质底层都是对系统调用的封装 (图)

5.2 系统的文件描述符fd和语言的FILE类型 二者的关系

  • 进程打开文件☞进程维护的表:文件描述符表☞下标(文件描述符)☞ struct file
  • 在OS内,系统在访问文件的时候,只认文件描述符fd❗
  • 如何理解C语言通过FILE*访问文件呢❓

  • C语言编程语言:没有文件描述符fd,在访问文件使用的都是文件指针FILE*
  • FlLE是一个C语言提供的结构体类型。(课件最后)

  • FILE在语言层面上是一个结构体。在系统层面上OS只认识文件描述符fd。

  • 所以,FILE结构里面内部一定封装了文件描述符fd。文件流必须封装了文件描述符fd

证明FILE结构里面内部一定封装了文件描述符fd

  • man 3 fwrite
  • 第一个参数:缓冲区/字符串

  • 第二个参数: 基本单位的大小

  • 第三个参数: 写几个基本单位

  • 第四个参数:往哪个文件流中写

  • 返回值:实际写了多少个基本单元。

  • stdin / stdout / stderr 和 0/1/2

综上所述:语言层面上学习文件时,对应的接口,底层都是系统调用。只不过做了封装

在类型上。系统底层访问文件只能用文件描述符,语言上就用结构体封装了文件描述符。图

 【stdin / stdout / stderr 和 0/1/2】

5.3 封装

5.3.1 为什么要封装

  • 写代码,推荐使用语言提供的文件操作,不推荐系统调用(除非非用不可)。
  • Linux/MAC/windows操作系统一定是不一样的,体现在方方面面,系统调用接口也都不一样。
  • 如果在Linux上使用Linux系统调用接口写的代码,不一样能在其他操作系统上编译通过使用。
  • 系统调用不具有跨平台性
  • C/C++编程语言是具有可移植性,跨平台性的。
  • C/C++编程语言写的代码,无论是在Linux下还是其他系统下面都可以编译通过使用。 
  • 为什么要封装❓
  • 所以语言都想要具有跨平台性,所有语言要对不同的平台的系统调用进行封装(图)

5.3.2 怎样封装

  • 语言为什么具体跨平台性❓语言怎么做到具体跨平台性❓图
  • 编程语言的源代码中的标准库☞库的设计者
  • 在条件编译上 设计☞不同系统的条件下各自编译一份☞使用哪套系统就下载哪套编译完成的库即可
  • 同一语言☞针对不同的平台☞具有不同的标准库☞根据不同平台系统的需求下载不同的库
  • 库的底层☞不同平台系统 实现 是不同的☞但是语言会把他们封装成相同的。
  • 编程语言的标准库在不同系统的底层实现上是不一样的,但是经过编程语言的封装所使用的库函数接口是一样的。所以使用哪个平台,就安装此平台的语言的标准库。所以编程语言是具有跨平台性的。

6.联系&拓展

  • 打开文件就是打开一个设备(显示器),其实是终端。
  • 一个进程在运行时,默认会打开三个输入输出流。
  • /proc目录下是当前正在进程的pid
  • /proc/pid 目录下pid的进程的所启用文件的文件描述符/可执行程序/当前工作路径等等
  • 文件描述符☞终端(键盘/显示器)
  • /dev/pts/4就是的终端文件。进程把/dev/pts/4设备文件打开写入,相当于往终端写入。
  • 每次登录云服务器,shell是第一个进程,它就把0/1/2打开了。后面每打开一个终端,就会把shell创建的/dev/pts文件再次打开,增加新的终端对应的设备文件。
  • 终端不是显示器,显示器只有一个。但是可以有很多终端。


  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐唐思

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值