Брайан Керниган объясняет в этом видео, что ранние исследования Bell Labs по отношению к небольшим языкам / программам основаны на ограничении памяти
Большая машина была бы 64 кбайт - K, а не M или G - и это означало, что любая отдельная программа не могла быть очень большой, и поэтому существовала естественная тенденция писать маленькие программы, а затем конвейерный механизм, в основном перенаправление ввода-вывода, позволяющее связать одну программу с другой.
Но я не понимаю, как это может ограничить использование памяти, учитывая тот факт, что данные должны храниться в оперативной памяти для передачи между программами.
Из Википедии :
В большинстве Unix-подобных систем все процессы конвейера запускаются одновременно [выделено мной]с соответствующими потоками, которые управляются планировщиком вместе со всеми другими процессами, запущенными на машине. Важным аспектом этого, отделяющего каналы Unix от других реализаций канала, является концепция буферизации: например, отправляющая программа может производить 5000 байтов в секунду, а принимающая программа может быть способна принимать только 100 байтов в секунду, но не данные потеряны. Вместо этого выходные данные отправляющей программы хранятся в буфере. Когда принимающая программа готова к чтению данных, следующая программа в конвейере считывает данные из буфера. В Linux размер буфера составляет 65536 байт (64 КБ). Доступен сторонний фильтр с открытым исходным кодом, называемый bfr, для обеспечения больших буферов при необходимости.
Это смущает меня еще больше, поскольку это полностью противоречит цели небольших программ (хотя они будут модульными до определенного масштаба).
Единственное, что я могу представить в качестве решения моего первого вопроса (ограниченность памяти зависит от размера данных), это то, что большие наборы данных просто не рассчитывались в то время, и реальная проблема, которую должны были решить конвейеры, заключалась в том, что объем памяти, необходимый самим программам. Но учитывая жирный текст в цитате из Википедии, меня это даже смущает: одна программа не реализуется за раз.
Все это имело бы большой смысл, если бы использовались временные файлы, но, насколько я понимаю, каналы не записывают на диск (если не используется swap).
Пример:
sed 'simplesubstitution' file | sort | uniq > file2
Для меня ясно, что sed
чтение в файле и выкладывание его построчно. Но sort
, как утверждает БК в связанном видео, это полная остановка, поэтому все данные должны быть прочитаны в память (или не так ли?), А затем переданы uniq
, что (на мой взгляд) будет одним -линейная программа. Но между первым и вторым каналом все данные должны быть в памяти, нет?
unless swap is used
своп всегда используется, когда не хватает оперативной памятиОтветы:
Данные не должны храниться в оперативной памяти. Трубы блокируют своих писателей, если читателей нет или они не успевают; под Linux (и я думаю, что в большинстве других реализаций) есть некоторая буферизация, но это не обязательно. Как упомянуто mtraceur и JdeBP (см . Ответ последнегоранние версии Unix буферизованных каналов на диск, и вот как они помогли ограничить использование памяти: конвейер обработки можно было бы разбить на небольшие программы, каждая из которых обрабатывала бы некоторые данные в пределах дисковых буферов. Небольшие программы занимают меньше памяти, а использование каналов означает, что обработка может быть сериализована: первая программа запустится, заполнит свой выходной буфер, будет приостановлена, затем вторая программа будет запланирована, обработает буфер и т. Д. Современные системы - это заказы величины больше, чем в ранних системах Unix, и может работать параллельно с несколькими каналами; но для огромных объемов данных вы все равно увидите похожий эффект (и варианты такого рода техники используются для обработки «больших данных»).
В вашем примере
sed
считывает данные поfile
мере необходимости, затем записывает их до тех пор,sort
пока они готовы их прочитать; еслиsort
не готов, блоки записи. В конце концов, данные действительно хранятся в памяти, но они специфичныsort
иsort
готовы решать любые проблемы (они будут использовать временные файлы, если объем данных для сортировки слишком велик).Вы можете увидеть поведение блокировки, запустив
Это создает достаточное количество данных и направляет их процессу, который не готов ничего читать в течение первых двух минут. Вы увидите, как проходит ряд
write
операций, но очень быстроseq
остановится и будет ждать две минуты, заблокированные ядром (write
системный вызов ожидает).источник
Это твоя фундаментальная ошибка. Ранние версии Unix не содержали данные канала в оперативной памяти. Они сохранили их на диске. Трубы имели i-узлы; на дисковом устройстве, которое было обозначено как устройство трубы . Системный администратор запустил программу с именем,
/etc/config
чтобы указать (среди прочего), какой том, на каком диске было устройство канала, какой том был корневым устройством , а какое устройство дампа .Количество ожидающих данных было ограничено тем фактом, что для хранения использовались только прямые блоки i-узла на диске. Этот механизм упростил код, поскольку для чтения из канала использовался почти такой же алгоритм, как и для чтения для обычного файла, с некоторыми изменениями, вызванными тем, что каналы не доступны для поиска, а буфер является круглым.
Этот механизм был заменен другими в середине и конце 1980-х годов. SCO XENIX получила «Высокопроизводительную систему трубопроводов», которая заменила i-узлы буферами в ядре. 4BSD превратил безымянные трубы в пары гнезд. AT & T переопределяет трубы, используя механизм STREAMS.
И, конечно,
sort
программа выполняла ограниченную внутреннюю сортировку 32-килобайтных порций ввода (или любого меньшего объема памяти, который мог бы выделить, если 32-килобайт не было доступно), записывая отсортированные результаты в промежуточныеstmX??
файлы, в/usr/tmp/
которых затем внешне объединялись, сортируя для получения окончательного результата. выход.дальнейшее чтение
config
(1M)". Руководство программиста Unix: 3. Средства системного администрирования . Холт, Райнхарт и Уинстон. ISBN 0030093139. С. 23–28.источник
Вы частично правы, но только случайно .
В вашем примере все данные должны быть действительно прочитаны «между» каналами, но они не обязательно должны находиться в памяти (включая виртуальную память). Обычные реализации
sort
могут сортировать наборы данных, которые не помещаются в ОЗУ, выполняя сортировку партиалов по временным файлам и объединяя их. Тем не менее, это факт, что вы не можете вывести отсортированную последовательность до того, как прочитаете каждый элемент. Это довольно очевидно. Так что да,sort
можно начинать вывод во второй канал только после того, как прочитал (и сделал все что угодно, возможно, частично отсортировав временные файлы) все с первого. Но это не обязательно держать все это в оперативной памяти.Однако это не имеет ничего общего с тем, как работают трубы. Каналы могут быть названы (традиционно они были названы), что означает не что иное, как расположение файлов в файловой системе, например файлы. И это как раз те каналы, которые когда-то были файлами (с записями, объединенными настолько, насколько позволяет физическая память, как оптимизация).
В настоящее время каналы представляют собой небольшой буфер ядра конечного размера, в который копируются данные, по крайней мере, так происходит концептуально . Если ядро может в этом помочь, копии удаляются путем воспроизведения виртуальных трюков (например, передача из файла, как правило, просто делает ту же страницу доступной для чтения другим процессом, поэтому, в конце концов, это всего лишь операция чтения, а не две копии, и нет в любом случае требуется дополнительная память, которая уже не используется буферным кешем. В некоторых ситуациях вы также можете получить 100% нулевое копирование или что-то очень близкое.
Если каналы малы и имеют конечный размер, то как это работает для любого неизвестного (возможно, большого) объема данных? Это просто: когда больше ничего не подходит, запись блокируется, пока не останется места.
Философия многих простых программ была наиболее полезна когда-то, когда памяти было очень мало. Потому что, ну, вы можете выполнять работу небольшими шагами, по одному за раз. В наши дни преимущества, кроме некоторой дополнительной гибкости, я полагаю, уже не так велики.
Тем не менее, каналы реализованы очень эффективно (они должны были быть!), Так что в этом нет и недостатка, и это устоявшаяся вещь, которая работает нормально и к которой привыкли люди, поэтому нет необходимости менять парадигму.
источник
|
в команде)?