Я пытаюсь понять концепцию специальных файлов в Linux. Тем не менее, иметь специальный файл в /dev
выглядит просто глупо, когда его функция может быть реализована несколькими строками в C, насколько мне известно.
Более того, вы можете использовать его почти таким же образом, то есть использовать null
вместо перенаправления /dev/null
. Есть ли конкретная причина для того, чтобы иметь его в виде файла? Разве создание файла не вызывает много других проблем, таких как слишком много программ, обращающихся к одному и тому же файлу?
files
devices
executable
null
Анкур С
источник
источник
cat foo | bar
гораздо хуже (в масштабе), чемbar <foo
.cat
это тривиальная программа, но даже тривиальная программа создает затраты (некоторые из них специфичны для семантики FIFO - потому что программы не могут бытьseek()
внутри FIFO, например, программа, которая может быть эффективно реализована с помощью поиска, может в итоге выполнять гораздо более дорогие операции когда передается конвейер; с помощью символьного устройства,/dev/null
которое может имитировать эти операции, или с помощью реального файла, он может их реализовывать, но FIFO не допускает какой-либо контекстно-зависимой обработки).grep blablubb file.txt 2>/dev/null && dosomething
не может работать с нулем, являющимся программой или функцией.read
функции for/dev/null
состоит из «return 0» (что означает, что она ничего не делает и, я полагаю, приводит к EOF): (Из статического github.com/torvalds/linux/blob/master/ drivers / char / mem.c )ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; }
(О, я просто вижу, что @JdeBP уже высказал эту мысль. В любом случае, вот иллюстрация :-).Ответы:
В дополнение к преимуществам производительности при использовании специального устройства для персонажей, основным преимуществом является модульность . / dev / null может использоваться практически в любом контексте, где ожидается файл, а не только в конвейерах оболочки. Рассмотрим программы, которые принимают файлы в качестве параметров командной строки.
Это все случаи, когда использование программы в качестве источника или приемника было бы чрезвычайно громоздким. Даже в случае конвейера оболочки stdout и stderr могут быть перенаправлены на файлы независимо, что трудно сделать с исполняемыми файлами в качестве приемников:
источник
/dev/null
только в командах оболочки. Вы можете использовать его в других параметрах, предоставляемых программному обеспечению - например, в файлах конфигурации. --- Для программного обеспечения это очень удобно. Не нужно делать различие между/dev/null
обычным файлом.pipe
,fork
и ,execve
как и любой другой технологический трубопровод, только с изменениями вdup2
вызовах , которые устанавливают соединения, не так ли? Это правда, что большинство оболочек не предлагают самых привлекательных способов сделать это, но, по-видимому, если бы у нас не было такого большого количества шаблонов «устройство-файл» и большинство вещей/dev
и/proc
рассматривались как исполняемые файлы, оболочки были бы разработаны с использованием способов сделать это так же легко, как мы перенаправить сейчас.wait4
! Вы правы, конечно, можно передавать stdout и stderr различным программам, используя POSIX apis, и возможно придумать умный синтаксис оболочки для перенаправления stdout и stderr различным командам. Тем не менее, я не знаю ни о какой такой оболочке в данный момент, и более важным является то, что / dev / null аккуратно вписывается в существующий инструментарий (который в основном работает с файлами), а / bin / null - нет. Мы могли бы также представить некоторый IO API, который позволяет (безопасно!) Выводить gcc на программу, как на файл, но это не та ситуация, в которой мы находимсяgrep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile)
результат, обрабатывается отдельноerrfile
иoutfile
Честно говоря, это не обычный файл сам по себе; это специальное устройство персонажа :
Он функционирует как устройство, а не как файл или программа, что означает более простую операцию перенаправления ввода или вывода из него, поскольку он может быть присоединен к любому дескриптору файла, включая стандартный ввод / вывод / ошибку.
источник
cat file | null
будет иметь много накладных расходов, в первую очередь при настройке канала, порождении процесса, выполнении «null» в новом процессе и т. д. Кроме того,null
сам по себе будет использовать довольно много CPU в цикле чтения байтов в буфер, который будет позже просто отбрасывается ... Реализация/dev/null
в ядре просто более эффективна. Кроме того, что если вы хотите передать/dev/null
в качестве аргумента вместо перенаправления? (Вы можете использовать<(...)
в bash, но это даже тяжелее!)null
вместо использования перенаправления/dev/null
, будет ли простой и понятный способ указать оболочке запустить программу, отправляя только ее stderr в ноль?/dev/zero
вместо этого.dd of=-
пишет в файл с именем-
, просто пропуститеof=
для записи в стандартный вывод, так как именно тамdd
пишет по умолчанию. Трубопроводfalse
не будет работать, так какfalse
не читает его stdin, поэтомуdd
будет убит SIGPIPE. Для команды , которая отбрасывает его вход можно использовать ...cat > /dev/null
. Также сравнение, которое, вероятно, не имеет отношения к горлышку бутылки, вероятно, было бы здесь генерацией случайных чисел.dd
и т. Д. Даже не удосуживаются выполнить системный вызов записи, когда обнаруживают, что местом назначения является / dev / null.Я подозреваю, что причина во многом связана с концепцией / дизайном, которые сформировали Unix (и, следовательно, Linux), и вытекающими из этого преимуществами.
Без сомнения, есть немаловажное преимущество в производительности, если не затягивать дополнительный процесс, но я думаю, что это еще не все: в раннем Unix была метафора «все - файл», которая имеет неочевидное, но элегантное преимущество, если вы посмотрите на это с точки зрения системы, а не с точки зрения сценариев оболочки.
Скажем, у вас есть программа
null
командной строки и/dev/null
узел устройства. С точки зрения сценариев оболочки,foo | null
программа на самом деле действительно полезна и удобна ,foo >/dev/null
набирает текст чуть дольше и может показаться странной.Но вот два упражнения:
Давайте реализуем программу ,
null
используя существующие инструменты Unix и/dev/null
- легко:cat >/dev/null
. Готово.Можете ли вы реализовать
/dev/null
с точки зренияnull
?Вы абсолютно правы в том, что код на C, который просто отбрасывает ввод, тривиален, поэтому может быть еще не очевидно, почему полезно иметь виртуальный файл для этой задачи.
Учтите: почти каждый язык программирования уже должен работать с файлами, дескрипторами файлов и путями к файлам, потому что они были частью парадигмы Unix "все является файлом" с самого начала.
Если все, что у вас есть, это программы, которые пишут в stdout, то программе все равно, будете ли вы перенаправлять их в виртуальный файл, который поглощает все записи, или канал в программу, которая поглощает все записи.
Теперь, если у вас есть программы, которые используют пути к файлам для чтения или записи данных (что делает большинство программ) - и вы хотите добавить в эти программы функции «пустой ввод» или «сбросить этот вывод» - ну,
/dev/null
это бесплатно.Обратите внимание, что его элегантность заключается в том, что он уменьшает сложность кода всех участвующих программ - для каждого общего, но особого варианта использования, который ваша система может предоставить в виде «файла» с фактическим «именем файла», ваш код может избежать добавления пользовательских команд -линии и пользовательские пути кода для обработки.
Хорошая разработка программного обеспечения часто зависит от нахождения хороших или «естественных» метафор для абстрагирования некоторого элемента проблемы таким образом, который становится более легким для размышления, но остается гибким , так что вы можете решить в основном тот же диапазон задач более высокого уровня без необходимости тратить время и умственную энергию на постоянное воплощение решений одних и тех же проблем более низкого уровня.
«Все является файлом», по-видимому, является одной из таких метафор для доступа к ресурсам: вы вызываете
open
заданный путь в иерархическом пространстве имен, получаете ссылку (дескриптор файла) на объект, и вы можетеread
иwrite
, и т. Д. На дескрипторы файлов. Ваш stdin / stdout / stderr также является файловым дескриптором, который только что был предварительно открыт для вас. Ваши каналы - это просто файлы и файловые дескрипторы, а перенаправление файлов позволяет вам склеивать все эти части вместе.Unix преуспел так же, как и частично, благодаря тому, как хорошо эти абстракции работали вместе, и
/dev/null
его лучше всего понимать как часть этого целого.PS Стоит взглянуть на версию Unix «все является файлом» и тому подобное,
/dev/null
в качестве первых шагов к более гибкому и мощному обобщению метафоры, которое было реализовано во многих последующих системах.Например, в Unix специальные файловые объекты, подобные тому, которые
/dev/null
должны были быть реализованы в самом ядре, но оказывается, что это достаточно полезно для предоставления функциональности в форме файлов / папок, что с тех пор было создано несколько систем, которые обеспечивают способ для программ сделать это.Одной из первых была операционная система Plan 9, созданная теми же людьми, что и Unix. Позже GNU Hurd сделал нечто подобное со своими «переводчиками». Между тем, Linux в конечном итоге получил FUSE (который теперь распространился и на другие основные системы).
источник
NUL
появиться в каждом каталоге, т.е. все, что вам нужно набрать, это> NUL
.Я думаю, что
/dev/null
это символьное устройство (которое ведет себя как обычный файл) вместо программы по соображениям производительности .Если это будет программа, для нее потребуется загрузка, запуск, планирование, запуск, а затем остановка и выгрузка программы. Простая программа на C, которую вы описываете, конечно же, не потребляет много ресурсов, но я думаю, что она имеет существенное значение при рассмотрении большого количества (скажем, миллионов) действий перенаправления / передачи по трубам, поскольку операции по управлению процессами являются дорогостоящими в больших масштабах, так как они включают переключение контекста.
Другое предположение: для передачи в программу требуется выделение памяти принимающей программой (даже если она отбрасывается непосредственно после этого). Так что, если вы подключитесь к инструменту, у вас будет двойное потребление памяти, один раз в отправляющей программе и снова в принимающей программе.
источник
read
данные). Это немаловажно на одноядерном PDP-11, где был разработан Unix! Пропускная способность / копирование памяти сегодня намного дешевле, чем тогда.write
Системный вызов к FD откроется/dev/null
может вернуться сразу же, даже не читая никаких данных из буфера.null
программе было бы отстой, но большинство многоядерных процессоров не работают все время с занятыми всеми ядрами, поэтому Процессорное время для запускаnull
программы в основном бесплатное. В системе со всеми привязанными ядрами бесполезный трубопровод - большая проблема. Но нет, «горячий» буфер может много раз переписываться без сброса в ОЗУ, поэтому мы в основном конкурируем за пропускную способность L3, а не за кеш. Не очень хорошо, особенно в системе SMT (гиперпоточность), где конкурируют другие логические ядра на одном и том же физическом ...vpaddd zmm
: 16 32-битных элементов на команду.Помимо «все является файлом» и, следовательно, простотой использования везде, на котором основано большинство других ответов, существует также проблема с производительностью, как упоминает @ user5626466.
Чтобы показать на практике, мы создадим простую программу под названием
nullread.c
:и скомпилировать
gcc -O2 -Wall -W nullread.c -o nullread
(Примечание: мы не можем использовать lseek (2) на каналах, поэтому единственный способ опорожнить канал - это читать из него, пока он не опустеет).
тогда как при стандартном
/dev/null
перенаправлении файлов мы получаем гораздо лучшую скорость (из-за упомянутых фактов: меньше переключения контекста, ядро просто игнорирует данные, а не копирует их и т. д.):(это должен быть комментарий, но он слишком велик и совершенно не читается)
источник
dd
буфер + буфер приема не подходит; вы, вероятно, имеете дело с пропускной способностью памяти вместо пропускной способности кэша последнего уровня. Вы могли бы получить лучшую пропускную способность с 512 тыс. Буферов или даже 64 тыс. Буферов. (Поstrace
на моем рабочем столе,write
иread
возвращается 1048576, так что я думаю , что означает , что мы только платим пользователь <->. Стоимость ядра TLB недействительности + ответвление предсказания вровень один раз в МиБ, а не за 6ки, @sourcejedi Это Specter я думаю, что это имеет наибольшую стоимость)syscall
возвращаемого сразу жеENOSYS
составляет ~ 1800 циклов на Skylake с включенным смягчением Spectre, в большинстве случаев этоwrmsr
делает BPU недействительным, согласно тестированию @ BeeOnRope . При отключенном смягчении время обхода пользователя-> ядра-> пользователя составляет ~ 160 циклов. Но если есть трогательные много памяти, Meltdown смягчение является значительным, тоже. Огромные страницы должны помочь (нужно перезагрузить меньшее количество записей TLB).Ваш вопрос задан так, как будто что-то может быть получено, возможно, в простоте с помощью пустой программы вместо файла. Возможно, мы можем избавиться от понятия «магические файлы» и вместо этого иметь просто «обычные каналы».
Но учтите, что труба - это тоже файл . Обычно они не имеют имен, поэтому ими можно манипулировать только через дескрипторы файлов.
Рассмотрим этот несколько надуманный пример:
Используя подстановку процессов Bash, мы можем сделать то же самое более окольным путем:
Замените
grep
наecho
и мы можем увидеть под крышками:<(...)
Конструкция просто заменяется именем файла, и Grep думает , что это открытие любой старый файл, он просто случается , чтобы быть названным/dev/fd/63
. Вот/dev/fd
магический каталог, который создает именованные каналы для каждого дескриптора файла, которым владеет файл, обращающийся к нему.Мы могли бы сделать его менее волшебным,
mkfifo
создав именованный канал, который появляется воls
всем, как обычный файл:В другом месте:
и вот, grep выведет
foo
.Я думаю, что как только вы поймете, что каналы, обычные файлы и специальные файлы, такие как / dev / null, являются просто файлами, становится очевидным, что реализация нулевой программы более сложна. Ядро должно обрабатывать записи в файл в любом случае, но в случае / dev / null оно может просто отбросить записи на пол, тогда как с помощью канала оно должно фактически передавать байты другой программе, которая затем должна на самом деле читать их.
источник
/dev/fd/63
?Я бы сказал, что есть проблема безопасности, выходящая за рамки исторических парадигм и производительности. Ограничение числа программ с привилегированными учетными данными для выполнения, какими бы простыми они ни были, является фундаментальным принципом безопасности системы. Замена
/dev/null
, безусловно, потребовалась бы, чтобы иметь такие привилегии из-за использования системными службами. Современные системы безопасности отлично справляются с задачей предотвращения атак, но они не являются надежными. Управляемое ядром устройство, доступ к которому осуществляется в виде файла, гораздо сложнее использовать.источник
/dev/null
или предлагаемого ввода-отбрасывая программы, вектор атаки будет такой же: получить скрипт или программу , которая работает как корень , чтобы сделать что - то странное (как пытаютсяlseek
в/dev/null
или открытой это несколько раз из одного и того же процесса, или IDK что. Или вызывать/bin/null
со странной средой, или что-то).Как и другие уже отмечалось,
/dev/null
является программа сделана из нескольких строк кода. Просто эти строки кода являются частью ядра.Чтобы сделать это более понятным, вот реализация Linux: символьное устройство вызывает функции при чтении или записи. Запись на
/dev/null
вызовы write_null , а на чтение вызовов read_null , зарегистрированных здесь .Буквально несколько строк кода: эти функции ничего не делают. Вам понадобится больше строк кода, чем пальцев на руках, только если вы считаете функции, отличные от чтения и записи.
источник
Я надеюсь, что вы также знаете о / dev / chargen / dev / zero и других подобных им, включая / dev / null.
В LINUX / UNIX есть несколько из них - они доступны для того, чтобы люди могли эффективно использовать ФРАГМЕНТЫ ХОРОШОГО ПИСЬМЕННОГО КОДА.
Chargen предназначен для генерации определенного и повторяющегося набора символов - он достаточно быстрый и будет расширять границы последовательных устройств, а также поможет отлаживать последовательные протоколы, которые были написаны, и не пройдут тот или иной тест.
Ноль предназначен для заполнения существующего файла или вывода большого количества нулей
/ dev / null - это просто еще один инструмент с той же идеей.
Все эти инструменты в вашем наборе инструментов означают, что у вас есть шанс на то, чтобы заставить существующую программу сделать что-то уникальное, независимо от их (вашей конкретной потребности) в качестве устройства или замены файла.
Давайте устроим конкурс, чтобы узнать, кто может дать самый захватывающий результат, учитывая только несколько символических устройств в вашей версии LINUX.
источник