AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 438130
Accepted
Ankur S
Ankur S
Asked: 2018-04-17 07:28:04 +0800 CST2018-04-17 07:28:04 +0800 CST 2018-04-17 07:28:04 +0800 CST

为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现?

  • 772

我试图理解 Linux 上特殊文件的概念。/dev但是,据我所知,当它的功能可以通过 C 中的几行代码来实现时,拥有一个特殊文件似乎很愚蠢。

此外,您可以以几乎相同的方式使用它,即通过管道输入null而不是重定向到/dev/null. 将其作为文件有特定原因吗?将其设为文件不会导致许多其他问题,例如访问同一文件的程序过多吗?

files devices
  • 9 9 个回答
  • 24599 Views

9 个回答

  • Voted
  1. Best Answer
    ioctl
    2018-04-17T12:55:58+08:002018-04-17T12:55:58+08:00

    除了使用字符特殊设备的性能优势外,主要优势是模块化。/dev/null 几乎可以在任何需要文件的上下文中使用,而不仅仅是在 shell 管道中。考虑接受文件作为命令行参数的程序。

    # We don't care about log output.
    $ frobify --log-file=/dev/null
    
    # We are not interested in the compiled binary, just seeing if there are errors.
    $ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".
    
    # Easy way to force an empty list of exceptions.
    $ start_firewall --exception_list=/dev/null
    

    这些都是使用程序作为源或接收器非常麻烦的情况。即使在 shell 管道的情况下,stdout 和 stderr 也可以独立地重定向到文件,这对于作为接收器的可执行文件来说是很难做到的:

    # Suppress errors, but print output.
    $ grep foo * 2>/dev/null
    
    • 156
  2. DopeGhoti
    2018-04-17T07:56:28+08:002018-04-17T07:56:28+08:00

    公平地说,它本身不是一个常规文件。这是一个字符特殊设备:

    $ file /dev/null
    /dev/null: character special (3/2)
    

    它用作设备而不是文件或程序意味着将输入重定向到它或从它输出重定向是一种更简单的操作,因为它可以附加到任何文件描述符,包括标准输入/输出/错误。

    • 62
  3. mtraceur
    2018-04-17T14:05:18+08:002018-04-17T14:05:18+08:00

    我怀疑为什么与塑造 Unix(以及随后的 Linux)的愿景/设计以及由此产生的优势有很大关系。

    毫无疑问,不启动额外进程会带来不可忽视的性能优势,但我认为还有更多:早期的 Unix 有一个“一切都是文件”的比喻,如果你看一下,它有一个不明显但优雅的优势它是从系统的角度,而不是从 shell 脚本的角度。

    假设您有null命令行程序和/dev/null设备节点。从 shell 脚本的角度来看,该foo | null程序实际上是真正有用和方便的,而且foo >/dev/null输入时间稍长,而且看起来很奇怪。

    但这里有两个练习:

    1. 让我们null使用现有的 Unix 工具和/dev/null- 简单:cat >/dev/null. 完毕。

    2. 你能/dev/null在 方面实施null吗?

    您绝对正确,仅丢弃输入的 C 代码是微不足道的,因此可能尚不清楚为什么拥有可用于该任务的虚拟文件很有用。

    考虑一下:几乎每种编程语言都需要处理文件、文件描述符和文件路径,因为它们从一开始就是 Unix 的“一切都是文件”范式的一部分。

    如果您只有写入标准输出的程序,那么程序并不关心您是否将它们重定向到吞下所有写入的虚拟文件,或者将管道重定向到吞下所有写入的程序中。

    现在,如果您的程序采用文件路径来读取或写入数据(大多数程序都这样做)-并且您想向这些程序添加“空白输入”或“丢弃此输出”功能-那么,/dev/null这是免费的。

    请注意,它的优雅之处在于降低了所有相关程序的代码复杂性 - 对于您的系统可以作为具有实际“文件名”的“文件”提供的每个常见但特殊的用例,您的代码可以避免添加自定义命令-line 选项和要处理的自定义代码路径。

    好的软件工程通常依赖于找到好的或“自然”的隐喻,以一种更容易思考但保持灵活的方式抽象问题的某些元素,这样您就可以解决基本相同范围的高级问题,而不必不断地花时间和精力重新实现相同的较低级别问题的解决方案。

    “一切都是文件”似乎是访问资源的一种隐喻:您调用open分层命名空间中的给定路径,获取对对象的引用(文件描述符),并且您可以在文件描述符上使用readand等。write您的 stdin/stdout/stderr 也是恰好为您预先打开的文件描述符。您的管道只是文件和文件描述符,文件重定向可让您将所有这些部分粘合在一起。

    Unix 之所以成功,部分原因在于这些抽象如何很好地协同工作,并且/dev/null最好将其理解为整体的一部分。


    PS 值得一看 Unix 版本的“一切都是文件”之类的东西/dev/null,是迈向更灵活、更强大的隐喻泛化的第一步,该隐喻已在随后的许多系统中实现。

    例如,在 Unix 中,类似文件的特殊对象/dev/null必须在内核本身中实现,但事实证明,以文件/文件夹形式公开功能已经足够有用,从那时起,已经制作了多个系统,为程序提供了一种方式要做到这一点。

    第一个是Plan 9 操作系统,它是由一些制造Unix 的人制造的。后来,GNU Hurd 用它的“翻译器”做了类似的事情。与此同时,Linux 最终获得了 FUSE(现在它也已经传播到其他主流系统)。

    • 55
  4. user5626466
    2018-04-17T08:21:44+08:002018-04-17T08:21:44+08:00

    出于性能原因,我认为/dev/null是字符设备(其行为类似于普通文件)而不是程序。

    如果它是一个程序,它将需要加载、启动、调度、运行,然后停止和卸载程序。您所描述的简单 C 程序当然不会消耗大量资源,但我认为在考虑大量(比如数百万)重定向/管道操作时会产生显着差异,因为流程管理操作大规模成本高昂,因为它们涉及上下文切换。

    另一个假设:管道进入程序需要接收程序分配内存(即使之后直接丢弃)。因此,如果您通过管道输入该工具,则会消耗双倍的内存,一次是在发送程序上,另一次是在接收程序上。

    • 14
  5. Matija Nalis
    2018-04-20T03:32:10+08:002018-04-20T03:32:10+08:00

    除了“一切都是文件”以及大多数其他答案所基于的任何地方都易于使用之外,还存在@user5626466 提到的性能问题。

    为了在实践中展示,我们将创建一个名为的简单程序nullread.c:

    #include <unistd.h>
    char buf[1024*1024];
    int main() {
            while (read(0, buf, sizeof(buf)) > 0);
    }
    

    并编译它gcc -O2 -Wall -W nullread.c -o nullread

    (注意:我们不能在管道上使用 lseek(2) ,因此排空管道的唯一方法是读取它直到它为空)。

    % time dd if=/dev/zero bs=1M count=5000 |  ./nullread
    5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
    dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
    ./nullread  0,02s user 3,90s system 41% cpu 9,337 total
    

    而使用标准/dev/null文件重定向我们可以获得更好的速度(由于提到的事实:更少的上下文切换,内核只是忽略数据而不是复制它等):

    % time dd if=/dev/zero bs=1M count=5000 > /dev/null
    5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
    dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total
    

    (这应该是一个评论,但是对于那个来说太大了并且完全不可读)

    • 7
  6. Phil Frost
    2018-04-20T14:07:47+08:002018-04-20T14:07:47+08:00

    您提出的问题好像通过使用空程序代替文件可能会简单地获得一些东西。也许我们可以摆脱“魔术文件”的概念,而只使用“普通管道”。

    但是考虑一下,管道也是文件。它们通常没有命名,因此只能通过它们的文件描述符进行操作。

    考虑这个有点做作的例子:

    $ echo -e 'foo\nbar\nbaz' | grep foo
    foo
    

    使用 Bash 的进程替换,我们可以通过更迂回的方式完成同样的事情:

    $ grep foo <(echo -e 'foo\nbar\nbaz')
    foo
    

    替换grepfor echo,我们可以在幕后看到:

    $ echo foo <(echo -e 'foo\nbar\nbaz')
    foo /dev/fd/63
    

    该<(...)构造只是替换为文件名,并且 grep 认为它正在打开任何旧文件,它恰好被命名为/dev/fd/63. 这/dev/fd是一个神奇的目录,它为访问它的文件所拥有的每个文件描述符创建命名管道。

    我们可以通过mkfifo创建一个显示在ls所有内容中的命名管道来减少魔法,就像一个普通文件一样:

    $ mkfifo foofifo
    $ ls -l foofifo 
    prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
    $ grep foo foofifo
    

    别处:

    $ echo -e 'foo\nbar\nbaz' > foofifo
    

    看哪, grep 将输出foo.

    我认为,一旦您意识到管道和常规文件以及 /dev/null 之类的特殊文件都只是文件,那么显然实现 null 程序会更加复杂。内核必须以任何一种方式处理对文件的写入,但在 /dev/null 的情况下,它可以将写入丢弃在地板上,而使用管道它必须实际将字节传输到另一个程序,然后必须实际阅读它们。

    • 6
  7. NickW
    2018-04-21T08:13:01+08:002018-04-21T08:13:01+08:00

    我会争辩说,除了历史范式和性能之外,还有一个安全问题。限制具有特权执行凭证的程序的数量,无论多么简单,都是系统安全的基本原则。由于系统服务的使用,替换/dev/null肯定需要具有这样的权限。现代安全框架在防止漏洞利用方面做得很好,但它们并非万无一失。作为文件访问的内核驱动设备更难利用。

    • 0
  8. Matthieu Moy
    2018-04-24T07:14:08+08:002018-04-24T07:14:08+08:00

    正如其他人已经指出的那样,/dev/null 是一个由几行代码组成的程序。只是这些代码行是内核的一部分。

    为了更清楚,这里是 Linux 实现:字符设备在读取或写入时调用函数。写入/dev/null调用write_null,而读取调用read_null ,在此处注册。

    从字面上看,几行代码:这些函数什么都不做。仅当您计算读取和写入以外的功能时,您需要的代码行数比手上的手指还多。

    • 0
  9. helpful
    2018-04-21T05:45:25+08:002018-04-21T05:45:25+08:00

    我希望你也知道 /dev/chargen /dev/zero 和其他类似的,包括 /dev/null。

    LINUX/UNIX 提供了其中的一些 - 使人们可以充分利用编写良好的代码片段。

    Chargen 旨在生成特定且重复的字符模式 - 它非常快,并且会突破串行设备的限制,并且有助于调试已编写且未通过某些测试或其他测试的串行协议。

    零旨在填充现有文件或输出大量零

    /dev/null 只是另一个具有相同想法的工具。

    您工具包中的所有这些工具意味着您有一半的机会让现有程序做一些独特的事情,而无需考虑它们(您的特定需求)作为设备或文件替换

    让我们举办一场比赛,看看谁能在您的 LINUX 版本中仅使用少数字符设备就产生最令人兴奋的结果。

    • -2

相关问题

  • 删除文件名后缀最小的文件

  • 列出根据特定内容行排序的文件

  • 如何提前知道 .zip 内部是否有父目录

  • 如何找到特定的文件类型并将它们 tar?

  • du/df 和 ls 报告不同的磁盘使用情况

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve