Почему / dev / null - это файл? Почему его функция не реализована как простая программа?

114

Я пытаюсь понять концепцию специальных файлов в Linux. Тем не менее, иметь специальный файл в /devвыглядит просто глупо, когда его функция может быть реализована несколькими строками в C, насколько мне известно.

Более того, вы можете использовать его почти таким же образом, то есть использовать nullвместо перенаправления /dev/null. Есть ли конкретная причина для того, чтобы иметь его в виде файла? Разве создание файла не вызывает много других проблем, таких как слишком много программ, обращающихся к одному и тому же файлу?

Анкур С
источник
20
Между прочим, большая часть этих накладных расходов также почему-то cat foo | barгораздо хуже (в масштабе), чем bar <foo. catэто тривиальная программа, но даже тривиальная программа создает затраты (некоторые из них специфичны для семантики FIFO - потому что программы не могут быть seek()внутри FIFO, например, программа, которая может быть эффективно реализована с помощью поиска, может в итоге выполнять гораздо более дорогие операции когда передается конвейер; с помощью символьного устройства, /dev/nullкоторое может имитировать эти операции, или с помощью реального файла, он может их реализовывать, но FIFO не допускает какой-либо контекстно-зависимой обработки).
Чарльз Даффи
13
grep blablubb file.txt 2>/dev/null && dosomethingне может работать с нулем, являющимся программой или функцией.
rexkogitans
16
Возможно, вам будет полезно (или, по крайней мере, расширить кругозор) прочитать об операционной системе Plan 9, чтобы увидеть, куда движется концепция «все - файл» - становится немного легче увидеть силу наличия ресурсов в виде файла. пути, как только вы увидите систему, полностью охватывающую концепцию (а не в основном / частично, как это делают современные Linux / Unix).
mtraceur
25
Так же как никто не указал, что драйвер устройства, работающий в пространстве ядра, является программой с «горсткой строк C» , ни один из ответов до сих пор фактически не рассматривал предположение о «слишком большом количестве программ, обращающихся к одному и тому же файлу». в вопросе.
JdeBP
12
Re «ее функция может быть реализована с помощью нескольких строк в C»: Вы не поверите, но это будет реализовано на нескольких строк в C! Например, тело 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 уже высказал эту мысль. В любом случае, вот иллюстрация :-).
Питер А. Шнайдер

Ответы:

153

В дополнение к преимуществам производительности при использовании специального устройства для персонажей, основным преимуществом является модульность . / dev / null может использоваться практически в любом контексте, где ожидается файл, а не только в конвейерах оболочки. Рассмотрим программы, которые принимают файлы в качестве параметров командной строки.

# We don't care about log output.
$ frobify --log-file=/dev/null

# We are not interested in the compiled binary, just seeing if there are errors.
$ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".

# Easy way to force an empty list of exceptions.
$ start_firewall --exception_list=/dev/null

Это все случаи, когда использование программы в качестве источника или приемника было бы чрезвычайно громоздким. Даже в случае конвейера оболочки stdout и stderr могут быть перенаправлены на файлы независимо, что трудно сделать с исполняемыми файлами в качестве приемников:

