Linux 如何区分真实的文件和不存在的(例如:设备)文件?

Linux 如何区分真实的文件和不存在的(例如:设备)文件?

这是一个相当低级的问题,我知道这可能不是最好的提问地点。但是,它似乎比任何其他 SE 网站都更合适,所以就在这里。

我知道在 Linux 文件系统上,有些文件实际上存在,例如:/usr/bin/bash是存在的。然而,(据我了解),有些实际上并不存在,而且更多虚拟的文件,例如:/dev/sda/proc/cpuinfo等。我的问题是(它们是两个,但关系太密切,无法成为单独的问题):

  • 当发出读取命令(或类似命令)时,Linux 内核如何判断这些文件是否真实(从而从磁盘读取它们)?
  • 如果文件不是真实的:例如,从 读取/dev/random将返回随机数据,从 读取/dev/null将返回EOF。它如何确定从这个虚拟文件中读取什么数据(以及当数据也写入虚拟文件时该怎么做) - 是否存在某种映射,其中的指针指向适合每个文件或甚至虚拟目录本身的单独读/写命令?因此,的条目/dev/null可以简单地返回一个EOF

答案1

所以这里基本上有两种不同类型的东西:

  1. 普通文件系统,以熟悉的方式(包括软链接、硬链接等)将文件保存在包含数据和元数据的目录中。它们通常(但并非总是)由用于持久存储的块设备支持(tmpfs 仅存在于 RAM 中,但在其他方面与普通文件系统相同)。这些的语义是熟悉的;读取、写入、重命名等等,一切都按照您期望的方式进行。
  2. 各种虚拟文件系统。/proc和是此处的示例,FUSE 自定义文件系统(如或 )/sys也是如此。其中有更多的多样性,因为实际上它们只是指具有某种意义上“自定义”语义的文件系统。因此,当您从 下的文件中读取数据时,您实际上并没有像在普通文件系统下那样访问由其他先前写入的内容存储的特定数据。您本质上是在进行内核调用,请求一些即时生成的信息。这段代码可以做任何它想做的事情,因为它只是某个地方实现语义的函数。因此,您会遇到 下的文件的奇怪行为,例如假装是符号链接,而实际上它们不是。sshfsifuse/procread/proc

关键是/dev实际上通常是第一种。在现代发行版中,/dev类似于 tmpfs 的东西是很正常的,但在较旧的系统中,它是磁盘上的普通目录,没有任何特殊属性,这是正常的。关键是下面的文件/dev是设备节点,是一种类似FIFO或者Unix套接字的特殊文件;设备节点有主设备号和次设备号,读取或写入它们就是调用内核驱动程序,就像读取或写入 FIFO 就是调用内核在管道中缓冲输出一样。该驱动程序可以做任何它想做的事情,但它通常会以某种方式接触硬件,例如访问硬盘或在扬声器中播放声音。

回答原来的问题:

  1. 有两个与“文件是否存在”相关的问题;这些是设备节点文件是否确实存在,以及支持它的内核代码是否有意义。前者的解决就像普通文件系统上的任何事情一样。现代系统使用udev或类似的东西来监视硬件事件并/dev相应地自动创建和销毁设备节点。但较旧的系统或轻型自定义构建只能将所有设备节点直接保存在磁盘上,并提前创建。同时,当您读取这些文件时,您正在调用由主设备号和次设备号确定的内核代码;如果这些不合理(例如,您尝试读取不存在的块设备),您只会收到某种 I/O 错误。

  2. 它计算出为哪个设备文件调用哪些内核代码的方式各不相同。对于像 之类的虚拟文件系统/proc,它们实现自己的readwrite功能;内核只是根据它所在的安装点来调用该代码,文件系统实现会处理其余的事情。对于设备文件,它是根据主设备号和次设备号进行调度的。

答案2

/dev/sda1以下是我几乎最新的 Arch Linux 服务器上的文件列表:

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

/dev/所以for中的目录项sda有一个 inode 号,1294。它是磁盘上的一个真实文件。

查看文件大小通常出现的位置。而是出现“8, 1”。这是主设备号和次设备号。另请注意文件权限中的“b”。

该文件/usr/include/ext2fs/ext2_fs.h包含此(片段)C 结构:

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

该结构向我们展示了文件 inode 的磁盘结构。该结构中有很多有趣的东西;仔细看看。

i_mode的元素有struct ext2_inode16 位,仅使用 9 位用于用户/组/其他、读/写/执行权限,另外 3 位用于 setuid、setgid 和 Sticky。它有 4 位来区分“普通文件”、“链接”、“目录”、“命名管道”、“Unix 系列套接字”和“块设备”等类型。

Linux内核可以遵循通常的目录查找算法,然后根据元素中的权限和标志做出决定i_mode。对于块设备文件“b”,它可以找到主设备号和次设备号,并且传统上,使用主设备号来查找指向处理磁盘的某些内核函数(设备驱动程序)的指针。次要设备号通常用作 SCSI 总线设备号、EIDE 设备号或类似的设备号。

关于如何处理文件的其他一些决定/proc/cpuinfo是根据文件系统类型做出的。如果您执行以下操作:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

您可以看到/proc文件系统类型为“proc”。从文件中读取数据/proc会导致内核根据文件系统的类型执行不同的操作,就像在 ReiserFS 或 DOS 文件系统上打开文件会导致内核使用不同的函数来定位文件以及定位文件的数据一样。文件。

答案3

归根结底,它们都是 Unix 的文件,这就是抽象的美妙之处。

内核处理文件的方式现在是一个不同的故事了。

/proc 以及现在的 /dev 和 /run(又名 /var/run)是 RAM 中的虚拟文件系统。 /proc 是内核变量和结构的接口/windows。

我推荐阅读《Linux 内核》http://tldp.org/LDP/tlk/tlk.html和 Linux 设备驱动程序,第三版https://lwn.net/Kernel/LDD3/

我也喜欢《FreeBSD 操作系统的设计与实现》http://www.amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/ref=sr_1_1

查看与您的问题相关的相关页面。

http://www.tldp.org/LDP/tlk/dd/drivers.html

答案4

文件系统中的所有文件都是“真实的”,因为它们允许文件 I/O。当您打开文件时,内核会创建一个文件描述符,它是一个行为类似于文件的对象(在面向对象编程的意义上)。如果读取文件,文件描述符会执行其 read 方法,该方法又会向文件系统(sysfs、ext4、nfs 等)请求文件中的数据。文件系统向用户空间提供统一的接口,并且知道如何处理读取和写入。文件系统反过来要求其他层处理它们的请求。对于 ext4 文件系统上的常规文件,这将涉及在文件系统的数据结构中查找(可能涉及磁盘读取),并最终从磁盘(或缓存)读取以将数据复制到读取缓冲区。对于 sysfs 中的文件,它通常只是 sprintf() 将某些内容写入缓冲区。对于块开发节点,它将要求磁盘驱动程序读取一些块并将它们复制到缓冲区中(主要编号和次要编号告诉文件系统向哪个驱动程序发出请求)。

相关内容