linux read无效,为什么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.

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型”):

,...

,,...

因此,类型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”.

标签:unix,c-3,linux,architecture,filesystem-access

来源: https://codeday.me/bug/20190516/1115815.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核的VFS层中,readdir或getdents函数用于实现读取目录下文件或目录的名称的功能。以下是一个简化的代码示例,展示了如何在Linux内核中实现该功能: ```c #include <linux/fs.h> #include <linux/dirent.h> // 定义一个函数来打印目录项的名称 void print_directory_entries(struct file *filp) { struct dirent *dir_entry; // 获取目录文件对应的dentry struct dentry *dentry = filp->f_path.dentry; // 获取目录的inode struct inode *inode = dentry->d_inode; // 检查是否为目录文件 if (!S_ISDIR(inode->i_mode)) { printk(KERN_INFO "Not a directory.\n"); return; } // 通过inode的操作函数指针调用readdir或getdents函数 struct super_block *sb = dentry->d_sb; struct file_operations *fops = sb->s_op->readdir; if (!fops) { printk(KERN_INFO "readdir/getdents not supported.\n"); return; } // 调用readdir或getdents函数读取目录项 struct dir_context ctx = { .actor = simple_dir_name, .pos = filp->f_pos, }; fops->iterate(filp, &ctx); } // 简单的目录项遍历回调函数,用于打印目录项名称 static int simple_dir_name(struct inode *inode, struct file *file, struct dir_context *ctx) { struct dentry *dentry = ctx->dent; // 打印目录项名称 printk(KERN_INFO "Directory Entry: %s\n", dentry->d_name.name); // 更新文件位置 ctx->pos = file->f_pos; return 0; } ``` 请注意,这只是一个简化的示例代码,实际的实现可能会更加复杂,因为需要处理错误处理、锁定和其他相关的VFS操作。此外,这段代码仅适用于内核开发,无法直接在用户空间中运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值