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


28

这是一个比较低级的问题,我知道这可能不是最好的问题。但是,它似乎比其他任何SE网站都更合适,所以请继续。

我知道在Linux文件系统上实际上存在一些文件,例如:/usr/bin/bash是存在的文件。然而,(据我所知),有的还没有实际存在本身和更虚拟的文件,如:/dev/sda/proc/cpuinfo等我的问题是(它们是两个,而是要独立的问题也密切相关):

  • 当发出读取命令(或类似命令)时,Linux内核如何判断这些文件是真实的(并因此从磁盘读取)?
  • 如果文件不是真实的:例如,从读取/dev/random将返回随机数据,而从读取/dev/null将返回EOF。如何计算要从该虚拟文件中读取哪些数据(以及如果/如果将数据也写入该虚拟文件中,那么该怎么做)-是否存在某种类型的映射,该映射具有用于分隔适用于每个文件的读/写命令的指针,甚至虚拟目录本身?因此,的条目/dev/null可以简单地返回EOF

1
创建文件后,内核会记录其类型。然后,将常规磁盘文件与符号链接,块设备,字符设备,目录,套接字,FIFO等区别对待。这是内核的工作。
乔纳森·莱夫勒2015年

看到mknod的人pge
Jasen 2015年

这有点像问“电灯开关如何知道电灯是否打开?” 灯开关负责确定灯是否打开。
莫妮卡(Monica)

Answers:


25

因此,这里基本上有两种不同类型的事物:

  1. 普通文件系统,以熟悉的方式将文件保存在具有数据和元数据的目录中(包括软链接,硬链接等)。这些通常但并非总是由块设备支持以进行持久存储(tmpfs仅驻留在RAM中,但与普通文件系统相同)。它们的语义很熟悉。读取,写入,重命名等等,所有这些都按照您期望的方式进行。
  2. 各种虚拟文件系统。/proc/sys在这里的例子,因为是定制的FUSE文件系统,如sshfsifuse。这些中有更多的多样性,因为实际上它们只是指的是某种意义上具有“自定义”语义的文件系统。因此,当您从中的文件中读取/proc数据时,实际上并没有像在普通文件系统下那样,访问由其他较早编写的文件存储的特定数据。您实际上是在进行内核调用,请求一些即时生成的信息。这段代码可以做任何喜欢的事情,因为它只是实现read语义的某个功能。因此,您在下有文件的怪异行为/proc,例如,在文件不存在时假装为符号链接。

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

要回答原始问题:

  1. 关于“文件是否存在”有两个问题。这些是设备节点文件是否确实存在,以及支持该文件的内核代码是否有意义。前者就像普通文件系统上的任何东西一样被解析。现代系统使用udev或类似工具监视硬件事件,并相应地自动创建和销毁设备节点/dev。但是,较早的系统,或轻量级的定制版本,只能将所有设备节点按字面意思放在磁盘上,并提前创建。同时,当您读取这些文件时,您正在调用内核代码,该代码由主设备号和次设备号确定;如果这些都不合理(例如,您正在尝试读取不存在的块设备),则只会得到某种I / O错误。

  2. 它计算出要针对哪个设备文件调用的内核代码的方式。对于虚拟文件系统,如/proc,他们实现自己的readwrite功能; 内核只是根据其所在的挂载点调用该代码,而文件系统实现将处理其余部分。对于设备文件,它是根据主要和次要设备号调度的。


所以,如果说,一个旧系统断电了,其中的文件/dev仍然存在,但是我猜它们将在系统启动时被清除吗?
2015年

2
如果正常或异常关闭了旧系统(一个没有创建任何动态设备的系统),则设备节点将像任何文件一样保留在磁盘上。然后,在下次启动时,它们也将保留在磁盘上,您可以照常使用它们。只有在现代系统中,创建和销毁设备节点才会发生任何特殊情况。
汤姆·亨特

因此,不使用的更现代的系统tmpfs会根据需要动态创建和删除它们,例如:启动和关闭?

3
devtmpfs/dev现代Linux中的文件系统与相似tmpfs,但是要支持一些差异udev。(内核会在移交给之前自行进行一些自动的节点创建udev,以减少启动的复杂性。)在所有这些情况下,设备节点仅驻留在RAM中,并根据硬件的需要动态创建和销毁。大概您也可以udev在普通的磁盘上使用/dev,但我从未见过这样做,而且似乎没有任何充分的理由。
汤姆·亨特

17

这是/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_inode具有16位,并且仅对用户/组/其他,读/写/执行权限使用9位,对setuid,setgid和sticky使用另外3位。它有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文件系统上打开文件会导致内核使用不同的功能来查找文件以及查找文件的数据一样。文件。


您确定只有“磁盘上的实际文件”显示一个索引节点号吗?我知道4026531975 -r--r--r-- 1 root root 0 Nov 14 18:41 /proc/mdstat这显然不是“真实文件”。
贡伯特

7

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

内核处理文件的方式,现在已经不一样了。

/ proc和现今的/ dev和/ run(aka / var / run)是RAM中的虚拟文件系统。/ proc是内核变量和结构的接口/窗口。

我建议阅读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


谢谢,在您发表评论后,我对第一个问题做了些微修改。
2015年

请阅读最后一条评论。
Rui F Ribeiro

5

除了@RuiFRibeiro和@BruceEdiger的答案之外,您所做的区分也不完全是内核所做的区分。实际上,您有各种各样的文件:常规文件,目录,符号链接,设备,套接字(而且我总是忘记一些文件,因此我不会尝试列出完整的文件)。您可以使用以下命令获取有关文件类型的信息ls:它是该行的第一个字符。例如:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

开头的“ b”表示此文件是一个块设备。破折号表示常规文件,“ l”表示符号链接,依此类推。此信息存储在文件的元数据中stat,例如可以通过系统调用访问,因此内核可以以不同方式读取文件和符号链接。

然后,您在“真实文件” /bin/bash和“虚拟文件” 之间进行了另一个区分,/proc/cpuinfols都将它们报告为常规文件,因此区别是另一种:

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

发生的情况是它们属于不同的文件系统。/proc是伪文件系统的安装点,procfs/bin/bash在常规磁盘文件系统上。当Linux打开文件时(根据文件系统的不同,它的工作方式也不同),它会填充一个数据结构file,该数据结构除其他属性外还具有几个函数指针的结构,这些指针描述了如何使用此文件。因此,它可以为不同类型的文件实现不同的行为。

例如,以下是这些操作公告的操作/proc/meminfo

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

如果看一下的定义meminfo_proc_open,您会发现该函数使用函数返回的信息填充内存中的缓冲区,该函数meminfo_proc_show的任务是收集有关内存使用情况的数据。然后可以正常读取此信息。每次打开文件时,meminfo_proc_open都会调用该函数,并刷新有关内存的信息。


3

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.