一切皆文件的由来
"一切皆文件"是Unix
和类Unix
(类似Linux
)的一种设计哲学;
这种思想最早来自于Unix
操作系统的设计者之一, 肯·汤普森(Ken Thompson) 和 丹尼斯·里奇(Dennis Ritchie);
该文章中以Linux
为例;
在Linux
当中,并不只是传统的文件,许多其他的类似设备,进程等等也都被当做文件来进行处理;
这种设计哲学带来的好处就是,由于大部分操作都可以通过读写文件来完成,故程序员可以使用同样的接口来处理各种情况从而简化编程工作;
硬件设备与操作系统的服务接口
硬件设备的设计初衷是为了为用户与操作系统OS
提供服务;
为了实现这个目标,硬件设备必须提供一种方法,而这种方法也就是接口;
让用户和操作系统都可以与他们进行交互;
例如键盘,显示器,磁盘,网卡等硬件设备都提供这样的接口;
用户可以通过键盘输入数据,通过显示器查看信息,通过磁盘存储和读取数据,通过网卡进行网络通信;
操作系统也将通过这些接口管理这些硬件设备;
但是这里举例的并不包括内存与CPU
,虽然内存和CPU
也为上层提供了接口,但是内存和CPU
作为OS
的核心组件,通常是由操作系统直接调用和管理的,而不是类似于键盘或者显示器这样的接口;
操作系统会直接管理内存的分配和回收;
也会控制CPU
的任务调度,这是因为内存与CPU
是计算机体系中的核心资源;
对应的他们的工作效率将会影响到整个系统的性能,故需要有操作系统直接进行精细的管理;
对于内存而言,操作系统一般通过内存管理单元MMU
来控制内存的访问,分配和保护;
而对于CPU
而言,操作系统将通过终端,系统调用或者其他机制来调度进程和线程的执行;
即使这些 “接口” 与上文当中的类似于显示器,键盘等设备的接口概念有所不同;
Linux中的硬件识别机制
那么可以思考一个问题:
- 硬件是如何被识别到的?
实际上硬件的识别主要到两个重要的组件: 设备驱动程序 , udev系统 ;
那么什么是 udev系统 ?
在Linux
当中的/dev
目录下可以看到存在许多文件;
而这些文件被称为设备文件;
同时这些设备文件代表着系统当中的各种硬件设备,它们将为用户的程序提供一个接口;
用户可以通过这些口从而间接的调用硬件;
$ ls /dev
AliSecGuard initctl
autofs input
block kmsg
btrfs-control log
bus loop-control
char mapper
console mcelog
而对应的 udev系统 是Linux
当中的一个动态设备管理器;
其主要功能就是管理这些设备节点;
在上文的图中可以看到,硬件在设计时不仅为上层提供了操作接口,同时也提供了一个硬件识别码;
当操作系统内核识别到硬件识别码时将对应的给udev系统发送信号,这个信号对应的是一个设备时间,这个时间中包含了设备的相关信息;
并去在已有的驱动程序当中匹配适合的驱动程序;
当 udev系统 会根据接收到的事件信息,对应的在/dev
目录下创建对应的设备节点;
当设备从系统中移除时, udev系统 会删除对应的设备节点;
而若是系统内核并未匹配到这个设备对应的硬件时,则表示可能在已有的设备当中并不存在适合匹配该设备的驱动程序,该硬件则可能不能被使用;
若是发生这种情况,用户需要手动的在开发者处获取对应的驱动程序并手动为系统安装该驱动程序;
硬件设备的操作流程
- 用户是如何对硬件进行交互?
如该问题所问,那么用户是如何对硬件进行交互的?
这个问题的回答实际上可以类比进程当中的进程地址空间;
在用户层对物理内存进行访问时,对应的系统将为进程创建对应的PCB
结构体对其进行管理;
而当用户需要对物理内存进行访问时不能对物理内存直接进行访问;
而是需要对应的去访问虚拟地址空间,虚拟地址空间通过页表进行映射从而间接访问物理内存;
对于与硬件的交互也是类似;
用户无法直接与硬件进行交互,而是通过其提供的各种接口间接进行访问;
具体流程如该图所示;
用户空间程序
用户控件是操作系统外的一个层级,一般是普通应用程序执行的地方;
设备文件
在
/dev
目录当中的特殊文件,代表系统中的硬件设备,这些文件为用户空间的程序提供了一个接口,用户可以通过这些接口程序给驱动程序发送请求,从而间接的调用硬件;驱动程序
驱动程序是系统内核的一部分,是一种较为特殊的软件,负责管理和控制特定的硬件设备,当用户空间的程序通过设备文件发起请求时,相应的驱动程序会接收到这个请求并执行必要的操作以与硬件设备进行交互;
硬件设备
实际的物理设备;
这里需要注意的是驱动程序负责管理和控制硬件,而驱动程序本身也是被OS
进行管理的;
设备文件本身也是为用户提供一个与用户与驱动交互的接口;
而驱动程序则为为设备文件提供的一个与硬件交互的一个接口;
故在操作系统学当中存在 一切皆文件 的哲学思想;
这些抽象的接口本身对于OS
来说是不知情的;
OS
只知道在调用对应的硬件时只需要去调用对应的设备文件即可;
这也使得两者形成了解耦合;
而对于设备文件而言,设备文件是当操作系统内核识别到硬件对应的硬件码时发送对应的信号给 udev系统 使其动态生成的;
设备文件根据硬件的种类分为不同一般分为两种:
字符设备文件
这类设备文件用于表示以字符为单位进行数据传输的设备,如键盘鼠标和各种串口设备等;
字符设备文件支持顺序访问;
块设备文件
这类设备文件用于表示那些以数据块为单位进行数据传输的设备,例如硬盘和光驱等;
块设备文件支持随机访问;
对于硬件层来说,硬件层本身并不提供读写方法,而是通过电子接口与通信协议与外界进行交互
这些不同的层级当中,若是需要访问硬件则需要以上述的流程逐级进行访问;
当一个进程需要去调用一个硬件设备时,对应的需要向操作系统内核发送请求;
当操作系统接收到请求时首先将会去访问该进程对应的PCB
结构体,也就是对应的task_struct
结构体,从而获取结构体当中的信息;
这些信息包括进程的PID
,读写权限等等;
当操作系统接收到请求时将会对应的让 udev系统 为需要打开的硬件创建一个对应的设备文件;
而这个设备文件则是作为用户空间程序与硬件驱动程序的一座桥梁,同时设备文件也在OS
当中代表一个硬件设备;
届时,用户空间程序将打开对应的设备文件(采用open
方式打开),并为该打开的设备文件创建一个对应的file
结构体,并且将其交给操作系统进行管理,具体的管理细节如下:
而最终这些程序将通过设备文件转化为对对应的驱动程序操作,最终转化为对物理硬件的操作;
在上文中提到,设备文件是检测到硬件设备后动态创建的;
而在Linux当中,当检测到设备并为其创建设备文件时,这些设备未见往往是具有同样的读写接口从而方便设备文件与驱动程序之间的交互;
而正因为是动态创建的,故对应的接口往往是一致的;
当用户层空间程序通过设备文件对硬件进行操作时,对应的将经过驱动程序;
而驱动程序将根据设备的特性进行条件编译,当遇到不合适的操作时将为上层(设备文件)返回错误;
设备文件接收到错误时,可能将继续返回上层并告诉其操作并不支持并记录到错误日志当中或是不做任何处理;
实际上这也完成了一个 [硬件设备 驱动程序] , [用户层 操作系统] , [操作系统 设备文件] 之间的解耦合;
同时还需要注意:
设备文件是当
OS
检测到对应的硬件时动态创建的;且硬件当中也将会出现不兼容的问题,不兼容的问题原因有多重;
一方面可能是硬件未提供标准的识别码使得
OS
无法识别设备;同时,即使设备识别,但是如果没有合适的驱动程序来实现与硬件的通信协议与接口,该设备也无法再
OS
当中被成功调用;
虚拟文件系统(VFS)
在上文当中提到了部分关于用户空间程序是如何间接去调用硬件的;
而这部分的内容则被称为 虚拟文件系统;
用户与空间与内核空间
当用户空间的程序需要与硬件设备进行交互时,它会通过系统调用切换到内核空间;
因为用户空间的应用程序不能直接与硬件进行通信,当他们需要访问硬件时,他们通过系统调用请求内核代表他们执行这些操作;
获取进程信息
操作系统会查看对应的
task_struct
结构体,以获取该进程的详细信息,如PID,权限等;打开设备文件
操作系统会打开请求的设备文件;这个设备文件代表了一个硬件设备,并通过设备驱动程序与该硬件设备进行交互;
文件描述符
操作系统会为新打开的设备文件在进程的文件描述符表中(通常存储在
files_struct
结构体中)分配一个新的文件描述符;文件操作表
设备文件关联的
file
结构体中包含一个指向设备驱动程序的函数指针,这些函数构成了一个文件操作表file_operations
;这个表提供了设备驱动程序的操作接口,如读、写、控制等;设备驱动程序
操作系统通过
file_operations
表中的函数指针调用设备驱动程序的操作方法,从而实现与硬件设备的交互;当程序执行如读取、写入等操作时,实际上是通过
file_operations
表调用相应的设备驱动函数,由驱动程序与硬件设备进行直接通信;关闭文件描述符
操作完成后,必须通过
close
系统调用关闭文件描述符,这样可以释放资源并减少对设备的锁定;设备管理和抽象
udev系统 或类似的设备管理器负责在硬件连接或断开时动态创建和删除设备文件,这为设备的热插拔提供了支持;VFS为不同的文件系统和硬件设备提供了统一的接口,简化了应用程序的开发;
这个过程确保了操作系统的稳定性和安全性,因为所有的硬件访问都是由内核来进行的,用户空间的程序无法直接访问硬件;同时,通过设备文件和文件操作表的抽象,使得不同的硬件设备能以一致的接口暴露给用户空间的程序,大大提高了程序的可移植性;
而这一系统被称为虚拟文件系统(VFS);
而实际上这种虚拟文件系统可以看做是具有多态性质的;
VFS
提供了一种统一的接口来处理各种不同类型的文件系统和设备文件;
VFS
定义了一组通用的文件操作接口,如open
,read
,write
等等,这些接口在不同的文件系统和设备驱动当中可能有着不同的实现;
但当一个操作被调用时,VFS会根据文件的类型调用相应的实现,因此这可以被看做是一种多态的行为;
但需要注意的是,虽然这种行为在某种程度上类似于面向对象当中的多台,但Linux内核并不是利用面向对象的方式编写,故这并不是真正意义上的多态;