# Suppress errors, but print output.
$ grep foo * 2>/dev/null
IOCTL
источник
14
Также вы не используете /dev/nullтолько в командах оболочки. Вы можете использовать его в других параметрах, предоставляемых программному обеспечению - например, в файлах конфигурации. --- Для программного обеспечения это очень удобно. Не нужно делать различие между /dev/nullобычным файлом.
Пабук
Я не уверен, что понимаю часть о том, что отдельные перенаправления для выполнения исполняемых файлов трудны. В C, вы просто сделать pipe, forkи , execveкак и любой другой технологический трубопровод, только с изменениями в dup2вызовах , которые устанавливают соединения, не так ли? Это правда, что большинство оболочек не предлагают самых привлекательных способов сделать это, но, по-видимому, если бы у нас не было такого большого количества шаблонов «устройство-файл» и большинство вещей /devи /procрассматривались как исполняемые файлы, оболочки были бы разработаны с использованием способов сделать это так же легко, как мы перенаправить сейчас.
aschepler
6
@aschepler Дело в том, что трудно не перенаправить на исполняемые файлы. Дело в том, что написание приложений, которые могут записывать / читать из обоих файлов и нулевого приемника, было бы более сложным, если бы нулевой приемник не был файлом. Разве вы не говорите о мире, где вместо того, чтобы быть файлом, все является исполняемым файлом? Это была бы модель, отличная от той, что есть в * nix OS.
Куб
1
@aschepler Вы забыли wait4! Вы правы, конечно, можно передавать stdout и stderr различным программам, используя POSIX apis, и возможно придумать умный синтаксис оболочки для перенаправления stdout и stderr различным командам. Тем не менее, я не знаю ни о какой такой оболочке в данный момент, и более важным является то, что / dev / null аккуратно вписывается в существующий инструментарий (который в основном работает с файлами), а / bin / null - нет. Мы могли бы также представить некоторый IO API, который позволяет (безопасно!) Выводить gcc на программу, как на файл, но это не та ситуация, в которой мы находимся
ioctl
2
@ioctl относительно раковин; как zsh, так и bash, по крайней мере, позволят вам делать такие вещи, как grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile)результат, обрабатывается отдельно errfileиoutfile
Matija Nalis
62

Честно говоря, это не обычный файл сам по себе; это специальное устройство персонажа :

$ file /dev/null
/dev/null: character special (3/2)

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

DopeGhoti
источник
24
cat file | nullбудет иметь много накладных расходов, в первую очередь при настройке канала, порождении процесса, выполнении «null» в новом процессе и т. д. Кроме того, nullсам по себе будет использовать довольно много CPU в цикле чтения байтов в буфер, который будет позже просто отбрасывается ... Реализация /dev/nullв ядре просто более эффективна. Кроме того, что если вы хотите передать /dev/nullв качестве аргумента вместо перенаправления? (Вы можете использовать <(...)в bash, но это даже тяжелее!)
filbranden
4
Если бы вам пришлось передавать по конвейеру программу с именем nullвместо использования перенаправления /dev/null, будет ли простой и понятный способ указать оболочке запустить программу, отправляя только ее stderr в ноль?
Марк Плотник
5
Это действительно дорогая установка для демонстрации накладных расходов. Я бы предложил использовать /dev/zeroвместо этого.
Хрилис - на забастовке -
20
Эти примеры неверны. dd of=-пишет в файл с именем -, просто пропустите of=для записи в стандартный вывод, так как именно там ddпишет по умолчанию. Трубопровод falseне будет работать, так как falseне читает его stdin, поэтому ddбудет убит SIGPIPE. Для команды , которая отбрасывает его вход можно использовать ... cat > /dev/null. Также сравнение, которое, вероятно, не имеет отношения к горлышку бутылки, вероятно, было бы здесь генерацией случайных чисел.
Стефан Шазелас
8
Версии AST ddи т. Д. Даже не удосуживаются выполнить системный вызов записи, когда обнаруживают, что местом назначения является / dev / null.
Марк Плотник
54

Я подозреваю, что причина во многом связана с концепцией / дизайном, которые сформировали Unix (и, следовательно, Linux), и вытекающими из этого преимуществами.

Без сомнения, есть немаловажное преимущество в производительности, если не затягивать дополнительный процесс, но я думаю, что это еще не все: в раннем Unix была метафора «все - файл», которая имеет неочевидное, но элегантное преимущество, если вы посмотрите на это с точки зрения системы, а не с точки зрения сценариев оболочки.

Скажем, у вас есть программа nullкомандной строки и /dev/nullузел устройства. С точки зрения сценариев оболочки, foo | nullпрограмма на самом деле действительно полезна и удобна , foo >/dev/nullнабирает текст чуть дольше и может показаться странной.

Но вот два упражнения:

  1. Давайте реализуем программу , nullиспользуя существующие инструменты Unix и /dev/null- легко: cat >/dev/null. Готово.

  2. Можете ли вы реализовать /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 (который теперь распространился и на другие основные системы).

