【问题标题】:Using `read` system call on a directory在目录上使用 `read` 系统调用
【发布时间】:2013-07-11 04:56:55
【问题描述】:

我正在查看K&R 2 中的一个示例(8.6 示例 - 列出目录)。它是 Linux 命令 ls 或 Windows 的 dir 的精简版。该示例显示了 opendirreaddir 等函数的实现。我已经尝试逐字输入代码,但它仍然不起作用。它所做的只是打印一个点(针对当前目录)并退出。

我在代码中发现了一件有趣的事情(在readdir 的实现中),它在目录上调用了像openread 这样的系统调用。有点像 -

int fd, n;
char buf[1000], *bufp;

bufp = buf;
fd = open("dirname", O_RDONLY, 0);
n = read(fd, bufp, 1000);
write(fd, bufp, n);

当我运行此代码时,即使文件夹名称 "dirname" 中有一些文件,我也没有得到任何输出。

另外,这本书说,该实现适用于版本 7 和 System V UNIX 系统。这就是它无法在 Linux 上运行的原因吗?

这是代码-http://ideone.com/tw8ouX

那么Linux不允许read对目录进行系统调用吗?还是其他原因造成的?

【问题讨论】:

    标签: c operating-system directory system-calls kernighan-and-ritchie


    【解决方案1】:

    事实上,Linux 不允许read 用于目录。请参阅 man page 并搜索 errno EISDIR。你会发现

    read() 和 pread() 函数会失败,如果 ...

    fildes 参数引用一个目录,并且该实现不允许使用 read() 或 pread() 读取该目录。应该使用 readdir() 函数。

    。不过,其他 UNIX 允许这样做。

    【讨论】:

    • 老实说,我不知道readdir() 本身是系统调用还是调用其他东西。编写一个测试程序,用strace 运行它,找出并告诉我。我也很好奇。
    【解决方案2】:

    在版本 7 UNIX 中,只有一个 unix 文件系统,其目录具有简单的磁盘格式:struct direct 数组。阅读它并解释结果是微不足道的。系统调用将是多余的。

    在现代,有多种文件系统可以被 Linux 和其他类 unix 系统(ext4、ZFS、NTFS!)挂载,其中一些具有复杂的目录格式。您不能对任意目录的原始字节做任何明智的事情。因此内核承担了为目录提供通用接口作为抽象对象的责任。 readdir 是该界面的核心部分。

    一些现代的 unice 仍然允许在目录中使用 read(),因为这是它们历史的一部分。 Linux 的历史始于 90 年代,当时已经很明显,目录上的 read() 永远不会有用,因此 Linux 从未允许这样做。

    Linux 确实提供了一个readdir 系统调用,但它不再使用太多,因为出现了更好的东西:getdents。 readdir 一次只返回一个目录条目,因此如果您在循环中使用 readdir 系统调用来获取目录中的文件列表,您将在每次循环迭代时进入内核。 getdents 将多个条目返回到缓冲区中。

    readdir 不过是标准接口,所以 glibc 提供了一个 readdir 函数,它调用 getdents 系统调用而不是 readdir 系统调用。在普通程序中,您会在源代码中看到 readdir,但在 strace 中看到 getdents。 C 库通过缓冲来提高性能,就像它在 stdio 中调用getchar() 时对常规文件所做的那样,它一次执行几千字节的read(),而不是一堆单字节read()s .

    你永远不会在现代 Linux 系统上使用原始的无缓冲 readdir 系统调用,除非你运行很久以前编译的可执行文件,或者想方设法绕过 C 库。

    【讨论】:

    • 我花了太长时间试图找出失败的原因。尽管如此,这证明了 C 语言的长寿,我已经写了 180~ 页的 K&R 书,而没有遇到与当前实现的重大差异
    • @Alex 它在最初的 Linux 中,并且在 i386(32 位)上仍然受支持,因为删除它会创建一个不兼容的用户空间 ABI,而没有任何好处。 getdents 之后出现的架构,如 amd64,没有旧的系统调用。
    • 如果没有足够的缓冲空间,你也可以放弃 getdents 并用 read 替换它,如果没有足够的缓冲区空间,它将失败,例如 EBUFS。另一个系统调用只会使事情复杂化。使用 getdents,您现在需要例如另一种类型的命令,例如,异步 IO 以使其可用于目录读取(当然没有提供该命令。也许如果 dent-reading 系统调用是一个简单的读取异步目录读取将简单地工作)。
    猜你喜欢
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-11
    • 2014-02-22
    • 1970-01-01
    相关资源
    最近更新 更多