“/dev”Linux 文件是如何创建的?

IQA*_*eas 118 linux devices files

Linux 中有一些特殊的文件,它们并不是真正的文件。

其中最显着和清晰的例子是在dev文件夹中,“文件”,如:

  • /dev/null - 忽略您写入文件的任何内容
  • /dev/random - 输出随机数据而不是文件内容
  • /dev/tcp - 通过网络发送您写入此文件的任何数据

首先,这些类型的“文件”的名称是什么,它们实际上是某种伪装的脚本或二进制文件?

其次,它们是如何创建的?这些文件是在内核级别内置到系统中的,还是有办法自己创建一个“神奇文件”(a 怎么样/dev/rickroll)?

Mat*_*hid 110

/dev/zero是“特殊文件”的一个例子——特别是“设备节点”。通常这些是由发行版安装过程创建的,但如果您愿意,您可以完全自己创建它们。

如果你问ls有关/dev/zero

# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5  Nov 5 09:34 /dev/zero
Run Code Online (Sandbox Code Playgroud)

开头的“c”告诉你这是一个“字符设备”;另一种类型是“块设备”(打印ls为“b”)。粗略地说,硬盘等随机访问设备往往是块设备,而磁带驱动器或声卡等顺序设备往往是字符设备。

“1、5”部分是“主设备号”和“次设备号”。

有了这些信息,我们可以使用mknod命令来制作我们自己的设备节点:

# mknod foobar c 1 5
Run Code Online (Sandbox Code Playgroud)

这将创建一个名为的新文件foobar,在当前文件夹,这不正是同样的事情/dev/zero。(当然,如果您愿意,您可以对其设置不同的权限。)所有这个“文件”真正包含的就是上面的三项——设备类型、主要编号、次要编号。您可以使用它ls来查找其他设备的代码并重新创建这些代码。当你无聊时,只需使用rm删除你刚刚创建的设备节点即可。

基本上,主要编号告诉 Linux 内核要与哪个设备驱动程序通信,而次要编号告诉设备驱动程序您正在谈论哪个设备。(例如,您可能有一个 SATA 控制器,但可能插入了多个硬盘。)

如果您想发明具有新功能的新设备……好吧,您需要编辑 Linux 内核的源代码并编译您自己的自定义内核。所以我们不要那样做!:-) 但是您可以添加与您已有的文件重复的设备文件。像 udev 这样的自动化系统基本上只是监视设备事件并自动为您调用mknod/ rm。没有比这更神奇的了。

还有其他类型的特殊文件:

  • Linux 将目录视为一种特殊的文件。(通常你不能直接打开一个目录,但如果你可以,你会发现它是一个包含特殊格式数据的普通文件,并告诉内核在哪里可以找到该目录中的所有文件。)

  • 符号链接是一种特殊文件。(但硬链接不是。)您可以使用该ln -s命令创建符号链接。(查找联机帮助页。)

  • 还有一种叫做“命名管道”或“FIFO”(先进先出队列)的东西。您可以使用mkfifo. FIFO 是一个神奇的文件,可以同时被两个程序打开——一个读,一个写。发生这种情况时,它的工作方式就像一个普通的壳管。但是您可以单独启动每个程序...

