C 为什么Linux在目录上使用getdents()而不是read()?

C 为什么Linux在目录上使用getdents()而不是read()?,c,linux,unix,architecture,filesystem-access,C,Linux,Unix,Architecture,Filesystem Access,我浏览了一下K&R C,注意到要阅读a目录中的条目,他们使用: while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)) /* code */ 其中,dirbuf是系统特定的目录结构,dp->fd是有效的文件描述符。在我的系统上,dirbuf应该是一个struct linux\u dirent。请注意,struct linux\u dirent有一个灵活的数组成员作为条目名,但是为了简

我浏览了一下K&R C,注意到要阅读a目录中的条目,他们使用:

while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf))
    /* code */
其中,
dirbuf
是系统特定的目录结构,
dp->fd
是有效的文件描述符。在我的系统上,
dirbuf
应该是一个
struct linux\u dirent
。请注意,
struct linux\u dirent
有一个灵活的数组成员作为条目名,但是为了简单起见,我们假设它没有。(在这种情况下,处理灵活的数组成员只需要一点额外的样板代码)

然而,Linux不支持这种构造。使用
read()
尝试读取上述目录项时,
read()
返回
-1
,并且
errno
设置为
EISDIR

相反,Linux专门用于读取目录的系统调用,即
getdents()
系统调用。然而,我注意到它的工作方式与上面的工作方式基本相同

while (syscall(SYS_getdents, fd, &dirbuf, sizeof(dirbuf)) != -1)
    /* code */
这背后的理性是什么?与在K&R中使用
read()
相比,似乎没有什么好处。

在K&R中(实际上,至少通过SVr2实现了Unix),目录项是16个字节,使用2个字节作为索引节点,14个字节作为文件名

使用
read
是有意义的,因为磁盘上的目录项大小都相同。16字节(2的幂)也有意义,因为它不需要硬件乘法来计算偏移量。(我记得1978年左右有人告诉我,Unix磁盘驱动程序使用浮点,速度很慢……但这是二手货,尽管很有趣)

后来对目录的改进允许使用更长的名称,这意味着大小不同(没有必要使巨大的条目与可能的最大名称相同)。提供了一个更新的界面,
readdir

Linux提供了一个较低级别的接口。根据其报告:

这些不是您感兴趣的接口。看看 用于符合POSIX标准的C库接口。本页 记录裸内核系统调用接口

如示例所示,
getdents
是一个系统调用,用于实现
readdir
。未指定
readdir
的实现方式。早期的
readdir
(大约30年前)不能作为库函数使用
read
malloc
以及类似的函数来管理从目录中读取的长文件名,这没有什么特别的原因

在本例中,将特性移动到内核(可能)是为了提高性能。因为
getdents
一次读取多个目录条目(与
readdir
不同),这可能会减少读取一个小目录的所有条目的开销(通过减少系统调用次数)

进一步阅读:


getdents
将返回
struct linux\u dirent
。它将对任何底层类型的文件系统执行此操作。“磁盘上”格式可能完全不同,只有给定的文件系统驱动程序知道,因此简单的用户空间读取调用无法工作。也就是说,
getdents
可以从本机格式转换为填充
linux\u-dirent

对于使用read()从文件读取字节,不能说同样的话吗?文件中数据的磁盘格式在文件系统中不一定是统一的,甚至在磁盘上也不一定是连续的——因此,从磁盘读取一系列字节也是我希望委托给文件系统驱动程序的事情

中的不连续文件数据由VFS[“虚拟文件系统”]层处理。无论FS选择如何组织文件的阻止列表(例如,ext4使用“索引节点”:“索引”或“信息”节点。这些节点使用“ISAM”(“索引顺序访问方法”)组织。但是,MS/DOS FS可以具有完全不同的组织)

每个FS驱动程序在启动时注册一个VFS函数回调表。对于给定的操作(例如,
打开/关闭/读取/写入/搜索
),表中有相应的条目

VFS层(即来自用户空间系统调用)将“向下调用”到FS驱动程序中,FS驱动程序将执行操作,执行其认为满足请求所必需的任何操作

我假设FS驱动程序会知道数据在磁盘上的常规文件中的位置,即使数据是碎片化的

对。例如,如果读取请求是从文件中读取前三个块(例如0,1,2),则FS将查找文件的索引信息,并获取要从磁盘表面读取的物理块列表(例如1000000200,37)。这一切都在FS驱动程序中透明处理

用户空间程序只会看到它的缓冲区被正确的数据填满,而不考虑FS索引和块获取的复杂性

也许更合适的说法是[松散地]将其称为传输inode数据,因为存在文件的inode(即,inode具有索引信息以“分散/聚集”文件的FS块)。但是,FS驱动程序也在内部使用它来读取目录。也就是说,每个目录都有一个inode来跟踪该目录的索引信息

因此,对于FS驱动程序来说,目录非常类似于包含特殊格式信息的平面文件。这些是目录“条目”。这是
getdents
返回的结果。这“位于”inode索引层的顶部

目录项可以是可变长度[基于文件名的长度]。因此,磁盘上的格式是(称为“A型”):

但是。。。一些FSE的组织方式不同(称之为“B型”):

,。。。
,,...
因此,可以读取组织所在的类型
static part|variable length name
static part|variable length name
...
<static1>,<static2>...
<variable1>,<variable2>,...