mtraceur
источник
8
@PeterCordes смысл ответа начинается с позиции непонимания дизайна. Если бы все уже поняли дизайн, этого вопроса не было бы.
OrangeDog
1
@mtraceur: монтировать файл изображения без прав root? показывает некоторые доказательства того, что FUSE может не требовать root, но я не уверен.
Питер Кордес
1
@PeterCordes RE: «кажется странным»: это не извинение за дизайн, а просто признание того, как это может выглядеть, если вы не думаете о реализации системы под ним, и у вас еще не было момента эврика о системе широкие дизайнерские преимущества. Я попытался прояснить это, открыв это предложение «с точки зрения сценариев оболочки» и ссылаясь на контраст между этим и системной перспективой несколькими предложениями ранее. Если подумать "лучше может показаться странным", я подправлю это к этому. Я приветствую дальнейшие предложения по формулировке, чтобы сделать его более понятным, не делая его слишком многословным.
mtraceur
2
Самым первым, что мне сказали, будучи молодым инженером в связи с Unix, было «Все - файл», и я клянусь, вы слышали столицы. И раннее освоение этой идеи делает Unix / Linux более простым для понимания. Linux унаследовал большую часть этой философии дизайна. Я рад, что кто-то упомянул об этом.
StephenG
2
@PeterCordes, DOS «решила» проблему с печатанием, заставив волшебное имя файла NULпоявиться в каждом каталоге, т.е. все, что вам нужно набрать, это > NUL.
Кристиан Чиупиту
15

Я думаю, что /dev/nullэто символьное устройство (которое ведет себя как обычный файл) вместо программы по соображениям производительности .

Если это будет программа, для нее потребуется загрузка, запуск, планирование, запуск, а затем остановка и выгрузка программы. Простая программа на C, которую вы описываете, конечно же, не потребляет много ресурсов, но я думаю, что она имеет существенное значение при рассмотрении большого количества (скажем, миллионов) действий перенаправления / передачи по трубам, поскольку операции по управлению процессами являются дорогостоящими в больших масштабах, так как они включают переключение контекста.

Другое предположение: для передачи в программу требуется выделение памяти принимающей программой (даже если она отбрасывается непосредственно после этого). Так что, если вы подключитесь к инструменту, у вас будет двойное потребление памяти, один раз в отправляющей программе и снова в принимающей программе.

