При использовании труб, например
sudo cat /dev/sda | strings | less
Я могу перемещаться по строкам моего устройства sda. Но полностью ли загружается содержимое устройства sda и выводится ли оно в выходной поток cat? Или новые строки оцениваются всякий раз, когда программа запрашивает вывод из cat ? (т.е. я нажимаю j на пейджере less)
решение1
Это больше связано с тем, как less
работает, чем с тем, как работает cat
или strings
работает.
Команда cat
будет только передавать данные на стандартный вывод и блокироваться всякий раз, когда буфер канала между ней и strings
заполнен и никто не читает. cat
сама по себе выполняет минимальную буферизацию, а буфер канала обычно невелик.
Это также верно для strings
. Он будет обрабатывать данные из cat
и будет блокироваться, когда less
не считывает данные, которые strings
производит.
less
будет буферизовать свой ввод, чтобы позволить вам перемещаться вперед и назад по данным, которые он отображает. Когда вы прокручиваете на следующую страницу, less
будет считывать больше данных из strings
своего буфера. Пока вы не прокручиваете вперед, я полагаю, less
будет считывать только ограниченное количество данных (и, следовательно, strings
будет cat
заблокирован, пока вы не прокручиваете вперед).
Если вы передаете большой объем данных less
, для этой буферизации будет использовано довольно много памяти.есливы решили дочитать все до конца less
.
Существует опция -B
, которая ограничивает объем памяти, используемый для буферизации, до 64 килобайт (или любого другого значения, которое вы укажете с помощью опции -b
). Ограничение размера буфера таким образом не позволит вам прокручивать назад больше, чем может быть сохранено в указанном буферном пространстве, но также позволит вам читать огромные объемы данных без less
исчерпания памяти.
См. также man less
в вашей системе.
решение2
У каналов ограниченное буферное пространство, и если считыватель канала (такой как less
в вашем примере) не считывает больше данных из канала, писатель будет заблокирован после заполнения буфера. Это повлияет на команду strings
, которая, в свою очередь, заблокирует cat
команду после заполнения ее канала.
Естественно, cat
команда не может считать все содержимое устройства sda в основную память, поэтому, если блоки, которые еще не были ею прочитаны, изменяются, cat
она увидит измененное содержимое.
решение3
И , cat
и strings
, и большинство подобных утилит¹ считывают небольшой объем ввода за раз, обрабатывают его, затем считывают еще больше ввода и т. д. Так что в вашем случае cat
считывает только то, что less
отображается, плюс еще немного того, что находится в пути.
Более подробно, основная операция cat
выглядит следующим образом:
- Зарезервируйте несколько килобайт памяти для использования в качестве буфера.
- Хотя есть и другие доступные данные:
- Считывание до N байт входных данных в буфер. (Это перезаписывает данные, записанные в предыдущем цикле.)
- Записать содержимое буфера на выход.
Операция записи блокируется до тех пор, пока не появится место для копирования вывода. Когда вывод канала, сам канал потребляет немного памяти в ядре, что называетсябуфер трубы. Как только он заполнен, если cat
попытаться записать в канал, попытка записи блокируется, пока не появится место. В буфере канала может быть место, когда процесс на считывающем конце канала считывает некоторые данные.
Программа strings
работает так же, как и cat
, за исключением того, что она копирует не весь ввод, а только выбранные части.
Программа less
работает немного по-другому: она сохраняет все, что считывает, в памяти. Она не перерабатывает свой буфер, а продолжает его увеличивать, пока поступает больше входных данных. Однако часть чтения похожа тем, что less
считывает данные только тогда, когда они ей нужны: она считывает только последнюю строку, которую отображает, плюс немного больше, что считывает в ожидании, если доступно.
Итак, когда вы запускаете sudo cat /dev/sda | strings | less
, то, что было прочитано, /dev/sda
состоит из:
- Данные, которые
less
уже были отображены (или прокручены). - До нескольких килобайт данных, которые
less
были считаны, но еще не отображены. - До нескольких килобайт в буфере канала между
strings
иless
. - До нескольких килобайт в памяти
strings
. - До нескольких килобайт в буфере канала между
cat
иstrings
. - До нескольких килобайт в памяти
cat
.
Вы можете наблюдать, когда каждая программа считывает и записывает данные, отслеживая ее системные вызовы:
sudo strace -e read,write -o cat.strace cat /dev/sda | strace -e read,write -o cat.strace strings | strace -e read,write -o less.strace less
и смотреть *.strace
файлы. Вы также можете проверить, сколько cat
было прочитано, проверив смещение файла, например, с помощью lsof -p1234
или с помощью head /proc/1234/fdinfo/0
где 1234
находится идентификатор процесса cat
.
¹ Среди основных утилит обработки текста основным исключением является sort
, которая не может вывести никаких данных, пока не прочитает все входные данные: насколько ей известно, первая строка выходных данных вполне может оказаться последней строкой входных данных, до которой она доберется.
решение4
В некоторых системах (например, MS-Dos) конвейер реализуется путем копирования вывода первой команды в файл, а затем запуска второй команды для чтения из этого файла. Unix так не делает.
В Unix это похоже на производственную линию. Каждая стадия работает одновременно, считывая входные данные и производя выходные данные. Если процесс A производит быстрее, чем потребляет процесс B, то между процессами A и B накапливается запас. Когда его становится слишком много (от ½КиБ до 4КиБ), процесс A приостанавливается. Когда для обработки B нет запаса, то B приостанавливается. Процессы приостанавливаются и возобновляются, чтобы поддерживать низкий уровень запасов.
Код в этих программах не заботится ни о чем из этого. Он просто читает ввод и записывает вывод. Если он попытается прочитать, прежде чем данные станут доступны, или попытается записать, прежде чем будет готов следующий процесс, то операционная система приостановит его, пока не будет готов.
Когда читать больше нечего (и ничего больше не будет в пути), читатель получает сигнал конца файла и выходит. Это, в свою очередь, запускает сигнал конца файла в следующем процессе.