Использование ресурсов с использованием pipe и здесь string

16

Мы можем получить тот же результат, используя следующие два bash,

echo 'foo' | cat

и

cat <<< 'foo'

Мой вопрос: в чем разница между этими двумя в отношении используемых ресурсов и какой из них лучше?

Я думаю, что при использовании канала мы используем дополнительный процесс echoи канал, в то время как в этой строке используется только дескриптор файла cat.

utlamn
источник

Ответы:

17

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

Здесь строка - это обычный файл, созданный в смонтированной файловой системе. Оболочка создает файл и сохраняет его дескриптор, одновременно удаляя его единственную ссылку на файловую систему (и, следовательно, удаляя его), прежде чем когда-либо записать / прочитать байт в / из файла. Ядро будет поддерживать пространство, необходимое для файла, пока все процессы не освободят все дескрипторы для него. Если у ребенка, читающего такой дескриптор, есть возможность сделать это, его можно перемотать lseek()и прочитать снова.

В обоих случаях токены <<<и |представляют файловые дескрипторы и не обязательно сами файлы. Вы можете получить лучшее представление о том, что происходит, выполнив такие вещи, как:

readlink /dev/fd/1 | cat

...или...

ls -l <<<'' /dev/fd/*

Наиболее существенное различие между этими двумя файлами заключается в том, что здесь-строка / doc является практически единым делом - оболочка записывает все данные в него, прежде чем предложить дескриптор чтения дочернему элементу. С другой стороны, оболочка открывает канал для соответствующих дескрипторов и разветвляет дочерние элементы, чтобы управлять ими для канала - и поэтому он записывается / читается одновременно на обоих концах.

Эти различия, однако, только целом верны. Насколько я знаю (что на самом деле не так уж и далеко), это справедливо практически для каждой оболочки, которая обрабатывает <<<сокращенную строку <<здесь для перенаправления документа здесь, за единственным исключением yash. yash, busybox, dash, И другие ashварианты имеют тенденцию к спине здесь-документы с трубами, хотя, и поэтому в этих снарядов там действительно очень мало разницы между ними после всего.

Хорошо - два исключения. Теперь, когда я думаю об этом, на ksh93самом деле вообще не выполняет конвейер |, а скорее обрабатывает весь бизнес с сокетами - хотя он и делает удаленный файл tmp, <<<*как и большинство других. Более того, он только помещает отдельные секции конвейера в среду подоболочек, что является своего рода эвфемизмом POSIX, по крайней мере, он действует как подоболочка , и даже не делает вилки.

Дело в том, что результаты теста @ PSkocik (который очень полезен) могут здесь сильно различаться по многим причинам, и большинство из них зависят от реализации. Для настройки данного документа основными факторами будут ${TMPDIR}тип целевой файловой системы и текущая конфигурация / доступность кэша, а также объем записываемых данных. Для канала это будет размер самого процесса оболочки, потому что копии создаются для необходимых вилок. Таким образом bash, ужасно при настройке конвейера (включая подстановки $(команд )) - потому что он большой и очень медленный, но с ksh93ним вряд ли что-то меняет.

Вот еще один небольшой фрагмент оболочки, демонстрирующий, как оболочка разделяется на подоболочки для конвейера:

pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0

32059  #bash's pid
32059  #sh's ppid
32059  #1st subshell's $$
32111  #1st subshell sh's ppid
32059  #2cd subshell's $$
32114  #2cd subshell sh's ppid

Разница между тем, что pipe_who()сообщается в конвейерном вызове, и отчетом об одном прогоне в текущей оболочке, связана с тем, что в (подоболочке )указано, как запрашивать pid родительской оболочки $$при расширении. Хотя bashподоболочки определенно являются отдельными процессами, $$специальный параметр оболочки не является надежным источником этой информации. Тем не менее, дочерняя shоболочка subshell не отказывается точно сообщать о ней $PPID.

mikeserv
источник
Очень полезно. Файловая система в ядре, есть ли название для нее? это означает, что он существует в пространстве ядра?
Утлам
2
@utlamn - на самом деле, да - просто пайфс . Это все в ядре - но (кроме таких вещей, как FUSE) так же, как и все файловые операции ввода-вывода .
mikeserv
10

Ничто не заменит бенчмаркинг:

pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done  )

real    0m2.080s
user    0m0.738s
sys 0m1.439s
pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done  )

real    0m4.432s
user    0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done  )
real    0m3.380s
user    0m1.121s
sys 0m3.423s

И для большего количества данных:

TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done  )

real    0m42.327s
user    0m38.591s
sys 0m4.226s
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done  )

real    1m26.946s
user    1m23.116s
sys 0m3.681s
pskocik@ProBook:~ 

$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done  )

real    0m43.910s
user    0m40.178s
sys 0m4.119s

Казалось бы, версия канала имеет большую стоимость установки, но в итоге более эффективна.

PSkocik
источник
@mikeserv Это было правильно. Я добавил тест с большим количеством данных.
PSkocik
2
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/nullказалось, быстро в обоих случаях ...
user23013
@ user23013 Это имеет смысл. Я не понимаю, почему так или echo "$longstring"иначе <<<"$longstring"было бы настроено на эффективность, а с короткими струнами эффективность все равно не имеет большого значения.
PSkocik
Интересно, что в моем случае (на Ubuntu 14.04 Intel Quad Core i7) cat <(echo foo) >/dev/nullбыстрее, чем echo foo | cat >/dev/null.
Пабук
1
@Prem Да, это был бы лучший подход, но еще лучше было бы вообще не беспокоиться об этом и использовать подходящий инструмент для работы. Нет причин думать, что heredocs будет настроен на производительность.
PSkocik