Вопрос
Когда я искал инструменты конвейерной буферизации в * NIX, я увидел предложения по использованию buffer
, mbuffer
или pv
. Тем не менее, первые два не всегда находятся в официальном репозитории дистрибутивов (например, Arch), в то время как pv
(начиная с 1.6.0) имеет ошибку, которая препятствует этой функциональности. В нескольких других вопросах я вижу упоминания о том, что dd
используется в качестве буферов, и я хотел бы изучить его, потому что dd
всегда там. Тем не менее, ни один из них не является достаточно сложным, чтобы иметь реальный смысл, поэтому здесь я прошу "правильный" способ его использования.
Вопросы , указанные dd
включают https://unix.stackexchange.com/questions/345072/can-dd-be-used-to-add-a-buffer-to-a-pipe и https://unix.stackexchange.com/ вопросы / 21918 / полезности к буферу-ан-неограничена-количество-о-данных-в-трубопроводе
Для простоты тестирования ниже приведен сценарий тестирования с некоторыми комментариями о моих собственных экспериментах. Подробности будут объяснены после перечисления кода. Пожалуйста, убедитесь, что у вас установлен pv
и как минимум 256M памяти перед запуском!
#!/bin/sh
producer() {
while [ 1 ]; do
dd if=/dev/zero bs=64M count=1 iflag=fullblock status=none
sleep 4
done
}
buffer() {
# Works, but long
# Must at least fill 32M before consumer starts
# So, must choose small obs and chain more to look
# more like a proper "buffer"
dd obs=32M status=none | \
dd obs=32M status=none| \
dd obs=32M status=none| \
dd obs=32M status=none
# Doesn't work, producer rate limited
#dd bs=128M status=none
# Doesn't work, producer must fill buffer, then
# must wait until buffer is empty
#dd obs=128M status=none
# Doesn't work, producer rate limited
#dd ibs=128M status=none
# Doesn't work, producer must fill buffer, then
# must wait until buffer is empty
#dd bs=128M status=none iflag=fullblock
}
consumer() {
pv --rate-limit 1M -q | dd of=/dev/null status=none
}
producer | pv -cN produce | buffer | pv -cN consume | consumer
Здесь производитель производит 64 МБ данных каждые 4 секунды с буфером 128 МБ, в то время как потребитель потребляет с постоянной скоростью 1 МБ / с. Конечно, это означает, что буфер будет переполнен довольно быстро, но это должно четко показывать эффекты. В идеале, до того как буфер заполнится (при третьем производстве), мы должны увидеть постоянное потребление 1 МБ / с, а приросты производительности дают данные по 64 МБ каждый. "Правильный" вывод выглядит так:
produce: 128MiB 0:00:07 [ 0 B/s] [ <=> ]
consume: 7.25MiB 0:00:07 [1.01MiB/s] [ <=> ]
Здесь рабочее решение показано следующим образом:
dd obs=32M status=none | \
dd obs=32M status=none| \
dd obs=32M status=none| \
dd obs=32M status=none
Это строится путем разделения необходимого буфера 128 МБ на 4 блока. Да, каждый блок должен заполняться до того, как данные будут переданы на следующий уровень, но, поскольку 32 МБ меньше пакета 64 МБ, он работает для этого теста, как если бы это был настоящий буфер. Теперь есть некоторые проблемы.
- В реальных приложениях у нас нет мгновенного пакета данных, поэтому куски должны быть маленькими, но не слишком маленькими. Это означает, что будет длинная цепочка команд
dd
Что если EOF обнаружен до достижения отметки 32 МБ? Будет ли этот блок потерян?Я тестировал с помощьюdd if=test.txt| dd obs=1M | dd obs=1M | dd of=test2.txt
и сравниваемый результат. Оказывается, это не проблема. Таким образом, использование его для резервного копирования не повредит данные.- Сколько накладных расходов это создает?
- Есть ли более элегантный способ добиться того же самого, грамотно расставив параметры?
Есть несколько других попыток, включенных в сценарий, и они не работают, как объяснено в комментариях. И я попытался использовать фоновые процессы FIFO +, который дает тот же результат.
PS. Как вы знаете, буферизация канала весьма полезна при резервном копировании A в B, особенно когда A - жесткий диск, у которого есть время поиска. Так что я бы сделал что-то вроде этого:
tar cSpf - <path> -C <root path> | <a large buffer> | <some parallel compressor> \
| <small buffer if compressor is generally slow and B have seek time> \
| dd bs=<several GB if B is not an SSD> iflag=fullblock oflag=direct of=<archive.tar.??>