user5626466
источник
10
Это не просто стоимость установки, это то, что каждая запись в канал требует копирования памяти и переключения контекста на чтение программы. (Или, по крайней мере, переключение контекста, когда буфер канала заполнен. И читатель должен сделать еще одну копию, когда это readданные). Это немаловажно на одноядерном PDP-11, где был разработан Unix! Пропускная способность / копирование памяти сегодня намного дешевле, чем тогда. writeСистемный вызов к FD откроется /dev/nullможет вернуться сразу же, даже не читая никаких данных из буфера.
Питер Кордес
@PeterCordes, моя заметка является тангенциальной, но возможно, что, как ни парадоксально, сегодня записи в память стоят дороже, чем когда-либо. 8-ядерный ЦП потенциально выполняет 16 целочисленных операций за такт, в то время как сквозная запись в память будет завершена, например, за 16 тактов (4 ГГц ЦП, 250 МГц ОЗУ). Это фактор 256. ОЗУ для современного ЦП - это как RL02 для ЦП PDP-11, почти как периферийный накопитель! :) Естественно, не так просто, но все попадание в кеш будет записано, а бесполезные записи лишат другие вычисления когда-либо важного пространства кеша.
ккм
@kkm: Да, тратить около 2x128 КБ места в кэш-памяти L3 на конвейерном буфере в ядре и буфере чтения в nullпрограмме было бы отстой, но большинство многоядерных процессоров не работают все время с занятыми всеми ядрами, поэтому Процессорное время для запуска nullпрограммы в основном бесплатное. В системе со всеми привязанными ядрами бесполезный трубопровод - большая проблема. Но нет, «горячий» буфер может много раз переписываться без сброса в ОЗУ, поэтому мы в основном конкурируем за пропускную способность L3, а не за кеш. Не очень хорошо, особенно в системе SMT (гиперпоточность), где конкурируют другие логические ядра на одном и том же физическом ...
Питер Кордес
.... Но ваш расчет памяти очень испорчен. Современные процессоры имеют большой параллелизм памяти, поэтому, хотя задержка DRAM составляет 200-400 тактовых тактов ядра и L3> 40, пропускная способность составляет ~ 8 байт / такт. (Удивительно, что однопоточная полоса пропускания до L3 или DRAM хуже на многоядерном Xeon с четырехканальной памятью по сравнению с четырехъядерным рабочим столом, поскольку она ограничена максимальным параллелизмом запросов, которые одно ядро ​​может поддерживать в полете. Bandwidth = max_concurrency / latency: почему Skylake намного лучше, чем Broadwell-E, для однопоточной пропускной способности памяти? )
Питер Кордес
... См. Также 7-cpu.com/cpu/Haswell.html для чисел Haswell, сравнивающих четырехъядерные и 18-ядерные. В любом случае, да, современные процессоры могут выполнять смехотворную работу за такт, если они не застряли в ожидании памяти. Похоже, ваши цифры составляют всего 2 операции ALU за такт, как, например, Pentium 1993 года или современный бюджетный ARM двойного выпуска. Ryzen или Haswell потенциально могут выполнять 4 скалярных целочисленных операции ALU + 2 операции памяти на ядро за такт или намного больше с SIMD. Например, Skylake-AVX512 имеет (на ядро) пропускную способность 2 на такт vpaddd zmm: 16 32-битных элементов на команду.
Питер Кордес
7

Помимо «все является файлом» и, следовательно, простотой использования везде, на котором основано большинство других ответов, существует также проблема с производительностью, как упоминает @ user5626466.

Чтобы показать на практике, мы создадим простую программу под названием nullread.c:

#include <unistd.h>
char buf[1024*1024];
int main() {
        while (read(0, buf, sizeof(buf)) > 0);
}

и скомпилировать gcc -O2 -Wall -W nullread.c -o nullread

(Примечание: мы не можем использовать lseek (2) на каналах, поэтому единственный способ опорожнить канал - это читать из него, пока он не опустеет).

% time dd if=/dev/zero bs=1M count=5000 |  ./nullread
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
./nullread  0,02s user 3,90s system 41% cpu 9,337 total

тогда как при стандартном /dev/nullперенаправлении файлов мы получаем гораздо лучшую скорость (из-за упомянутых фактов: меньше переключения контекста, ядро ​​просто игнорирует данные, а не копирует их и т. д.):

% time dd if=/dev/zero bs=1M count=5000 > /dev/null
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total

(это должен быть комментарий, но он слишком велик и совершенно не читается)

