web-dev-qa-db-fra.com

Qu'est-ce qui définit la taille maximale d'un seul argument de commande?

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. J'ai donc 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 processus echo. Au lieu de cela, j'ai eu l'erreur:

bash: /bin/echo: Argument list too long

Après avoir joué pendant un certain temps, j'ai trouvé que le maximum était un ordre de grandeur hexadécimal plus petit:

/bin/echo \
  $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
  >/dev/null

Lorsque le moins est supprimé, l'erreur revient. Apparemment, le maximum pour un seul argument est en fait ARG_MAX/16 et le -1 représente 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 pas encore 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

En utilisant "${args[0]:6533}" rend ici le dernier argument 1 octet plus long et donne le Argument list too long Erreur. Il est peu probable que cette différence soit expliquée par la taille de l'environnement étant donné:

$ cat /proc/$$/environ | wc -c
1045

Des questions:

  1. Ce comportement est-il correct ou y a-t-il un bug quelque part?
  2. Sinon, ce comportement est-il documenté quelque part? Y a-t-il un autre paramètre qui définit le maximum pour un seul argument?
  3. Ce comportement est-il limité à Linux (ou même à des versions particulières de ce type)?
  4. Ce qui explique l'écart supplémentaire de ~ 5 Ko entre la taille maximale réelle du tableau d'arguments plus la taille approximative de l'environnement et ARG_MAX?

Information additionnelle:

uname -a
Linux graeme-rock 3.13-1-AMD64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
51
Graeme

Réponses

  1. Certainement pas un bug.
  2. Le paramètre qui définit la taille maximale d'un argument est MAX_ARG_STRLEN. Il n'y a pas de documentation pour ce paramètre autre que les commentaires dans binfmts.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.

  3. 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. Cette 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 aucun autre système.

    L'article ci-dessus indique également que MAX_ARG_STRLEN a été introduit dans Linux 2.6.23, avec un certain nombre d'autres changements concernant les maximums des arguments de commande (discuté ci-dessous). Le log/diff du commit peut être trouvé ici .

  4. On ne sait toujours pas ce qui explique l'écart supplémentaire entre le résultat de getconf ARG_MAX et la taille réelle maximale possible des arguments plus l'environnement. 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 argument/environnement. Cependant, ma propre enquête suggère que ces pointeurs ne sont pas créés au début de l'appel système execve lorsqu'il peut toujours renvoyer un E2BIG erreur au processus appelant (bien que des pointeurs vers chaque chaîne argv soient certainement créés plus tard).

    De plus, les chaînes sont contiguës en mémoire pour autant que je puisse voir, donc aucun espace mémoire dû à l'alignement ici. Bien qu'il soit très probable qu'il soit un facteur dans tout ce qui utilise 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 ), il y a eu des changements dans la façon dont les maximums des arguments de commande sont traités, ce qui fait que Linux diffère 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. Tenez compte des éléments suivants 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 ceci commit (ajouté dans Linux 2.6.25-rc4 ~ 121). ARG_MAX dans limits.h sert maintenant de borne inférieure stricte au 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érieur à ARG_MAX dans limits.h, puis le limits.h la 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'appliquent toujours.

Lectures complémentaires

53
Graeme

Dans eglibc-2.18/NEWS

* ARG_MAX is not anymore constant on Linux.  Use sysconf(_SC_ARG_MAX).
Implemented by Ulrich Drepper.

Dans eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff

+      case _SC_ARG_MAX:
+   request[0] = CTL_KERN;
+   request[1] = KERN_ARGMAX;
+   if (__sysctl(request, 2, &value, &len, NULL, 0) == -1)
+       return ARG_MAX;
+   return (long)value;

Dans linux/include/uapi/linux/limits.h

#define ARG_MAX       131072    /* # bytes of args + environ for exec() */

Et 131072 Est votre $(getconf ARG_MAX)/16-1, vous devriez peut-être commencer à 0.

Vous traitez avec glibc et Linux. Il serait bon de patcher aussi getconf afin de récupérer la "bonne" valeur ARG_MAX.

Modifier:

Pour cliver un peu (après une discussion courte mais chaude)

La constante ARG_MAX Qui est définie dans limits.h, Donne la longueur maximale d'un argument passé avec exec.

La commande getconf ARG_MAX Renvoie la valeur maximale de la taille des arguments cumulés et de la taille de l'environnement transmises à exec.

0
user55518