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


112

Linux中有一些特殊文件不是真正的文件。

其中最著名,最清晰的示例在dev文件夹“文件”中,例如:

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

首先,这些实际上是某种形式的脚本或二进制文件的“文件”的名称是什么?

第二,它们是如何创建的?这些文件是在内核级别内置到系统中的,还是有办法自己创建“魔术文件”(/dev/rickroll)?


1
我不知道如何标记这个问题,特别是因为我不知道我要寻找的名称。随时编辑任何相关标签。
IQAndreas

15
顺便说一句,这是unix和类似unix的操作系统设计的基本部分:(几乎)所有内容都是文件,或者可以使其看起来像文件。
cas

5
另请参见:mknod(2) man 2 mknod
RobertL 2015年

4
这些是“设备节点”。但是,您提到的那些设备(与磁盘,键盘,鼠标,声卡和其他设备相关的设备不同)被称为“伪设备”,因为它们不是“真实”设备,仅存在于内核中。通过编写合适的设备驱动程序并将其添加到内核(例如,用于监视计算机上某些活动的伪设备),可以创建新的驱动程序。在/ dev-目录存在于磁盘上之前-如今,这是一个由内核创建的虚拟文件系统(类型为devfs)。
Baard Kopperud 2015年

10
所有文件,甚至是“真实”文件,都是软件工件。每一个设备,文件,插座,特殊的文件,或者说还没有发明背后的软件提供的功能来处理表open()read()close()等在此之后,它是由软件
waltinator

Answers:


101

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

如果你问ls有关/dev/zero

# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5  Nov 5 09:34 /dev/zero

开头的“ c”告诉您这是“字符设备”;另一种类型是“块设备”(由ls“ b” 打印)。大致来说,诸如硬盘之类的随机访问设备往往是块设备,而诸如磁带驱动器或声卡之类的顺序访问设备往往是字符设备。

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

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

# mknod foobar c 1 5

这将创建一个名为的新文件foobar,在当前文件夹,这不正是同样的事情/dev/zero。(当然,您可以根据需要设置不同的权限。)此“文件”真正包含的只是上述三个项目-设备类型,主号码,副号码。您可以ls用来查找其他设备的代码并重新创建它们。当您感到无聊时,只需使用rm即可删除刚创建的设备节点。

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

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

还有其他特殊文件:

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

  • 符号链接是一个特殊文件。(但不是硬链接。)您可以使用ln -s命令创建符号链接。(查找手册页。)

  • 还有一个叫做“命名管道”或“ FIFO”的东西(先进先出队列)。您可以使用创建一个mkfifo。FIFO是一个不可思议的文件,可以同时通过两个程序打开一个文件-一个读,一个写。发生这种情况时,它就像普通的外壳管道一样工作。但是您可以分别启动每个程序...

无论如何都不是“特殊”的文件称为“常规文件”。您偶尔会在Unix文档中看到对此的提及。那是什么意思;不是设备节点,符号链接或其他任何文件的文件。只是普通的日常文件,没有神奇的属性。


4
还有另外一种特殊文件,一种绑定到文件系统的Unix域套接字。
布莱恩·毕

8
如果您想玩mknod,请运行cat /proc/devices以查看所有驱动程序的主要编号。这将我们带到了/proc文件系统的另一种特殊文件中(此答案在讨论)。
ugoren 2015年

8
其他Unices发明了自己的特殊文件,例如Solaris具有door
凯文

6
次要nitpick:您不必重新编译内核即可编写新的字符/块设备:) crashcourse.ca/introduction-linux-kernel-programming / ...否则,这是一个非常好的答案,+ 1!
指挥官Coriander Salamander,2015年

1
@MathematicalOrchid:缺少答复(或至少仅隐式说明)的步骤是,这些特殊文件根本不是伪装的shell脚本或二进制文件(正如所暗示的那样),而是一个访问存在功能的接口在OS内核中。
2015年

34

大多数/dev条目是块设备索引节点或字符设备索引节点。维基百科上有很多细节,我不再赘述。

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

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

这是在用户模式下实现的,并且仅在某些程序中实现,如以下示例所示,该示例演示了让bashcat尝试打开之间的区别/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

与的区别ksh93在于,bash仅将使用重定向操作符进行这些TCP连接,而不会在其他可能打开诸如source.内置文件之类的位置进行TCP连接。


另外,GNU awk gawk同样是特例/inet{,4,6}/{tcp,udp}/$port/$remote/$rport,因为大约在2010年左右(我不记得确切,也找不到发行说明)。
dave_thompson_085

6
IMO,一种更好的说法/dev/tcp是,它不是文件。从来没有一个文件叫做这个。Bash用于打开套接字的语法使用/dev/tcp/address类似于文件名的字符串,但是将其称为“在用户空间中实现的文件”听起来很奇怪。有趣的是,ksh这些文件名可以钩住所有内容,而不仅仅是重定向。这更接近于“实现文件”。
彼得·科德斯

@PeterCordes我相信UWIN会将它们设置为实际文件。而且我认为3dfs也是一样。记住,bash仅复制了此行为,但它起源于其他地方。
mikeserv

19

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

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

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


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

13

它们被称为设备节点,mknod并由或自动创建udev。它们通常是文件或类似字符的接口,用于在内核中具有驱动程序的字符或块设备-例如,磁盘是块设备,tty和串行端口等是字符设备。

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


9

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

