Linux/Unix 中的文件有什么特点?

Tim*_*Tim 7 pipe inode files socket fifo

Linux/Unix 中的文件有什么特点?

一个文件可以有多种类型:常规文件、目录、符号链接设备文件套接字管道fifo等等。例如,一个符号链接:

$ sudo file /proc/22277/fd/23
/proc/22277/fd/23: broken symbolic link to socket:[7540288]
Run Code Online (Sandbox Code Playgroud)

一个插座:

$ sudo ls -l /run/user/1001/systemd/notify
srwxrwxr-x 1 testme testme 0 Feb  6 16:41 /run/user/1001/systemd/notify
Run Code Online (Sandbox Code Playgroud)
  1. 文件的特征是否具有inode(某个文件系统中的 inode,无论是在内存中还是在辅助存储设备中?)?所有文件类型的文件都有 inode 吗?(我想这两个问题都是肯定的。)

  2. Linux 的 Internet 域套接字、传输协议 (TCP/UDP) 的套接字和端口似乎在说一些带有打开文件的描述是文件。具有打开文件描述的东西一定有一个 inode 吗?

    打开文件描述是比文件更好的术语,您无法定义“文件”。网络套接字和 Unix 域套接字都是打开的文件描述。UDS 可能会或可能会关联磁盘上的某些内容(有很多条件会影响这一点)。NS 从不关联磁盘上的任何内容。

谢谢。

Ser*_*nyy 5

长话短说

  • 文件是一个对象,您可以在其上执行部分或全部基本操作(打开、读取、写入、关闭),并且将元数据存储在索引节点中。
  • 文件描述符是对这些对象的引用
  • 打开文件描述(是的,打开部分很重要)是文件(由至少一个文件描述符表示)如何打开

文件作为抽象

让我们参考POSIX 定义 2017,第 3.164 节了解 File 是如何定义的:

可以写入、读取或两者兼而有之的对象。文件具有某些属性,包括访问权限和类型。文件类型包括常规文件、字符特殊文件、块特殊文件、FIFO特殊文件、符号链接、套接字和目录。该实现可能支持其他类型的文件。

因此,文件是我们可以读取、写入或两者兼而有之的任何东西,它也包含元数据。大家——回家吧,案子结了!

嗯,没那么快。这样的定义为相关概念开辟了很大的空间,并且常规文件和管道之间存在明显的差异。“一切皆文件”本身就是一个概念和设计模式,而不是字面上的表述。基于该模式,目录、管道、设备文件、内存文件、套接字等文件类型 - 所有这些都可以通过一组系统调用进行操作,例如open()openat()write()、 对于套接字recv()send(),以一致的方式;以 USB 为例 - 您有很多不同的设备,但它们都连接到完全相同的 USB 端口(没关系,实际上有多种类型的 USB 端口类型,从 A 到 C,但您明白了)。

当然,必须有一个特定的接口或以一致的方式对实际数据的引用才能工作,这就是文件描述符

每个进程唯一的非负整数,用于标识打开的文件以进行文件访问。新创建的文件描述符的值是从零到{OPEN_MAX}-1。

因此,我们可以write()通过文件描述符 1 以与写入常规文件相同的方式访问 STDOUT /home/user/foobar.txt。当您open()访问文件时,您会获得文件描述符,并且可以使用相同的write()函数写入该文件。这就是 Unix 最初的创建者试图解决的重点——极简和一致的行为。当您执行command > /home/user/foobar.txt此操作时,shell 将复制引用的文件描述符foobar.txt 并将其作为command文件描述符 1 ( echo 的 STDOUT )传递,或者更准确地说,它将执行命令dup2(3,1),然后execve()执行命令。但无论如何,command仍然会使用相同的写入系统调用到文件描述符 1 中,就好像什么也没发生一样。

当然,就大多数用户所认为的文件而言,他们认为是磁盘文件系统上的常规文件。这与常规文件定义更一致,第 3.323 节

一种文件,它是可随机访问的字节序列,没有系统强加的进一步结构。

相比之下,我们有Sockets

用作进程间通信的通信端点的特定类型的文件,如 POSIX.1-2017 的系统接口卷中所述。

无论哪种类型,我们可以对不同文件类型执行的操作在概念上完全相同 - 打开、读取、写入、关闭。


所有文件都有索引节点

您应该在文件定义中注意到的是文件具有“某些属性”,这些属性存储在 inode 中。其实具体在Linux上,我们可以参考inode(7)手册第一行:

每个文件都有一个包含该文件元数据的索引节点。应用程序可以使用 stat(2) (或相关调用)检索此元数据

繁荣。清晰、直接。我们最熟悉的索引节点是磁盘上的数据块和目录中存储的文件名之间的桥梁(因为目录就是文件名和相应索引节点的列表)。即使在内核中的pipefs和sockfs等虚拟文件系统中,我们也可以找到inode。以这段代码片段为例:

static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
{
    return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]",
            dentry->d_inode->i_ino);
}
Run Code Online (Sandbox Code Playgroud)

打开文件说明

现在您已经完全困惑了,Linux/Unix 引入了称为“打开文件描述”的东西,并且为了使解释简单 - 这是另一个抽象。用斯蒂芬·查泽拉斯的话来说,

它更多的是关于文件如何打开的记录,而不是文件本身。

并且它与POSIX 定义一致:

一个进程或一组进程如何访问文件的记录。每个文件描述符恰好引用一个打开的文件描述,但一个打开的文件描述可以由多个文件描述符引用。文件偏移、文件状态和文件访问模式是打开文件描述的属性。

现在,如果我们还看一下Understanding the Linux Kernel这本书,作者指出

Linux 将 BSD 套接字实现为属于sockfs特殊文件系统的文件...更准确地说,对于每个新的 BSD 套接字,内核都会在sockfs特殊文件系统中创建一个新的 inode。

请记住,套接字也是由文件描述符引用的,因此内核中会有与套接字相关的打开文件描述,我们可以得出结论,套接字是文件。

待续 。。。或许


Ljm*_*art 1

1) 在 Unix 上的大多数文件系统上,文件、fifo、目录等由 inode 描述。一个 inode 有许多字段,但本例中最有趣的是 i_mode 字段。在权限旁边,它包含 inode 指向的“文件”类型:

Constant        Value   Description
-- file format --
EXT2_S_IFSOCK   0xC000  socket
EXT2_S_IFLNK    0xA000  symbolic link
EXT2_S_IFREG    0x8000  regular file
EXT2_S_IFBLK    0x6000  block device
EXT2_S_IFDIR    0x4000  directory
EXT2_S_IFCHR    0x2000  character device
EXT2_S_IFIFO    0x1000  fifo
Run Code Online (Sandbox Code Playgroud)

2)这取决于你如何看待它。对于每个打开的文件,无论它是“真实”文件还是其他构造(例如未命名管道),您都可以通过系统调用获取索引节点。但是当文件句柄关闭时,该索引节点将不可用。(编辑第 2 节以消除事实不正确之处)