Матия Налис
источник
На каком оборудовании вы тестировали? 4,8 ГБ / с - это довольно мало по сравнению с 23 ГБ / с, которые я получаю на Skylake i7-6700k (DDR4-2666, но буфер должен оставаться горячим в кеше L3. Таким образом, значительная часть затрат приходится на системные вызовы, которые стоят дорого с Spectre + Включено смягчение распада. Это вдвойне больно для трубопроводов, потому что конвейерные буферы меньше 1М, так что это больше системных вызовов записи / чтения. Хотя почти 10-кратная разница в производительности хуже, чем я ожидал. В моей системе Skylake это 23 ГБ / с против 3.3GB / s , работает x86-64 Linux 4.15.8-1-ARCH, так что фактор 6.8 Wow, системные вызовы. дорогостоящие сейчас)
Питер Кордес
1
@PeterCordes 3Гб / с с 64k конвейерными буферами предлагает 2x 103124 системных вызовов в секунду ... и это количество переключений контекста, хе. На серверном процессоре с 200000 системных вызовов в секунду можно ожидать ~ 8% накладных расходов от PTI, так как очень мало рабочего набора. (График, на который я ссылаюсь, не включает оптимизацию PCID, но, возможно, это не так важно для небольших рабочих наборов). Так что я не уверен, что PTI имеет большое влияние там? brendangregg.com/blog/2018-02-09/…
sourcejedi
1
О, интересно, так что это Silvermont с 2 МБ кэш-памяти L2 , поэтому ваш ddбуфер + буфер приема не подходит; вы, вероятно, имеете дело с пропускной способностью памяти вместо пропускной способности кэша последнего уровня. Вы могли бы получить лучшую пропускную способность с 512 тыс. Буферов или даже 64 тыс. Буферов. (По straceна моем рабочем столе, writeи readвозвращается 1048576, так что я думаю , что означает , что мы только платим пользователь <->. Стоимость ядра TLB недействительности + ответвление предсказания вровень один раз в МиБ, а не за 6ки, @sourcejedi Это Specter я думаю, что это имеет наибольшую стоимость)
Питер Кордес
1
@sourcejedi: При включенном смягчении Spectre стоимость syscallвозвращаемого сразу же ENOSYSсоставляет ~ 1800 циклов на Skylake с включенным смягчением Spectre, в большинстве случаев это wrmsrделает BPU недействительным, согласно тестированию @ BeeOnRope . При отключенном смягчении время обхода пользователя-> ядра-> пользователя составляет ~ 160 циклов. Но если есть трогательные много памяти, Meltdown смягчение является значительным, тоже. Огромные страницы должны помочь (нужно перезагрузить меньшее количество записей TLB).
Питер Кордес
1
@PeterCordes В одноядерной Unix-системе мы бы наверняка увидели 1 переключение контекста на 64 КБ или любой другой буфер канала, и это повредило бы ... на самом деле я также вижу [такое же количество переключателей контекста с двумя ядрами процессора] ( unix.stackexchange.com/questions/439260/… ); он также должен считать цикл сна / бодрствования для каждой 64k в качестве переключателя контекста (для номинального «холостого процесса»). Сохранение конвейерных процессов на одном и том же процессоре работало в два раза быстрее.
sourcejedi
6

Ваш вопрос задан так, как будто что-то может быть получено, возможно, в простоте с помощью пустой программы вместо файла. Возможно, мы можем избавиться от понятия «магические файлы» и вместо этого иметь просто «обычные каналы».

Но учтите, что труба - это тоже файл . Обычно они не имеют имен, поэтому ими можно манипулировать только через дескрипторы файлов.

Рассмотрим этот несколько надуманный пример:

$ echo -e 'foo\nbar\nbaz' | grep foo
foo

Используя подстановку процессов Bash, мы можем сделать то же самое более окольным путем:

$ grep foo <(echo -e 'foo\nbar\nbaz')
foo

Замените grepна echoи мы можем увидеть под крышками:

$ echo foo <(echo -e 'foo\nbar\nbaz')
foo /dev/fd/63

<(...)Конструкция просто заменяется именем файла, и Grep думает , что это открытие любой старый файл, он просто случается , чтобы быть названным /dev/fd/63. Вот /dev/fdмагический каталог, который создает именованные каналы для каждого дескриптора файла, которым владеет файл, обращающийся к нему.

Мы могли бы сделать его менее волшебным, mkfifoсоздав именованный канал, который появляется во lsвсем, как обычный файл:

$ mkfifo foofifo
$ ls -l foofifo 
prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
$ grep foo foofifo

В другом месте:

$ echo -e 'foo\nbar\nbaz' > foofifo

и вот, grep выведет foo.

Я думаю, что как только вы поймете, что каналы, обычные файлы и специальные файлы, такие как / dev / null, являются просто файлами, становится очевидным, что реализация нулевой программы более сложна. Ядро должно обрабатывать записи в файл в любом случае, но в случае / dev / null оно может просто отбросить записи на пол, тогда как с помощью канала оно должно фактически передавать байты другой программе, которая затем должна на самом деле читать их.