A. FUSE(USErspace中的文件系统)使您可以编写类似的代码/proc而不会导致内核崩溃的风险,并可以使用您选择的语言/运行时来执行,例如GoNode.jsPerlPHPPythonRubyRust

它还具有以下优点:可以挂载FUSE文件系统,sudo因为它们在用户执行挂载时便运行。

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

  • mp3fs(将FLAC文件作为MP3文件查看,当您将其复制/单击并拖动到MP3播放器时,即可即时创建它们)
  • PyTagsFS(在由元数据标记构建的虚拟文件夹树中查看媒体)
  • 保险丝-zip(作为文件夹安装Zip文件)
  • FuseISO(没有root权限的Mount ISO)
  • iFUSE(安装iDevices)
  • FuseDAV(WebDAV安装共享)
  • fuse-exfat(安装exFAT格式的文件系统)
  • NTFS-3G Linux的NTFS驱动程序)

B.如果要创建虚拟输入设备,如键盘,鼠标,操纵杆等(例如,为要使用的USB设备编写用户空间驱动程序libusb),则有uinput

很难找到它的绑定,但是我知道它们存在于Go(仅键盘),PythonRuby (2)中

实际使用uinput的示例包括:

  • G15Daemon(用于Logitech G15游戏键盘上的LCD和游戏键的Linux驱动程序)
  • ds4drv(Sony DualShock 4控制器驱动程序)
  • xboxdrv(替代XBox 360控制器驱动程序和Linux,与x360ce等效,因此设计不良的游戏,如Runner2:《节奏外星人的未来传奇》可能会认为它们在与真正的XBox控制器进行对话时会说话)
  • 在最终编写内核Wiimote驱动程序之前,需要使用旧的Wiimote驱动程序(例如cwiid),以便默认情况下将提供支持。

C.对于通用字符设备,有CUSEUSErspace中的字符设备)。虽然它不那么受欢迎。

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

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

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

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


1
cusepy可能有一段时间没有更新(实际上,它从未更新过;它只有一次提交!),但是几周前刚刚使用cusepy编写了字符设备,我可以确认它仍然可以正常工作。它缺少一些与实现相关的功能poll,但是由于cusepy使用ctypes并且绑定是基于C头文件自动生成的,因此,修复任何丢失的功能只是将所需的功能名称添加到中的导出功能列表中setup.py
Aleksi Torhamo,2015年

1
sshfs是FUSE使用的另一个有趣示例。它允许您使用下面的SSH连接浏览远程文件系统,就像它是本地的一样。
Deadless先生,2015年

@Deathless先生。我实际上使用了它并打算提及它,但是我忘了。
ssokolow

6

Linux设备驱动程序》一书(强烈推荐)对此进行了详细说明,甚至还创建了一个内核模块作为示例,但总而言之,每个设备驱动程序都有特定的功能,这些功能在打开,关闭文件时被调用。 ,读取,写入等。“特殊”文件只是在这些功能内做一些特殊的事情,而不是访问磁盘上的存储硬件。

例如,write函数/dev/null只执行任何操作,而忽略字节。读取函数用于/dev/random返回随机数。


1

mount -t devtmpfs

有趣的是,在现代系统中,/dev通常是文件系统类型,可以在所需的任何位置挂载。Ubuntu 16.04:

mkdir d
sudo mount -t devtmpfs none d
head -c 10 d/random
sudo umount d

该功能由启用CONFIG_DEVTMPFS=y,并允许内核本身根据需要创建和销毁设备文件。

CONFIG_DEVTMPFS_MOUNT=y

此选项使内核在上自动挂载devtmpfs /dev

drivers/base/Kconfig 文件:

config DEVTMPFS_MOUNT
    bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs"
    depends on DEVTMPFS
    help
      This will instruct the kernel to automatically mount the
      devtmpfs filesystem at /dev, directly after the kernel has
      mounted the root filesystem. The behavior can be overridden
      with the commandline parameter: devtmpfs.mount=0|1.
      This option does not affect initramfs based booting, here
      the devtmpfs filesystem always needs to be mounted manually
      after the rootfs is mounted.
      With this option enabled, it allows to bring up a system in
      rescue mode with init=/bin/sh, even when the /dev directory
      on the rootfs is completely empty.

file_operations

最后,您应该创建自己的字符设备内核模块,以查看发生了什么。

这是一个最小的可运行示例:了解字符设备(或字符特殊)文件

最重要的步骤是设置file_operations结构,例如:

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
    .open = open,
};

static int myinit(void)
{
    major = register_chrdev(0, NAME, &fops);
    return 0;
}

其中包含为每个与文件相关的系统调用而调用的函数指针。

很明显,您可以覆盖那些与文件相关的系统调用以执行所需的任何操作,因此内核就是这样实现设备的/dev/zero

/dev自动创建条目,无需mknod

最后一个谜是内核如何自动创建/dev条目。

可以通过制作一个自己执行的内核模块来观察该机制,如下所示:https : //stackoverflow.com/questions/5970595/how-to-create-a-device-node-from-the-init-module- linux-kernel-module / 45531867#45531867代码,然后进行device_create通话。


在OpenBSD中,有一个脚本MAKEDEV可以简化此过程,请参见man.openbsd.org/MAKEDEV.8。 不确定Linux为什么没有它,除非它更加复杂。也许零件可以改编。例如,您可以说MKNOD tty,它可以处理细节。
艾伦·科里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.