加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

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

发布时间:2020-12-14 01:05:43 所属栏目:Linux 来源:网络整理
导读:我正在浏览K R C,我注意到要读取目录中的条目,他们使用: while (read(dp-fd,(char *) dirbuf,sizeof(dirbuf)) == sizeof(dirbuf)) /* code */ 其中dirbuf是系统特定的目录结构,dp- fd是有效的文件描述符.在我的系统上,dirbuf将是一个struct linux_dirent.请
我正在浏览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()返回-1并且errno设置为EISDIR.

相反,Linux专门用于读取目录的系统调用,即getdents()系统调用.但是,我注意到它的工作方式与上面几乎相同.

while (syscall(SYS_getdents,fd,&dirbuf,sizeof(dirbuf)) != -1)
    /* code */

这背后的理性是什么?与在K& R中使用read()相比,似乎很少/没有益处.

解决方法

getdents将返回struct linux_dirent.它将为任何底层类型的文件系统执行此操作. “磁盘上”格式可能完全不同,只有给定的文件系统驱动程序才知道,因此简单的用户空间读取调用无法工作.也就是说,getdents可能会从原生格式转换为填充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”:“index”或“information”节点.这些使用“ISAM”(“索引顺序访问方法”)组织.但是,MS / DOS FS可以拥有完全不同的组织).

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

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驱动程序,目录很像具有特殊格式信息的平面文件.这些是“条目”目录.这是获奖者返回的内容.这“位于”inode索引层之上.

目录条目可以是[基于文件名长度]的可变长度.因此,磁盘格式是(称之为“Type A”):

static part|variable length name
static part|variable length name
...

但是……有些FS会以不同的方式组织起来(称之为“B型”):

<static1>,<static2>...
<variable1>,<variable2>,...

因此,类型A组织可能通过用户空间read(2)调用原子读取,类型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?

这就是获取者的目的.

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.

获取者并不总是存在.当dirents是固定大小并且只有一个FS格式时,readdir(3)调用可能在下面读取(2)并得到一系列字节[这只是read(2)提供的].实际上,IIRC,一开始只有readdir(2),并且getdents和readdir(3)不存在.

但是,如果read(2)为“short”(例如两个字节太小),你会怎么做?你如何与应用程序沟通?

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?

由于操作系统极简主义,读取目录不会被截获并转换为getdents.它希望您了解其中的差异并进行相应的系统调用.

你打开(2)文件或dirs [opendir(3)是包装,并打开(2)下面].您可以读取/写入/寻找文件和搜索/获取者的目录.

但是…正在阅读EISDIR的回报. [附注:我在原来的评论中忘记了这一点].在它提供的简单“平面数据”模型中,没有办法传达/控制所有获取者可以/做的事情.

因此,内核和应用程序开发人员通过getdents接口更简单,而不是允许以较差的方式获取部分/错误信息.

此外,getdents以原子方式做事.如果您正在读取给定程序中的目录条目,则可能有其他程序正在创建和删除该目录中的文件或重命名它们 – 就在您的getdents序列中间.

getdents将呈现一个原子视图.文件存在或不存在.它已被重命名,或者没有.因此,无论你周围发生了多少“混乱”,你都不会得到“半修改”的观点.当你向参与者询问20个参赛作品时,你会得到它们[如果只有那么多则可以获得10个].

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

因此,您总是获得“整体”条目,并且没有刚刚删除的文件的条目.这并不是说文件仍然存在,只是说它在获取者时就存在了.另一个过程可能会立即删除它,但不会在getdents中间删除

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

放慢阅读(2)去做受访者的做法在哲学上是不正确的.

getdents,unlink,creat,rmdir和rename(etc)操作是互锁和序列化的,以防止任何不一致[更不用说FS损坏或泄漏/丢失FS块].换句话说,这些系统调用都“彼此了解”.

如果pgmA将“x”重命名为“z”并且pgmB将“y”重命名为“z”,则它们不会发生碰撞.一个是第一个,另一个是第二个,但没有FS块丢失/泄漏. getdents获得整个视图(无论是“x y”,“y z”,“x z”还是“z”),但它永远不会同时看到“x y z”.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读