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

其中最明显的例子是dev文件夹中的“文件”,例如:



/dev/null-忽略您写入文件的任何内容

/dev/random-输出随机数据而不是文件的内容

/dev/tcp-发送您写入的任何数据该文件通过网络

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

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

评论

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

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

另请参见:mknod(2)man 2 mknod

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

所有文件,甚至是“真实”文件,都是软件工件。每个设备,文件,套接字,特殊文件或尚待发明的东西背后的软件都提供了处理open(),read(),close()等功能的表。在此之后,取决于软件

#1 楼

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

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

“ 1、5”部分是“主要”设备编号”和“较小的设备编号”。

借助此信息,我们可以使用mknod命令创建自己的设备节点:

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


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

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

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

还有其他特殊文件:Linux将目录视为特殊文件。 (通常,您不能直接打开目录,但如果可以,您会发现它是一个普通文件,其中包含特殊格式的数据,并告诉内核在哪里可以找到该目录中的所有文件。)
符号链接是一个特殊文件。 (但是不是硬链接。)您可以使用ln -s命令创建符号链接。 (查找手册页。)
还有一个叫做“命名管道”或“ FIFO”(先进先出队列)的东西。您可以使用mkfifo创建一个。 FIFO是一个不可思议的文件,可以同时通过两个程序打开一个文件-一个读,一个写。发生这种情况时,它就像普通的外壳管道一样工作。但是您可以分别启动每个程序...

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

评论


还有另外一种特殊文件,一种绑定到文件系统的Unix域套接字。

–布莱恩·毕(Brian Bi)
15年11月6日在18:50

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

–ugoren
2015年11月6日在21:57

其他Unices发明了自己的特殊文件,例如Solaris有门。

–凯文
2015年11月6日在22:03

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

–指挥官香菜Sal
15年11月7日,19:50

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

–梦想家
2015年11月9日在22:15

#2 楼

/dev的大多数条目都是块设备索引节点或字符设备索引节点。 Wikipedia对此有很多详细信息,我将不再重复。

但是您的问题中提到的/dev/tcp不能由任何现有答案解释。 /dev/tcp/dev/udp与大多数其他/dev条目不同。块和字符设备是由内核实现的,但是/dev/tcp/dev/udp是在用户模式下实现的。bash shell是一个程序,具有/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.

评论


另外,GNU awk gawk同样是特殊情况/ inet {,4,6} / {tcp,udp} / $ port / $ remote / $ rport,因为大约在2010年左右(我记不清了,也找不到发行版笔记)。

–dave_thompson_085
2015年11月6日23:28

IMO,一种更好的方式来陈述关于/ dev / tcp的观点是,它不是文件。从来没有一个文件叫做这个。 Bash用于打开套接字的语法使用字符串/ dev / tcp / address像文件名一样,但是将其称为“在用户空间中实现的文件”听起来很奇怪。有趣的是,ksh会将这些文件名挂在所有内容上,而不仅仅是重定向。这更接近于“实现文件”。

– Peter Cordes
2015年11月7日,下午1:52

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

–mikeserv
2015年11月7日,下午3:37

#3 楼

除了其他答案(由mknod(2)创建或由一些devfs提供)中解释的设备节点外,Linux还具有由特殊虚拟文件系统提供的其他“神奇”文件,尤其是在/proc/中(请参阅proc(5),请阅读有关内容)。 procfs)和/sys/(有关sysfs的信息)。这些伪文件(例如,在stat(2)中显示为普通文件,而不是设备)是内核提供的虚拟视图;特别是从/proc/(例如,使用cat /proc/$$/maps或在程序中通过open(2)-ing /proc/self/status)读取通常不涉及磁盘或网络中的任何物理I / O,因此速度非常快。

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

评论


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

–梦想家
2015年11月9日在22:22

#4 楼

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

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

#5 楼

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

A。 FUSE(USErspace中的文件系统)使您可以编写类似/proc的内容而不会导致内核崩溃的风险,并可以使用您选择的语言/运行时执行,例如Go,Node.js,Perl,PHP,Python,Ruby,Rust等。 ..

它还有一个优点是FUSE文件系统可以不用sudo进行挂载,因为它们是在用户执行挂载时运行的。

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



mp3fs(将您的FLAC文件作为MP3文件查看,当您将它们复制/单击并拖动到MP3播放器中时即可即时创建)

PyTagsFS(在通过元数据标记构建的虚拟文件夹树中查看媒体)

fuse-zip(将Zip文件安装为文件夹)

FuseISO(没有root权限的安装ISO)

iFUSE(安装iDevices)

FuseDAV(安装WebDAV共享)

fuse-exfat(安装exFAT-格式化的文件系统)

ntfs-3g(Linux NTFS驱动程序)

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

很难找到绑定,但是我知道它们存在于Go(仅键盘),Py​​thon和Ruby(2)中。

实际使用uinput的示例包括:



G15Daemon(Logitech G15游戏键盘上的LCD和游戏键的Linux驱动程序)


xboxdrv(替代XBox 360控制器驱动程序和Linux,与x360ce等效,因此设计不当的游戏如Runner2:《节奏外星人的未来传奇》会认为它们在与真正的XBox控制器对话时会与之对话)
在最终编写内核Wiimote驱动程序之前,需要使用诸如cwiid之类的旧Wiimote驱动程序,以便默认情况下将提供支持。

C。对于通用字符设备,有CUSE(USErspace中的字符设备)。但是它不那么受欢迎。

我个人知道的CUSE API的唯一用户是促使其创建的同一程序:osspd,它在用户空间中实现/dev/dsp/dev/adsp/dev/mixer(OSS音频API),因此它们可以通过PulseAudio或dmix进行路由。

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

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

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

评论


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

– Aleksi Torhamo
2015年11月8日在22:31

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

–不死先生
15年11月13日在13:23

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

– ssokolow
15年11月13日在18:54



#6 楼

《 Linux设备驱动程序》一书(强烈推荐)对此进行了详细说明,甚至还创建了一个内核模块作为示例,但总而言之,每个设备驱动程序都有特定的功能,这些功能在打开,关闭文件时被调用。 ,读取,写入等。“特殊”文件只是在这些功能内执行某些特殊操作,而不是访问磁盘上的存储硬件。例如,/dev/null的写入功能什么都不做,忽略字节。 /dev/random的读取函数返回一个随机数。

#7 楼

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

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

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/如何从Linux内核模块的初始模块代码创建设备节点-/ 45531867#45531867并归结为device_create调用。

评论


在OpenBSD中,有一个脚本MAKEDEV可以简化此过程,请参见man.openbsd.org/MAKEDEV.8。不确定Linux为什么没有它,除了它更复杂之外。也许零件可以改编。例如,您可以说MKNOD tty,它可以处理细节。

–艾伦·科里(Alan Corey)
19-2-22在13:45