Фил Фрост
источник
@ Лайл Да? Тогда почему эхо-печать /dev/fd/63?
Фил Фрост
Гектометр Хорошая точка зрения. Ну, это реализовано оболочками, так что, возможно, ваша оболочка отличается от старой оболочки Bourne, с которой я вырос.
Лайл
Единственное отличие состоит в том, что echo не читает из stdin, в то время как grep делает, но я не могу думать, как оболочка узнает об этом до того, как выполнит их.
Лайл
1
И Strace делает это более ясным: для меня. у вас все правильно, с bash. Конструкция '<(...)' делает нечто совершенно отличное от <filename. Гектометр Я кое-что узнал.
Лайл
0

Я бы сказал, что есть проблема безопасности, выходящая за рамки исторических парадигм и производительности. Ограничение числа программ с привилегированными учетными данными для выполнения, какими бы простыми они ни были, является фундаментальным принципом безопасности системы. Замена /dev/null, безусловно, потребовалась бы, чтобы иметь такие привилегии из-за использования системными службами. Современные системы безопасности отлично справляются с задачей предотвращения атак, но они не являются надежными. Управляемое ядром устройство, доступ к которому осуществляется в виде файла, гораздо сложнее использовать.

NickW
источник
Это звучит как чепуха. Написание безошибочного драйвера ядра не проще, чем безошибочная программа, которая читает + отбрасывает свой стандартный ввод. Он не должен быть УИП или что - нибудь, так как для /dev/nullили предлагаемого ввода-отбрасывая программы, вектор атаки будет такой же: получить скрипт или программу , которая работает как корень , чтобы сделать что - то странное (как пытаются lseekв /dev/nullили открытой это несколько раз из одного и того же процесса, или IDK что. Или вызывать /bin/nullсо странной средой, или что-то).
Питер Кордес
0

Как и другие уже отмечалось, /dev/null является программа сделана из нескольких строк кода. Просто эти строки кода являются частью ядра.

Чтобы сделать это более понятным, вот реализация Linux: символьное устройство вызывает функции при чтении или записи. Запись на /dev/nullвызовы write_null , а на чтение вызовов read_null , зарегистрированных здесь .

Буквально несколько строк кода: эти функции ничего не делают. Вам понадобится больше строк кода, чем пальцев на руках, только если вы считаете функции, отличные от чтения и записи.

Матье Мой
источник
Возможно, мне следовало бы сформулировать это более точно. Я имел в виду, зачем реализовывать его как символьное устройство вместо программы. В любом случае это будет несколько строк, но реализация программы будет значительно проще. Как указывалось в других ответах, в этом есть немало преимуществ; эффективность и мобильность в их числе.
Ankur S
Конечно. Я только добавил этот ответ, потому что видеть фактическую реализацию было забавно (я обнаружил это недавно самостоятельно), но настоящая причина - то, что действительно указали другие.
Матье Мой
Я тоже! Недавно я начал изучать устройства в Linux, и ответы были довольно информативными
Ankur S
-2

Я надеюсь, что вы также знаете о / dev / chargen / dev / zero и других подобных им, включая / dev / null.

В LINUX / UNIX есть несколько из них - они доступны для того, чтобы люди могли эффективно использовать ФРАГМЕНТЫ ХОРОШОГО ПИСЬМЕННОГО КОДА.

Chargen предназначен для генерации определенного и повторяющегося набора символов - он достаточно быстрый и будет расширять границы последовательных устройств, а также поможет отлаживать последовательные протоколы, которые были написаны, и не пройдут тот или иной тест.

Ноль предназначен для заполнения существующего файла или вывода большого количества нулей

/ dev / null - это просто еще один инструмент с той же идеей.

Все эти инструменты в вашем наборе инструментов означают, что у вас есть шанс на то, чтобы заставить существующую программу сделать что-то уникальное, независимо от их (вашей конкретной потребности) в качестве устройства или замены файла.

Давайте устроим конкурс, чтобы узнать, кто может дать самый захватывающий результат, учитывая только несколько символических устройств в вашей версии LINUX.

полезный
источник