J'avais l'impression que la longueur maximale d'un seul argument n'était pas le problème ici autant que la taille totale du tableau d'arguments global plus la taille de l'environnement, qui est limitée à ARG_MAX
. Ainsi, j'ai pensé que quelque chose comme ce qui suit réussirait :
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
Avec le - 100
étant plus que suffisant pour tenir compte de la différence entre la taille de l'environnement dans le shell et le echo
processus. Au lieu de cela, j'ai eu l'erreur :
bash: /bin/echo: Argument list too long
Après avoir joué un moment, j'ai trouvé que le maximum était d'un ordre de grandeur plus petit :
/bin/echo
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1)))
>/dev/null
Lorsque le moins un est supprimé, l'erreur revient. Apparemment, le maximum pour un seul argument est en fait ARG_MAX/16
et le -1
tient compte de l'octet nul placé à la fin de la chaîne dans le tableau d'arguments.
Un autre problème est que lorsque l'argument est répété, la taille totale du tableau d'arguments peut être plus proche de ARG_MAX
, mais toujours pas tout à fait là :
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
Utilisation de "${args[0]:6533}"
ici rend le dernier argument plus long d'un octet et donne la Argument list too long
Erreur. Il est peu probable que cette différence s'explique par la taille de l'environnement donné :
$ cat /proc/$$/environ | wc -c
1045
Questions :
- Ce comportement est-il correct ou y a-t-il un bogue quelque part ?
- Si non, ce comportement est-il documenté quelque part ? Existe-t-il un autre paramètre qui définit le maximum pour un seul argument ?
- Ce comportement est-il limité à Linux (ou même à des versions particulières de Linux) ?
- Ce qui explique l'écart supplémentaire d'environ 5 Ko entre la taille maximale réelle du tableau d'arguments plus la taille approximative de l'environnement et
ARG_MAX
?
Informations supplémentaires :
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
Réponse acceptée :
Réponses
- Certainement pas un bug.
-
Le paramètre qui définit la taille maximale pour un argument est
MAX_ARG_STRLEN
. Il n'y a pas de documentation pour ce paramètre autre que les commentaires dansbinfmts.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
Comme indiqué, Linux a également une (très grande) limite sur le nombre d'arguments d'une commande.
-
Une limite sur la taille d'un seul argument (qui diffère de la limite globale sur les arguments plus l'environnement) semble être spécifique à Linux. Cet article donne une comparaison détaillée de
ARG_MAX
et équivalents sur les systèmes de type Unix.MAX_ARG_STRLEN
est discuté pour Linux, mais il n'y a aucune mention d'équivalent sur d'autres systèmes.L'article ci-dessus indique également que
MAX_ARG_STRLEN
a été introduit dans Linux 2.6.23, ainsi qu'un certain nombre d'autres changements relatifs aux maximums d'arguments de commande (discutés ci-dessous). Le log/diff pour le commit peut être trouvé ici. -
On ne sait toujours pas ce qui explique l'écart supplémentaire entre le résultat de
getconf ARG_MAX
et la taille maximale réelle possible des arguments plus l'environnement. La réponse connexe de Stéphane Chazelas suggère qu'une partie de l'espace est représentée par des pointeurs vers chacune des chaînes d'argument/environnement. Cependant, ma propre enquête suggère que ces pointeurs ne sont pas créés au début de l'execve
appel système alors qu'il peut toujours renvoyer unE2BIG
erreur au processus appelant (bien que des pointeurs vers chaqueargv
chaîne sont certainement créées plus tard).De plus, les chaînes sont contiguës en mémoire pour autant que je puisse voir, donc aucun trou de mémoire n'est dû à l'alignement ici. Bien que ce soit très probablement un facteur dans tout ce que fait utiliser la mémoire supplémentaire. Comprendre ce qui utilise l'espace supplémentaire nécessite une connaissance plus détaillée de la façon dont le noyau alloue la mémoire (ce qui est une connaissance utile à avoir, donc je vais enquêter et mettre à jour plus tard).
ARG_MAX Confusion
Depuis Linux 2.6.23 (à la suite de cette validation), des modifications ont été apportées à la manière dont les maximums d'arguments de commande sont gérés, ce qui différencie Linux des autres systèmes de type Unix. En plus d'ajouter MAX_ARG_STRLEN
et MAX_ARG_STRINGS
, le résultat de getconf ARG_MAX
dépend maintenant de la taille de la pile et peut être différent de ARG_MAX
dans limits.h
.
Normalement le résultat de getconf ARG_MAX
sera 1/4
de la taille de la pile. Considérez ce qui suit dans bash
en utilisant ulimit
pour obtenir la taille de la pile :
$ echo $(( $(ulimit -s)*1024 / 4 )) # ulimit output in KiB
2097152
$ getconf ARG_MAX
2097152
Cependant, le comportement ci-dessus a été légèrement modifié par ce commit (ajouté dans Linux 2.6.25-rc4~121). ARG_MAX
dans limits.h
sert maintenant de limite inférieure dure sur le résultat de getconf ARG_MAX
. Si la taille de la pile est définie de telle sorte que 1/4
de la taille de la pile est inférieure à ARG_MAX
dans limits.h
, puis le limits.h
valeur sera utilisée :
$ 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
Notez également que si la taille de la pile est inférieure au minimum possible ARG_MAX
, puis la taille de la pile (RLIMIT_STACK
) devient la limite supérieure de la taille de l'argument/de l'environnement avant E2BIG
est renvoyé (bien que getconf ARG_MAX
affichera toujours la valeur dans limits.h
).
Une dernière chose à noter est que si le noyau est construit sans CONFIG_MMU
(prise en charge du matériel de gestion de la mémoire), puis la vérification de ARG_MAX
est désactivé, la limite ne s'applique donc pas. Bien que MAX_ARG_STRLEN
et MAX_ARG_STRINGS
s'applique toujours.
Autres lectures
- Réponse connexe de Stéphane Chazelas - https://unix.stackexchange.com/a/110301/48083
- Dans une page détaillée couvrant la plupart des éléments ci-dessus. Inclut un tableau de
ARG_MAX
(et équivalents) sur d'autres systèmes de type Unix - http://www.in-ulm.de/~mascheck/various/argmax/ - Apparemment l'introduction de
MAX_ARG_STRLEN
a causé un bogue avec Automake qui intégrait des scripts shell dans les Makefiles en utilisantsh -c
– http://www.mail-archive.com/[email protected]/msg05522.html