为什么Linux在目录而不是read()上使用getdents()?
我正在浏览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.
由VFS [“虚拟文件系统”]层处理的不连续文件数据.无论FS如何选择组织文件的阻止列表(例如,ext4使用“inode”:“index”或“information”节点.这些使用“ISAM”(“索引顺序访问方法”)组织.但是,MS / DOS FS可以拥有完全不同的组织). 每个FS驱动程序在启动时都会注册一个VFS函数回调表.对于给定的操作(例如,打开/关闭/读/写/搜索),表中有相应的条目. VFS层(即来自用户空间系统调用)将“调用”到FS驱动程序中,FS驱动程序将执行操作,执行它认为完成请求所需的任何操作.
是.例如,如果读取请求是从文件中读取前三个块(例如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调用处理此问题.
这就是获取者的目的.
获取者并不总是存在.当dirents是固定大小并且只有一个FS格式时,readdir(3)调用可能在下面读取(2)并得到一系列字节[这只是read(2)提供的].实际上,IIRC,一开始只有readdir(2),并且getdents和readdir(3)不存在. 但是,如果read(2)为“short”(例如两个字节太小),你会怎么做?你如何与应用程序沟通?
由于操作系统极简主义,读取目录不会被截获并转换为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”. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |