Měl jsem dojem, že maximální délka jednoho argumentu zde není problémem ani tak jako celková velikost celkového pole argumentů plus velikost prostředí, které je omezeno na ARG_MAX
. Tak jsem si myslel, že něco jako následující by uspělo:
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
S - 100
je více než dostačující k zohlednění rozdílu mezi velikostí prostředí v shellu a echo
proces. Místo toho se mi zobrazila chyba:
bash: /bin/echo: Argument list too long
Po chvíli hraní jsem zjistil, že maximum bylo o celý hex řádově menší:
/bin/echo
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1)))
>/dev/null
Po odstranění mínus jedna se chyba vrátí. Zdánlivě maximum pro jeden argument je ve skutečnosti ARG_MAX/16
a -1
odpovídá za nulový bajt umístěný na konci řetězce v poli argumentů.
Dalším problémem je, že když se argument opakuje, celková velikost pole argumentů se může blížit ARG_MAX
, ale stále to není úplně ono:
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
Pomocí "${args[0]:6533}"
zde je poslední argument o 1 bajt delší a Argument list too long
chyba. Tento rozdíl pravděpodobně nebude způsoben velikostí daného prostředí:
$ cat /proc/$$/environ | wc -c
1045
Otázky:
- Je toto chování správné, nebo je někde chyba?
- Pokud ne, je toto chování někde zdokumentováno? Existuje jiný parametr, který definuje maximum pro jeden argument?
- Je toto chování omezeno na Linux (nebo dokonce jeho konkrétní verze)?
- Co je příčinou dalších ~5KB nesrovnalostí mezi skutečnou maximální velikostí pole argumentů plus přibližná velikost prostředí a
ARG_MAX
?
Další informace:
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
Přijatá odpověď:
Odpovědi
- Určitě to není chyba.
-
Parametr, který definuje maximální velikost pro jeden argument, je
MAX_ARG_STRLEN
. Pro tento parametr neexistuje žádná jiná dokumentace než komentáře vbinfmts.h
:/* * These are the maximum length and maximum number of strings passed to the * execve() system call. MAX_ARG_STRLEN is essentially random but serves to * prevent the kernel from being unduly impacted by misaddressed pointers. * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. */ #define MAX_ARG_STRLEN (PAGE_SIZE * 32) #define MAX_ARG_STRINGS 0x7FFFFFFF
Jak je ukázáno, Linux má také (velmi velký) limit na počet argumentů příkazu.
-
Zdá se, že omezení velikosti jednoho argumentu (které se liší od celkového limitu argumentů plus prostředí) je specifické pro Linux. Tento článek poskytuje podrobné srovnání
ARG_MAX
a ekvivalenty na systémech podobných Unixu.MAX_ARG_STRLEN
je diskutován pro Linux, ale není zde žádná zmínka o ekvivalentu na jiných systémech.Výše uvedený článek také uvádí, že
MAX_ARG_STRLEN
byl představen v Linuxu 2.6.23 spolu s řadou dalších změn týkajících se maxima argumentů příkazů (probráno níže). Log/diff pro odevzdání lze nalézt zde. -
Stále není jasné, čím je způsoben další nesoulad mezi výsledkem
getconf ARG_MAX
a skutečná maximální možná velikost argumentů plus prostředí. Související odpověď Stephana Chazelase naznačuje, že část prostoru připadá na ukazatele na každý z řetězců argumentů/prostředí. Moje vlastní šetření však naznačuje, že tyto ukazatele nejsou vytvořeny na začátkuexecve
systémové volání, kdy může stále vracetE2BIG
chyba volajícího procesu (ačkoliv ukazatele na každýargv
řetězec se jistě vytvoří později).Řetězce jsou také v paměti souvislé, pokud vidím, takže zde neprovádějte žádné mezery v paměti. I když je velmi pravděpodobné, že bude faktorem v tom, co dělá využít další paměť. Pochopení toho, co využívá extra prostor, vyžaduje podrobnější znalosti o tom, jak jádro alokuje paměť (což jsou užitečné znalosti, takže to prozkoumám a aktualizuji později).
ARG_MAX zmatek
Od Linuxu 2.6.23 (v důsledku tohoto odevzdání) došlo ke změnám ve způsobu, jakým jsou zpracovávány maxima argumentů příkazů, díky čemuž se Linux liší od jiných systémů podobných Unixu. Kromě přidání MAX_ARG_STRLEN
a MAX_ARG_STRINGS
, výsledek getconf ARG_MAX
nyní závisí na velikosti zásobníku a může se lišit od ARG_MAX
v limits.h
.
Obvykle výsledek getconf ARG_MAX
bude 1/4
velikosti zásobníku. Zvažte následující v bash
pomocí ulimit
získat velikost zásobníku:
$ echo $(( $(ulimit -s)*1024 / 4 )) # ulimit output in KiB
2097152
$ getconf ARG_MAX
2097152
Výše uvedené chování však bylo mírně změněno tímto potvrzením (přidáno v Linuxu 2.6.25-rc4~121). ARG_MAX
v limits.h
nyní slouží jako pevná spodní hranice výsledku getconf ARG_MAX
. Pokud je velikost zásobníku nastavena tak, že 1/4
velikost zásobníku je menší než ARG_MAX
v limits.h
a poté limits.h
bude použita hodnota:
$ grep ARG_MAX /usr/include/linux/limits.h
#define ARG_MAX 131072 /* # bytes of args + environ for exec() */
$ ulimit -s 256
$ echo $(( $(ulimit -s)*1024 / 4 ))
65536
$ getconf ARG_MAX
131072
Všimněte si také, že pokud je velikost zásobníku nastavena nižší než minimální možné ARG_MAX
, pak velikost zásobníku (RLIMIT_STACK
) se stane horním limitem velikosti argumentu/prostředí před E2BIG
je vráceno (ačkoli getconf ARG_MAX
bude stále zobrazovat hodnotu v limits.h
).
Poslední věc, kterou je třeba poznamenat, je, že pokud je jádro sestaveno bez CONFIG_MMU
(podpora hardwaru pro správu paměti), poté kontrola ARG_MAX
je zakázáno, takže limit neplatí. Ačkoli MAX_ARG_STRLEN
a MAX_ARG_STRINGS
stále platí.
Další čtení
- Související odpověď od Stephana Chazelase – https://unix.stackexchange.com/a/110301/48083
- Na podrobné stránce pokrývající většinu výše uvedeného. Zahrnuje tabulku
ARG_MAX
(a ekvivalentní) hodnoty na jiných unixových systémech – http://www.in-ulm.de/~mascheck/various/argmax/ - Zdá se, že zavedení
MAX_ARG_STRLEN
způsobil chybu v Automake, která vkládala skripty shellu do Makefiles pomocísh -c
– http://www.mail-archive.com/[email protected]/msg05522.html