У меня сложилось впечатление, что максимальная длина одного аргумента не была проблемой здесь, так как общий размер массива аргументов плюс размер среды, которая ограничена ARG_MAX
. Таким образом, я думал, что что-то вроде следующего будет успешным:
env_size=$(cat /proc/$$/environ | wc -c)
(( arg_size = $(getconf ARG_MAX) - $env_size - 100 ))
/bin/echo $(tr -dc [:alnum:] </dev/urandom | head -c $arg_size) >/dev/null
При - 100
этом более чем достаточно, чтобы учесть разницу между размером среды в оболочке и echo
процессом. Вместо этого я получил ошибку:
bash: /bin/echo: Argument list too long
Поработав некоторое время, я обнаружил, что максимум был на порядок меньше в шестнадцатеричном формате:
/bin/echo \
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
>/dev/null
Когда минус один удаляется, ошибка возвращается. По-видимому, максимум для одного аргумента на самом деле ARG_MAX/16
и -1
учитывает нулевой байт, помещенный в конец строки в массиве аргументов.
Другая проблема заключается в том, что когда аргумент повторяется, общий размер массива аргументов может быть ближе ARG_MAX
, но все же не совсем там:
args=( $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) )
for x in {1..14}; do
args+=( ${args[0]} )
done
/bin/echo "${args[@]}" "${args[0]:6534}" >/dev/null
Использование "${args[0]:6533}"
здесь делает последний аргумент на 1 байт длиннее и выдает Argument list too long
ошибку. Это различие вряд ли будет объяснено размером окружающей среды:
$ cat /proc/$$/environ | wc -c
1045
Вопросов:
- Это правильное поведение, или где-то есть ошибка?
- Если нет, документируется ли это поведение где-нибудь? Есть ли другой параметр, который определяет максимум для одного аргумента?
- Это поведение ограничено Linux (или даже определенными версиями такого)?
- Чем объясняется дополнительное расхождение ~ 5 КБ между фактическим максимальным размером массива аргументов и приблизительным размером среды и
ARG_MAX
?
Дополнительная информация:
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
getconf ARG_MAX
зависит от токаulimit -s
. Установите неограниченное количество и получите потрясающий 4611686018427387903 для ARG_MAX.Ответы:
ответы
Параметр, который определяет максимальный размер для одного аргумента
MAX_ARG_STRLEN
. Для этого параметра нет документации, кроме комментариев вbinfmts.h
:Как показано, Linux также имеет (очень большое) ограничение на количество аргументов команды.
Ограничение на размер одного аргумента (которое отличается от общего ограничения на аргументы плюс окружение), по-видимому, специфично для Linux. Эта статья дает подробное сравнение
ARG_MAX
и эквиваленты на Unix-подобных системах.MAX_ARG_STRLEN
обсуждается для Linux, но нет никакого упоминания о каком-либо эквиваленте в других системах.В вышеприведенной статье также говорится, что она
MAX_ARG_STRLEN
была введена в Linux 2.6.23, а также ряд других изменений, связанных с максимумами аргументов команды (обсуждаются ниже). Log / diff для коммита можно найти здесь .До сих пор не ясно, что объясняет дополнительное расхождение между результатом
getconf ARG_MAX
и фактическим максимально возможным размером аргументов плюс среда. Ответ Стефана Чазеласа предполагает, что часть пространства учитывается указателями на каждую из строк аргумента / окружения. Тем не менее, мое собственное расследование показывает, что эти указатели не создаются в началеexecve
системного вызова, когда он все еще может вернутьE2BIG
ошибку вызывающему процессу (хотя указатели на каждуюargv
строку, безусловно, создаются позже).Кроме того, строки, насколько я вижу, являются смежными в памяти, поэтому никаких пробелов в памяти здесь не происходит. Хотя, скорее всего, это фактор, который израсходует лишнюю память. Понимание того, что использует дополнительное пространство, требует более подробных знаний о том, как ядро распределяет память (что полезно знать, поэтому я буду исследовать и обновлять позже).
ARG_MAX Путаница
Начиная с Linux 2.6.23 (в результате этого коммита ) изменились способы обработки максимумов аргументов команд, что отличает Linux от других Unix-подобных систем. В дополнение к добавлению
MAX_ARG_STRLEN
иMAX_ARG_STRINGS
, результатgetconf ARG_MAX
теперь зависит от размера стека и может отличаться отARG_MAX
inlimits.h
.Обычно результат
getconf ARG_MAX
будет иметь1/4
размер стека. Рассмотрим следующее приbash
использованииulimit
для получения размера стека:Однако приведенное выше поведение было слегка изменено этим коммитом (добавлено в Linux 2.6.25-rc4 ~ 121).
ARG_MAX
вlimits.h
настоящее время служит жесткой нижней границы на результатgetconf ARG_MAX
. Если размер стека установлен так, что1/4
размер стека меньше, чемARG_MAX
вlimits.h
, тоlimits.h
будет использоваться значение:Также обратите внимание, что если размер стека установлен ниже минимально возможного
ARG_MAX
, то размер стека (RLIMIT_STACK
) становится верхним пределом размера аргумента / среды до того, какE2BIG
будет возвращено (хотяgetconf ARG_MAX
все равно будет отображаться значение вlimits.h
).Последнее, что следует отметить, это то, что если ядро собрано без
CONFIG_MMU
(поддержка аппаратного обеспечения управления памятью), то проверкаARG_MAX
отключена, поэтому ограничение не применяется. ХотяMAX_ARG_STRLEN
иMAX_ARG_STRINGS
до сих пор применяются.Дальнейшее чтение
ARG_MAX
(и эквивалентных) значений в других Unix-подобных системах - http://www.in-ulm.de/~mascheck/various/argmax/MAX_ARG_STRLEN
вызвало ошибку в Automake, которая встраивала сценарии оболочки в Makefiles, используяsh -c
- http://www.mail-archive.com/bug-make@gnu.org/msg05522.htmlисточник
В
eglibc-2.18/NEWS
В
eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff
В
linux/include/uapi/linux/limits.h
И
131072
это ваш$(getconf ARG_MAX)/16-1
, возможно, вы должны начать с 0.Вы имеете дело с glibc и Linux. Было бы неплохо также пропатчить getconf, чтобы получить «правильное»
ARG_MAX
возвращаемое значение.Редактировать:
Чтобы немного прояснить (после короткого, но горячего обсуждения)
ARG_MAX
Константа , которая определена вlimits.h
, дает максимальную длину одного аргумента , переданного с Exec.Команда
getconf ARG_MAX
возвращает максимальное значение накопленных аргументов размера и размера среды, переданных в exec.источник
eglibc-2.18/NEWS
фрагмента? Было бы хорошо связать это с конкретной версией ядра.getconf ARG_MAX
идет о совокупном размере arg + env (переменная в недавнем Linux, см.ulimit -s
И другой вопрос, который я связал), а не о максимальной длине одного аргумента, для которого нет запроса sysconf / getconf.Так что @StephaneChazelas справедливо исправляет меня в комментариях ниже - сама оболочка никоим образом не определяет максимальный размер аргумента, разрешенный вашей системой, а скорее, он устанавливается вашим ядром.
Как уже говорили некоторые другие, кажется, что ядро ограничивает максимальный размер аргумента 128 КБ, который вы можете передать новому процессу из любого другого при первом его исключении. Эта проблема возникает именно из-за множества вложенных
$(command substitution)
подоболочек, которые должны выполняться на месте и передавать весь свой вывод от одного к другому.И это своего рода дикая догадка, но поскольку расхождение ~ 5 КБ кажется настолько близким к стандартному размеру системной страницы, я подозреваю, что оно предназначено для использования страницей
bash
для обработки подоболочки, которая$(command substitution)
требуется для окончательной доставки ее выходных данных и / или стек функций, который он использует для сопоставленияarray table
ваших данных. Я могу только предположить, что ни один не выйдет на свободу.Ниже я продемонстрирую, что, хотя это может быть немного сложнее, существует возможность передавать очень большие значения переменных оболочки при вызове новым процессам, при условии, что вам удастся выполнить их потоковую передачу.
Для этого я в основном использовал трубы. Но я также оценил массив оболочки в
here-document
указанномcat's stdin.
ниже результате.Но последнее замечание: если у вас нет особой необходимости в переносимом коде, мне кажется, что это
mapfile
может немного упростить работу вашей оболочки.Возможно, вы могли бы удвоить это, а затем сделать это снова, если бы вы делали это в потоках - я не настолько болезнен, чтобы это выяснить - но определенно это работает, если вы транслируете это.
Я попытался изменить
printf
часть генератора во второй строке:Это также работает:
Так что, может быть, я немного болен. Я использую
zero padding here
и добавляю предыдущее"$arg"
значение к текущему"$arg"
значению. Я получаю далеко за 6500 ...И если я изменить
cat
строку, чтобы выглядеть так:Я могу получить количество байтов из
wc.
Помните, что это размеры каждого ключа вargs
массиве. Общий размер массива является суммой всех этих значений.источник
echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null
будут работать нормально. Проблема возникает только при использовании внешней команды.bash
это как-то сжимает?printf
является встроенным, так что не выполняется , и AFAICT, вашcat
не дает никаких аргументов.