gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 13:40:22 33 4
gpt4 key购买 nike

我浏览了 K&R C,我注意到为了读取目录中的条目,他们使用了:

while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf))
/* code */

哪里 dirbuf是特定于系统的目录结构,而 dp->fd一个有效的文件描述符。在我的系统上, dirbuf本来是 struct linux_dirent .请注意,一个 struct linux_dirent条目名称有一个灵活的数组成员,但为了简单起见,让我们假设它没有。 (在这种情况下处理灵活的数组成员只需要一些额外的样板代码)。

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

相反,Linux 专门用于读取目录的系统调用,即 getdents()系统调用。但是,我注意到它的工作方式与上面几乎相同。
while (syscall(SYS_getdents, fd, &dirbuf, sizeof(dirbuf)) != -1)
/* code */

这背后的道理是什么?与使用 read() 相比似乎没有什么好处。正如在 K&R 中所做的那样。

最佳答案

getdents将返回 struct linux_dirent .它将为任何基础类型的文件系统执行此操作。 “on disk”格式可能完全不同,只有给定的文件系统驱动程序知道,所以简单的用户空间读取调用是行不通的。即,getdents可以从 native 格式转换为填充 linux_dirent .

couldn't the same thing be said about reading bytes from a file with read()? The on disk format of the data within a file isn't necessary uniform across filesystems or even contiguous on disk - thus, reading a series of bytes from disk would again be something I expect to be delegated to the file system driver.



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

每个 FS 驱动程序在启动时都会注册一个 VFS 函数回调表。对于给定的操作(例如 open/close/read/write/seek ),表中有相应的条目。

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

I assume that the FS driver would know about the location of the data inside a regular file on disk - even if the data was fragmented.



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

用户空间程序只会看到它的缓冲区被正确的数据填满,而不管 FS 索引和块提取有多复杂。

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

因此,对于 FS 驱动程序,目录很像具有特殊格式信息的平面文件。这些是目录“条目”。这是什么 getdents返回。这“位于”inode 索引层之上。

目录条目可以是可变长度的[基于文件名的长度]。因此,磁盘格式将是(称为“A 型”):
static part|variable length name
static part|variable length name
...

但是......一些FSes以不同的方式组织自己(称之为“B型”):
<static1>,<static2>...
<variable1>,<variable2>,...

因此,用户空间 read(2) 可能会自动读取类型 A 组织。调用,B 型将有困难。所以, getdents VFS 调用处理这个。

couldn't the VFS also present a "linux_dirent" view of a directory like the VFS presents a "flat view" of a file?



就是这样 getdents是为了。

Then again, I'm assuming that a FS driver knows the type of each file and thus could return a linux_dirent when read() is called on a directory rather than a series of bytes.


getdents并不总是存在。当目录是固定大小并且只有一种 FS 格式时, readdir(3)电话大概做了 read(2)在下面并得到一系列字节 [这只是 read(2)提供]。其实IIRC,一开始只有 readdir(2)getdentsreaddir(3)不存在。

但是,如果 read(2),你会怎么做? “短”(例如两个字节太小)?您如何将其传达给应用程序?

My question is more like since the FS driver can determine whether a file is a directory or a regular file (and I'm assuming it can), and since it has to intercept all read() calls eventually, why isn't read() on a directory implemented as reading the linux_dirent?


read目录上的 未截获并转换为 getdents因为操作系统是极简主义的。它希望您了解差异并进行适当的系统调用。

你做 open(2)对于文件或目录 [ opendir(3)是包装器并且做 open(2)下]。您可以读/写/查找文件,查找/获取目录。

但是...做 read退货 EISDIR . [旁注:我在最初的评论中忘记了这一点]。在它提供的简单“平面数据”模型中,没有办法传达/控制所有这些 getdents可以/可以。

因此,内核和应用程序开发人员可以更简单地通过 getdents 获取部分/错误信息,而不是允许以低劣的方式获取部分/错误信息。界面。

此外, getdents以原子方式做事。如果您正在读取给定程序中的目录条目,则可能有其他程序正在创建和删除该目录中的文件或重命名它们——就在您的 getdents 的中间。序列。
getdents将呈现原 subview 。文件存在或不存在。它已被重命名或尚未重命名。因此,无论您周围发生了多少“动荡”,您都不会获得“半修改”的观点。当你问 getdents对于 20 个条目,您会得到它们 [如果只有那么多,则为 10 个]。

旁注:一个有用的技巧是“过度指定”计数。即告诉 getdents您需要 50,000 个条目 [您必须提供空间]。你通常会得到大约 100 左右的东西。但是,现在,您拥有的是完整目录的及时原子快照。我有时会这样做,而不是循环计数 1--YMMV。您仍然必须防止立即消失,但至少您可以看到它(即后续文件打开失败)

因此,您总是会得到“整个”条目,而没有刚刚删除的文件的条目。这并不是说该文件仍然存在,只是在 getdents 发生时它就在那里。 .另一个进程可能会立即删除它,但不会在 getdents 中间

read(2)如果允许,您必须猜测要读取多少数据,并且不知道哪些条目是在部分状态下完全形成的。如果 FS 具有上述 B 型组织,则单次读取无法在单个步骤中原子地获取静态部分和可变部分。

放慢速度在哲学上是不正确的 read(2)做什么 getdents做。
getdents , unlink , creat , rmdir , 和 rename (等)操作是互锁和序列化的,以防止任何不一致[更不用说 FS 损坏或泄漏/丢失 FS 块]。换句话说,这些系统调用都“相互了解”。

如果 pgmA 将“x”重命名为“z”并且 pgmB 将“y”重命名为“z”,则它们不会发生冲突。一个先行,再第二个,但没有 FS 块丢失/泄漏。 getdents获取整个 View (无论是“x y”、“y z”、“x z”还是“z”),但它永远不会同时看到“x y z”。

关于c - 为什么 Linux 在目录上使用 getdents() 而不是 read()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36144807/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com