getdents
将返回struct linux_dirent
。它将对任何底层类型的文件系统执行此操作。“磁盘上”格式可能完全不同,只有给定的文件系统驱动程序知道,因此简单的用户空间读取调用无法工作。也就是说,getdents
可以从原生格式转换为填充linux_dirent
.
用 read() 从文件中读取字节不能说同样的话吗?文件中数据的磁盘格式不需要跨文件系统统一,甚至不需要在磁盘上连续 - 因此,从磁盘读取一系列字节将再次成为我希望委托给文件系统驱动程序的事情。
由 VFS [“虚拟文件系统”] 层处理的不连续文件数据。无论 FS 选择如何组织文件的块列表(例如 ext4 使用“inode”:“索引”或“信息”节点。这些使用“ISAM”(“索引顺序访问方法”)组织。但是,一个MS/DOS FS 可以有一个完全不同的组织)。
每个 FS 驱动程序在启动时都会注册一个 VFS 函数回调表。对于给定的操作(例如open/close/read/write/seek
),表中有相应的条目。
VFS 层(即来自用户空间系统调用)将“向下调用”到 FS 驱动程序,FS 驱动程序将执行操作,执行它认为满足请求所需的任何操作。
我假设 FS 驱动程序会知道数据在磁盘上常规文件中的位置——即使数据是碎片化的。
是的。例如,如果读取请求是从文件中读取前三个块(例如 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
...
但是......一些FS以不同的方式组织自己(称之为“B型”):
<static1>,<static2>...
<variable1>,<variable2>,...
因此,类型 A 的组织可能会被用户空间调用以原子方式读取read(2)
,而类型 B 会遇到困难。所以,getdents
VFS 调用处理这个。
VFS 不能像 VFS 一样呈现一个目录的“linux_dirent”视图吗?呈现一个文件的“平面视图”?
这就是getdents
目的。
再说一次,我假设 FS 驱动程序知道每个文件的类型,因此当 read() 对目录而不是一系列字节调用时,它可以返回一个 linux_dirent。
getdents
并不总是存在。当 dirent 的大小是固定的并且只有一种FS 格式时,readdir(3)
调用可能会在read(2)
下面进行并获得一系列字节 [这只是提供的内容read(2)
]。实际上,IIRC,一开始只有readdir(2)
而且getdents
不readdir(3)
存在。
read(2)
但是,如果“短”(例如两个字节太小),你会怎么做?您如何将其传达给应用程序?
我的问题更像是因为 FS 驱动程序可以确定文件是目录还是常规文件(我假设它可以),并且由于它最终必须拦截所有 read() 调用,为什么不读取( ) 在实现为读取 linux_dirent 的目录上?
read
on a dir 不会被拦截并转换为,getdents
因为操作系统是极简主义的。它希望您知道差异并进行适当的系统调用。
你open(2)
为文件或目录做[opendir(3)
是包装器,在open(2)
下面做]。您可以读取/写入/查找文件和查找/getdents 目录。
但是……read
为回报而努力EISDIR
。[旁注:我在原始评论中忘记了这一点]。在它提供的简单“平面数据”模型中,没有一种方法可以传达/控制所有getdents
可以/可以做的事情。
因此,与其允许以劣质方式获取部分/错误信息,不如让内核和应用程序开发人员通过getdents
界面更简单。
此外,原子地getdents
做事。如果您正在读取给定程序中的目录条目,则可能有其他程序正在该目录中创建和删除文件或重命名它们 - 就在您的序列中间。getdents
getdents
将呈现原子视图。文件存在或不存在。它已被重命名或没有。因此,无论您周围发生了多少“动荡”,您都不会得到“半修改”的观点。当你要求getdents
20 个条目时,你会得到它们 [如果只有那么多,你会得到 10 个]。
旁注:一个有用的技巧是“过度指定”计数。也就是说,告诉getdents
您想要 50,000 个条目[您必须提供空间]。你通常会得到大约 100 个左右。但是,现在,您得到的是完整目录的原子快照。我有时会这样做而不是循环计数为 1--YMMV。您仍然必须防止立即消失,但至少您可以看到它(即后续文件打开失败)
所以,你总是得到“完整”的条目,而刚刚删除的文件没有条目。这并不是说该文件仍然存在,只是说它在. 另一个进程可能会立即删除它,但不会在中间getdents
getdents
如果允许read(2)
,您将不得不猜测要读取多少数据,并且不知道哪些条目在部分状态下完全形成。如果 FS 具有上述 B 类组织,则单次读取无法在单个步骤中原子地获取静态部分和可变部分。
放慢脚步read(2)
去做该做的事情在哲学上是不正确的getdents
。
getdents
, unlink
, creat
, rmdir
, 和rename
(etc.) 操作是互锁和序列化的,以防止任何不一致 [更不用说 FS 损坏或泄漏/丢失的 FS 块]。换句话说,这些系统调用都“相互了解”。
如果 pgmA 将“x”重命名为“z”并且 pgmB 将“y”重命名为“z”,它们不会发生冲突。一个先行,然后再行,但没有 FS 块丢失/泄漏。getdents
获取整个视图(无论是“x y”、“y z”、“x z”还是“z”),但它永远不会同时看到“xy z”。