Как создаются файлы «/ dev» для Linux?

112

В Linux есть специальные файлы, которые на самом деле не являются файлами.

Наиболее заметные и наглядные примеры из них находятся в devпапке «файлы», например:

  • /dev/null - игнорирует все, что вы пишете в файл
  • /dev/random - выводит случайные данные вместо содержимого файла
  • /dev/tcp - Отправляет любые данные, которые вы пишете в этот файл по сети

Прежде всего, как называются эти типы «файлов», которые на самом деле являются замаскированным скриптом или двоичным файлом?

Во-вторых, как они созданы? Эти файлы встроены в систему на уровне ядра, или есть способ создать «волшебный файл» самостоятельно (как насчет a /dev/rickroll)?

IQAndreas
источник
1
Я понятия не имел, как пометить этот вопрос, тем более что я не знаю название того, что я ищу. Не стесняйтесь редактировать в любых соответствующих тегах.
IQAndreas
15
Кстати, это фундаментальная часть разработки Unix и Unix-подобных операционных систем: (почти) все является файлом, или может быть сделано, чтобы выглядеть как файл.
Cas
5
Также смотрите: mknod (2) man 2 mknod
RobertL
4
Это «узлы устройства». Однако те, которые вы упомянули - в отличие от дисков, клавиатуры, мыши, аудио-карт и других устройств - являются так называемыми «псевдо-устройствами», поскольку они не являются «настоящими» устройствами и существуют только в ядре. Можно создать новые, написав подходящий драйвер устройства и добавив его в ядро ​​(например, псевдоустройство для мониторинга некоторой активности на компьютере). До того, как / dev-каталог существовал на диске - в наши дни это виртуальная файловая система (типа devfs), созданная ядром.
Баард Копперуд
10
Все файлы, даже «настоящие» файлы, являются программными артефактами. Программное обеспечение за каждое устройство, файл, сокет, специальный файл, или что - то еще не изобретен содержит таблицу функций для обработки open(), read(), close()и т.д. После этого, это до программного обеспечения
waltinator

Ответы:

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. Вот что это значит; файл, который не является узлом устройства, символической ссылкой или чем-то еще. Просто обычный, каждый день файл без магических свойств.

MathematicalOrchid
источник
4
Существует также еще один тип специального файла - сокет домена Unix, связанный с файловой системой.
Брайан Би
8
Если вы хотите поиграть mknod, бегите, cat /proc/devicesчтобы увидеть основные цифры для всех водителей. Что приводит нас к еще одному виду специальных файловых /procфайловых систем ( этот ответ говорит об этом).
Угорен
8
Другие Unies изобрели свои собственные специальные файлы, например, у Solaris были двери .
Кевин
6
Незначительные мелочи: вам не нужно перекомпилировать ядро, чтобы написать новое символьное / блочное устройство :) crashcourse.ca/introduction-linux-kernel-programming/… В противном случае это действительно хороший ответ, +1!
Командир Кориандр Саламандра
1
@MateticOrchid: шаг, в котором отсутствует ваш ответ (или, по крайней мере, только неявно), - это тот факт, что эти специальные файлы вовсе не являются замаскированными сценариями оболочки или двоичными файлами (как подразумевается в вопросе), а скорее интерфейсом для доступа к функциональности, которая присутствует в ядре ОС.
Мечтатель
34

Большинство /devзаписей являются блочными инодами устройства или символьными инодами устройства. В Википедии есть много подробностей о том, что я не собираюсь повторять.

Но то, /dev/tcpчто упоминается в вашем вопросе, не объясняется ни одним из существующих ответов. /dev/tcpи /dev/udpотличаются от большинства других /devзаписей. Блочные и символьные устройства реализуются ядром, но /dev/tcpи /dev/udpреализуются в пользовательском режиме.

Оболочка bash - это одна из программ, которая имеет реализацию /dev/tcpи /dev/udp(скопирована ksh93). Когда вы пытаетесь открыть путь ниже тех, у которых есть операторы перенаправления bash, он не будет выполнять обычный openсистемный вызов. Вместо этого bash создаст сокет TCP и подключит его к указанному порту.

Это реализовано в пользовательском режиме и только в некоторых программах, что можно увидеть в следующем примере, который демонстрирует разницу между разрешением bashи catпопыткой открыть/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или ..

kasperd
источник
Кроме того, GNU awk gawkаналогично особым случаям /inet{,4,6}/{tcp,udp}/$port/$remote/$rport, начиная примерно с 2010 года (я точно не помню и не могу найти заметки о выпуске).
dave_thompson_085
6
ИМО, лучший способ заявить о /dev/tcpтом, что это НЕ файл. Там никогда не будет файла с именем этого. Синтаксис Bash для открытия сокетов использует строку /dev/tcp/addressкак имя файла, но называть ее «файл, реализованный в пространстве пользователя», звучит странно. Интересно, что kshэти имена перехватывают все, а не только перенаправления. Это ближе к "реализации файла".
Питер Кордес
@PeterCordes Я считаю, что UWIN устанавливает их как фактические файлы. и я думаю, что 3dfs делает то же самое. помните, bashтолько скопировал это поведение, но оно происходит в другом месте.
mikeserv
19

Помимо узлов устройств, описанных в других ответах (созданных с помощью mknod (2) или предоставленных некоторыми devfs ), в Linux есть и другие «магические» файлы, предоставляемые специальными виртуальными файловыми системами , в частности, в /proc/(см. Proc (5) , читайте о procfs). ) и в /sys/(читайте о sysfs ).

Эти псевдофайлы (которые появляются от -eg до stat (2) - как обычные файлы, а не как устройства) - это виртуальное представление, предоставляемое ядром; в частности, чтение с /proc/(например, с помощью cat /proc/$$/mapsили с помощью open (2) -ing /proc/self/statusв вашей программе), как правило, не требует физического ввода-вывода с диска или сети, поэтому выполняется довольно быстро.

Чтобы создать какой-то дополнительный псевдофайл, /proc/вам, как правило, следует написать собственный модуль ядра и загрузить его (см., Например, это ).

Василий Старынкевич
источник
3
AFAIK информация по расширению / proc устарела. Хотя технически это возможно, / proc (или, скорее, procfs) должен содержать информацию только о запущенных процессах. Все остальные псевдофайлы, включая те, которые содержат информацию о времени выполнения или параметры конфигурации ядра, должны быть помещены в / sys (sysfs). По-прежнему есть некоторые не связанные с процессом псевдо-файлы в / proc (например, meminfo, cpuinfo) по соображениям совместимости, но новые псевдо-файлы должны идти в sysfs.
Мечтатель
13

Они называются узлами устройства и создаются вручную mknodили автоматически udev. Обычно это файловые интерфейсы для символьных или блочных устройств с драйверами в ядре - например, диски - это блочные устройства, ttys и последовательные порты и т. Д. - символьные устройства.

Существуют и другие «специальные» типы файлов, в том числе именованные каналы, fifo и сокеты.

саз
источник
9

Как другие пользователи уже объяснили очень подробно, специальные файлы требуют кода для их резервного копирования. Однако, похоже, никто не упомянул, что 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 (монтирование ISO без прав root)
  • iFUSE (Mount iDevices)
  • FuseDAV ( общий доступ к WebDAV)
  • fuse-exfat (монтирует файловые системы в формате exFAT)
  • NTFS-3g ( драйвер Linux NTFS)

Б. Если вы хотите создать виртуальное устройство ввода, такое как клавиатура, мышь, джойстик и т. Д. (Например, для написания драйвера пользовательского пространства для USB-устройства, с которым вы разговариваете libusb), есть вход .

Связи для него найти сложнее, но я знаю, что они существуют для Go (только для клавиатуры), Python и Ruby (2) .

Примеры реального использования uinput включают в себя:

  • G15Daemon (драйвер Linux для ЖК-дисплея и игровых клавиш на игровых клавиатурах Logitech G15)
  • ds4drv (драйвер для контроллеров Sony DualShock 4)
  • xboxdrv (альтернативный драйвер контроллера XBox 360 и Linux, эквивалентный x360ce, настолько плохо спроектированные игры, как Runner2: Future Legend of Rhythm Alien могут думать, что они разговаривают с настоящим контроллером XBox, когда это не так)
  • Старые драйверы Wiimote, такие как cwiid , были необходимы до того, как кто-то наконец-то напишет драйвер Wiimote для ядра, поэтому поддержка будет доступна по умолчанию.

C. Для общих символьных устройств есть CUSE (Символьные устройства в USErspace). Это гораздо менее популярно, хотя.

Единственный пользователь CuSe API , что я лично в курсе, та же программа , что побудило его создание: osspd , который реализует /dev/dsp, /dev/adspи /dev/mixer(аудио API OSS) в пользовательском пространстве , таким образом они могут быть проложены через PulseAudio или DMIX.

Единственная привязка CUSE , которую мне удалось найти, это cusepy , которая не обновлялась с 2010 года.

D. Вам может вообще не понадобиться новый специальный файл.

Например, вы можете открыть необработанную связь с любым USB-устройством, используя libusb (Список привязок на странице), а затем обмениваться данными с другими программами через какой-то другой механизм (сокеты TCP / UDP, чтение / запись stdin / stdout или обычные файлы на диске). , и т.д.).

ssokolow
источник
1
Возможно, cusepy давно не обновлялся (на самом деле он никогда не обновлялся; у него только один коммит!), но, просто написав символьное устройство с использованием cusepy несколько недель назад, я могу подтвердить, что он по-прежнему работает нормально. В нем отсутствовало несколько функций, связанных с реализацией poll, но поскольку cusepy использует ctypes, а привязки генерируются автоматически на основе заголовочных файлов C, исправление любых отсутствующих функций - это просто добавление нужного имени функции в список экспортируемых функций в setup.py.
Алекси Торхамо
1
Еще один интересный пример использования FUSE - это sshfs . Это позволяет вам просматривать удаленную файловую систему, как если бы она была локальной, используя SSH-соединение под ней.
Мистер Бессмертный
@ Mr.Deathless Да. Я действительно использовал это и хотел упомянуть об этом, но я забыл.
Ssokolow
6

В книге « Драйверы устройств Linux» (настоятельно рекомендуется) это подробно объясняется, и даже вам нужно создать модуль ядра, который делает это в качестве примера, но в двух словах, каждый драйвер устройства имеет определенные функции, которые вызываются при открытии, закрытии файла. , читать, писать и т. д. «Специальные» файлы просто делают что-то особенное внутри этих функций, а не обращаются к оборудованию хранения на диске.

Например, функция write для /dev/nulljust ничего не делает, игнорируя байты. Функция чтения для /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- code-of-a-linux-kernel-module / 45531867 # 45531867 и сводится к device_createвызову.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
источник
В OpenBSD есть скрипт MAKEDEV, который немного упрощает это, см. Man.openbsd.org/MAKEDEV.8 Не уверен, почему в Linux его нет, за исключением того, что он намного сложнее. Может быть, части могут быть адаптированы. Вы можете сказать MKNOD tty, например, и он обрабатывает детали.
Алан Кори