在任何方面都不“特殊”的文件称为“常规文件”。您偶尔会在 Unix 文档中看到提到这一点。这就是它的意思;一个不是设备节点或符号链接或其他任何东西的文件。只是一个普通的日常文件,没有神奇的属性。

  • 如果您想使用 `mknod`,请运行 `cat /proc/devices` 以查看所有驱动程序的主要编号。这给我们带来了另一种特殊文件 `/proc` 文件系统([this answer](http://unix.stackexchange.com/a/241180/90379) 谈到了它)。 (9认同)
  • 其他 Unices 已经发明了他们自己的特殊文件,例如 Solaris 有 [门](https://en.wikipedia.org/wiki/Doors_%28computing%29)。 (9认同)
  • 小吹毛求疵:你不必重新编译内核来编写新的字符/块设备:) http://www.crashcourse.ca/introduction-linux-kernel-programming/lesson-17-your-first-character-设备驱动程序否则这是一个非常好的答案,+1! (7认同)
  • 还有另一种类型的特殊文件,一个绑定到文件系统的 Unix 域套接字。 (6认同)
  • @MathematicalOrchid:您的回复缺少的一个步骤(或至少只是隐式说明)是这些特殊文件根本不是伪装的shell脚本或二进制文件(正如问题所暗示的那样),而是访问存在的功能的接口在操作系统内核中。 (2认同)

kas*_*erd 35

大多数/dev条目是块设备 inode 或字符设备 inode。维基百科对此有很多细节,我不打算重复。

但是/dev/tcp,您的问题中提到的任何现有答案都没有解释。/dev/tcp并且/dev/udp与大多数其他/dev条目不同。的块和字符设备由内核中实现,但/dev/tcp/dev/udp在用户模式中实现的。

bash外壳是具有的一个实现一个节目/dev/tcp/dev/udp(来自复制ksh93)。当您尝试在带有 bash 重定向运算符的路径下打开路径时,它不会执行普通的open系统调用。相反,bash 将创建一个 TCP 套接字并将其连接到指定的端口。

这是在用户模式下实现的,并且仅在某些程序中实现,如下面的示例所示,该示例演示了 letbashcattry 打开之间的区别/dev/tcp/::1/22

$ cat /dev/tcp/::1/22
cat: /dev/tcp/::1/22: No such file or directory
$ cat < /dev/tcp/::1/22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3
Run Code Online (Sandbox Code Playgroud)

不同之处ksh93在于它bash只会使用重定向操作符执行那些 TCP 连接,而不是在其他可能打开文件的地方,如source.内置。

  • IMO,一个更好的方式来说明关于`/dev/tcp` 的观点是它不是一个文件。从来没有一个 **file** 叫这个。Bash 用于打开套接字的语法使用字符串 `/dev/tcp/address` 作为文件名,但称其为“在用户空间中实现的文件”听起来很奇怪。有趣的是,`ksh` 将所有文件名挂钩,而不仅仅是重定向。这更接近于“实现文件”。 (7认同)

Bas*_*tch 21

除了其他答案中解释的设备节点(使用mknod(2)创建或由某些devfs 提供)之外,Linux 还具有由特殊虚拟文件系统提供的其他“神奇”文件,特别是在/proc/(参见proc(5),阅读有关procfs ) 和/sys/(阅读关于sysfs )。

这些伪文件(出现在 -eg stat(2) 中- 作为普通文件,而不是设备)是内核提供的虚拟视图;特别是,读取/proc/(例如cat /proc/$$/maps,在程序中使用或open(2) -ing /proc/self/status)通常不涉及来自磁盘或网络的任何物理 I/O,因此速度非常快。

要在其中创建一些额外的伪文件,/proc/您通常应该编写自己的内核模块并加载它(参见例如this)。

  • AFAIK 有关扩展 /proc 的信息已过时。虽然在技术上仍然可行,但 /proc(或者更确切地说 procfs)应该只保存有关正在运行的进程的信息。所有其他伪文件,包括那些包含内核运行时信息或配置选项的文件,都应该进入 /sys (sysfs)。出于兼容性原因,/proc 中仍有一些与进程无关的伪文件(例如 meminfo、cpuinfo),但新的伪文件应该进入 sysfs。 (3认同)

cas*_*cas 13

它们称为设备节点,由 手动mknod或自动创建udev。它们通常是带有内核驱动程序的字符或块设备的类似文件的接口——例如磁盘是块设备,ttys 和串行端口等是字符设备。

还有其他“特殊”文件类型,包括命名管道、fifos 和套接字。


sso*_*low 12

正如其他用户已经详细解释的那样,特殊文件需要代码来备份。然而,似乎没有人提到 Linux 提供了几种在用户空间中编写代码的方法:

A. FUSE(用户空间中的文件系统)允许您编写类似的东西/proc而不会造成内核崩溃的风险,并使用您选择的语言/运行时来执行,例如GoNode.jsPerlPHPPythonRubyRust等等

它还具有 FUSE 文件系统可以在没有安装的情况下安装的优点,sudo因为它们作为执行安装的用户运行。

以下是人们使用 FUSE 编写的一些示例:

  • mp3fs(将您的 FLAC 文件查看为 MP3 文件,当您将它们复制/单击并拖动到 MP3 播放器时,这些文件会即时创建)
  • PyTagsFS(在从元数据标签构建的虚拟文件夹树中查看您的媒体)
  • fuse-zip(将 Zip 文件安装为文件夹)
  • FuseISO(在没有 root 权限的情况下挂载 ISO)
  • iFUSE(安装 iDevices)
  • FuseDAV(安装 WebDAV 共享)
  • fuse-exfat(挂载 exFAT 格式的文件系统)
  • NTFS-3GLinux的NTFS驱动程序)

B.如果你想创建一个虚拟输入设备,比如键盘、鼠标、游戏杆等(例如,为你正在使用的 USB 设备编写用户空间驱动程序libusb),有uinput

它的绑定更难找到,但我知道它们存在于Go (Keyboard-only)、PythonRuby (2)

实际使用 uinput 的示例包括:

  • G15Daemon(罗技 G15 游戏键盘上 LCD 和游戏键的 Linux 驱动程序)
  • ds4drv(Sony DualShock 4 控制器的驱动程序)
  • xboxdrv(替代 XBox 360 控制器驱动程序和 Linux 等价于x360ce如此糟糕的游戏,如Runner2:Future Legend of Rhythm Alien可能会认为他们在与真正的 XBox 控制器对话,而实际上并非如此)
  • 在有人最终编写内核 Wiimote 驱动程序之前需要像cwiid这样的旧 Wiimote 驱动程序,因此默认情况下支持可用。

C.对于通用字符设备,有CUSE(用户空间中的字符设备)。虽然它不太受欢迎。

该CUSE的API,我个人知道的唯一用户是促使其创作的同一程序:osspd,它实现/dev/dsp/dev/adsp以及/dev/mixer在用户空间(OSS的音频API),使他们能够通过的PulseAudio或DMIX路由。

我能找到的唯一 CUSE 绑定是cusepy,它自 2010 年以来一直没有更新。

D.您可能根本不需要新的特殊文件。

例如,您可以使用libusb(页面上的绑定列表)打开与任何 USB 设备的原始通信,然后通过某种其他机制(TCP/UDP 套接字、读/写 stdin/stdout 或磁盘上的常规文件)与其他程序通信, 等等。)。


Kar*_*ldt 6

Linux 设备驱动程序(强烈推荐)一书详细解释了这一点,甚至让您创建了一个内核模块来执行此操作作为示例,但简而言之,每个设备驱动程序都有特定的函数,在打开、关闭文件时会调用这些函数、读取、写入等。“特殊”文件只是在这些函数中做一些特殊的事情,而不是访问磁盘上的存储硬件。

例如, write 函数/dev/null什么都不做,忽略字节。的 read 函数/dev/random返回一个随机数。