Каким образом трубопроводы ограничивают использование памяти?

Брайан Керниган объясняет в это видео раннее привлечение Bell Labs к маленьким языкам /программы, основанные на ограничениях памяти

  

Большая машина была бы 64 k-байта - K, а не M или G, и это означало, что любая отдельная программа не может быть очень большой, и поэтому существует естественная тенденция писать небольшие программы, а затем трубчатый механизм, в основном перенаправление входного выхода, позволил связать одну программу с другой.

Но я не понимаю, как это может ограничить использование памяти, учитывая тот факт, что данные должны храниться в ОЗУ для передачи между программами.

Из Wikipedia :

  

В большинстве Unix-подобных систем все процессы конвейера запускаются в то же время [акцент мой] , при этом их потоки соответствующим образом подключаются и управляются планировщиком вместе со всеми другими процессами, запущенными на машине. Важным аспектом этого является то, что установка Unix-труб, отличных от других реализаций труб, представляет собой концепцию буферизации: например, программа отправки может выдавать 5000 байт в секунду, а принимающая программа может принимать только 100 байт в секунду, но нет данные теряются. Вместо этого вывод программы отправки сохраняется в буфере. Когда приемная программа готова к чтению данных, следующая программа в конвейере считывает из буфера. В Linux размер буфера составляет 65536 байт (64 КБ). Фильтр сторонних производителей с открытым исходным кодом, называемый bfr, доступен для обеспечения больших буферов, если это необходимо.

Это меня смущает еще больше, так как это полностью нарушает цель небольших программ (хотя они будут модульными до определенного масштаба).

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

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

Пример:

sed 'simplesubstitution' file | sort | uniq > file2

Мне ясно, что sed читает в файле и выплескивает его по строке за строкой. Но sort, как указано в BK в связанном видео, является полной остановкой, поэтому все данные должны быть считаны в память ( или он?), затем он переходит к uniq, который (на мой взгляд) был бы однострочным . Но между первым и вторым каналом все данные должны быть в памяти, не?

35 голосов | спросил malan 20 J0000006Europe/Moscow 2018, 16:40:55

3 ответа


43

Данные не нужно хранить в ОЗУ. Трубы блокируют своих авторов, если читателей нет или они не могут идти в ногу; под Linux (и большинство других реализаций, я думаю) есть некоторая буферизация, но это не требуется. Как упоминалось mtraceur и JdeBP (см. ответ последнего ), ранние версии буферизированных труб Unix на диск, и именно так они помогли ограничить использование памяти: конвейер обработки можно разделить на небольшие программы, каждый из которых обработает некоторые данные в пределах дисковых буферов. Маленькие программы занимают меньше памяти, а использование труб означает, что обработка может быть сериализована: первая программа будет запускаться, заполнять свой выходной буфер, приостанавливаться, затем вторая программа будет планироваться, обрабатывать буфер и т. Д. Современные системы - это заказы больше, чем ранние системы Unix, и может запускать много труб параллельно; но для огромного количества данных вы все равно видите аналогичный эффект (и варианты такого метода используются для обработки «больших данных»).

В вашем примере

 sed 'simplesubstitution' file | sort | uniq > file2

sed считывает данные из file по мере необходимости, затем записывает его, пока sort готов к чтению; if sort не готов, блоки записи. В действительности данные действительно живут в памяти, но это специфично для sort и sort готов к решению любых проблем (он будет использовать временные файлы, объем данных для сортировки слишком велик).

Вы можете увидеть поведение блокировки, запустив

 strace seq 1000000 -1 1 | (sleep 120; sort -n)

Это создает достаточный объем данных и передает его в процесс, который не готов читать что-либо в течение первых двух минут. Вы увидите несколько операций write, но очень быстро seq остановится и дождитесь истечения двух минут, заблокированных ядром (системный вызов write) ).

ответил Stephen Kitt 20 J0000006Europe/Moscow 2018, 16:46:24
34
  

Но я не понимаю, как это может ограничить использование памяти, учитывая тот факт, что данные должны храниться в ОЗУ для передачи между программами.

Это ваша основная ошибка. Ранние версии Unix не содержали данные о трубах в ОЗУ. Они хранят их на диске. У труб были i-узлы; на дисководе, которое было обозначено устройством . Системный администратор выполнил программу с именем /etc/config, чтобы указать (среди прочего), какой том на каком диске был трубным устройством, какой том был корневое устройство , а устройство .

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

Этот механизм был заменен другими в середине-конце 1980-х годов. SCO XENIX получила «высокопроизводительную систему труб», которая заменила i-узлы встроенными буферами. 4BSD сделал неназванные трубы в socketpairs. AT & T повторно реализует трубы с использованием механизма STREAMS.

И, конечно, программа sort выполнила ограниченный внутренний вид 32KiB блоков ввода (или того меньшего объема памяти, который он мог выделить если 32KiB не был доступен), записывая отсортированные результаты в промежуточные файлы stmX?? в /usr/tmp/, который затем внешне сортируется, чтобы обеспечить окончательный вывод.

Дальнейшее чтение

  • Стив Д. Пате (1996). "Межпроцессного взаимодействия". Внутренние UNIX: практический подход . Addison-Wesley. ISBN 9780201877212.
  • Морис Дж. Бах (1987). «Системные вызовы для файловой системы». Дизайн операционной системы Unix . Prentice-Hall. ISBN 0132017571.
  • Стивен В. Эрхарт (1986). "config (1M)". Руководство для программистов Unix: 3. Средства системного администрирования . Холт, Ринехарт и Уинстон. ISBN 0030093139. С. 23-28.
ответил JdeBP 20 J0000006Europe/Moscow 2018, 18:27:34
1

Вы частично правы, но только случайно .

В вашем примере все данные действительно должны быть прочитаны «между» трубами, но он не должен быть резидентным в памяти (включая виртуальную память). Обычные реализации sort могут сортировать наборы данных, которые не будут вписываться в ОЗУ, делая частичные сортировки для tempfiles и слияния. Тем не менее, это тот факт, что вы не можете выводить отсортированную последовательность перед чтением каждого элемента. Это довольно очевидно. Так что да, sort может только начать вывод во второй канал после чтения (и сделал все, возможно, частично сортировку tempfiles) все, начиная с первого , Но он не обязательно должен хранить все в ОЗУ.

Однако это не имеет никакого отношения к тому, как работают трубы. Трубы могут быть названы (традиционно все они названы), что означает не более того, и не что иное, как расположение в файловой системе, например файлы. И это как раз то, что когда-то было в трубках, файлы (с записью объединились так же, как и доступность физической памяти, в качестве оптимизации).

В настоящее время трубы представляют собой небольшой буфер ядра конечного размера, на который копируются данные, по крайней мере, это то, что происходит концептуально . Если ядро ​​может помочь в этом, копии будут удалены, играя в трюки VM (например, трафик из файла обычно просто делает одну и ту же страницу доступной для другого процесса для чтения, так что это, наконец, только операция чтения, а не две копии, и нет в любом случае необходима дополнительная память, чем уже используется буферным кешем. В некоторых ситуациях вы можете получить 100% нулевую копию тоже. Или что-то очень близкое.

Если трубы маленькие и конечные, то как это может работать для любого неизвестного (возможно большого) количества данных? Это просто: когда ничего больше не подходит, блоки записи до тех пор, пока не появится место.

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

ответил Damon 22 J0000006Europe/Moscow 2018, 15:35:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132