6

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

我在代码中(在 的实现中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对目录进行系统调用吗?还是其他原因造成的?

4

2 回答 2

11

在版本 7 UNIX 中,只有一个 unix 文件系统,并且它的目录有一个简单的磁盘格式:array of 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。

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

于 2013-07-12T21:29:32.353 回答
3

事实上,Linux 不允许read目录。请参阅手册页并搜索 errno EISDIR。你会找到

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

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

. 其他 UNIX 仍然允许它。

于 2013-07-12T15:37:35.213 回答