Использует ли cat ленивые вычисления?

Использует ли cat ленивые вычисления?

При использовании труб, например

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 приостанавливается. Процессы приостанавливаются и возобновляются, чтобы поддерживать низкий уровень запасов.

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

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

Связанный контент