it-swarm.com.de

Was definiert die maximale Größe für ein einzelnes Befehlsargument?

Ich hatte den Eindruck, dass die maximale Länge eines einzelnen Arguments hier weniger das Problem war als vielmehr die Gesamtgröße des gesamten Argumentarrays plus die Größe der Umgebung, die auf ARG_MAX Begrenzt ist. Daher dachte ich, dass so etwas wie das Folgende gelingen würde:

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

Der Wert von - 100 Ist mehr als ausreichend, um den Unterschied zwischen der Größe der Umgebung in der Shell und dem Prozess echo zu berücksichtigen. Stattdessen habe ich den Fehler bekommen:

bash: /bin/echo: Argument list too long

Nachdem ich eine Weile herumgespielt hatte, stellte ich fest, dass das Maximum eine volle Hex-Größenordnung kleiner war:

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

Wenn das Minus entfernt wird, kehrt der Fehler zurück. Scheinbar ist das Maximum für ein einzelnes Argument tatsächlich ARG_MAX/16 Und -1 Berücksichtigt das Nullbyte am Ende der Zeichenfolge im Argumentarray.

Ein weiteres Problem ist, dass bei Wiederholung des Arguments die Gesamtgröße des Argumentarrays näher an ARG_MAX Liegen kann, aber immer noch nicht ganz vorhanden ist:

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

Wenn Sie hier "${args[0]:6533}" Verwenden, wird das letzte Argument um 1 Byte länger und der Fehler Argument list too long Wird angezeigt. Es ist unwahrscheinlich, dass dieser Unterschied auf die Größe der Umgebung zurückzuführen ist:

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

Fragen:

  1. Ist das richtig oder gibt es irgendwo einen Fehler?
  2. Wenn nicht, ist dieses Verhalten irgendwo dokumentiert? Gibt es einen anderen Parameter, der das Maximum für ein einzelnes Argument definiert?
  3. Ist dieses Verhalten auf Linux (oder sogar bestimmte Versionen davon) beschränkt?
  4. Was erklärt die zusätzliche Diskrepanz von ~ 5 KB zwischen der tatsächlichen maximalen Größe des Argumentarrays plus der ungefähren Größe der Umgebung und ARG_MAX?

Zusätzliche Information:

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

Antworten

  1. Auf keinen Fall ein Fehler.
  2. Der Parameter, der die maximale Größe für ein Argument definiert, ist MAX_ARG_STRLEN. Für diesen Parameter gibt es keine andere Dokumentation als die Kommentare in 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
    

    Wie gezeigt, hat Linux auch eine (sehr große) Begrenzung für die Anzahl der Argumente für einen Befehl.

  3. Eine Begrenzung der Größe eines einzelnen Arguments (die sich von der Gesamtbeschränkung für Argumente plus Umgebung unterscheidet) scheint für Linux spezifisch zu sein. Dieser Artikel bietet einen detaillierten Vergleich von ARG_MAX Und Äquivalenten auf Unix-ähnlichen Systemen. MAX_ARG_STRLEN Wird für Linux diskutiert, aber auf anderen Systemen wird kein Äquivalent erwähnt.

    Der obige Artikel besagt auch, dass MAX_ARG_STRLEN In Linux 2.6.23 eingeführt wurde, zusammen mit einer Reihe anderer Änderungen in Bezug auf Befehlsargumentmaxima (siehe unten). Das Protokoll/Diff für das Commit finden Sie hier .

  4. Es ist immer noch nicht klar, was die zusätzliche Diskrepanz zwischen dem Ergebnis von getconf ARG_MAX Und der tatsächlich maximal möglichen Größe von Argumenten plus Umgebung erklärt. Stephane Chazelas 'verwandte Antwort legt nahe, dass ein Teil des Raums durch Zeiger auf jede der Argument-/Umgebungszeichenfolgen berücksichtigt wird. Meine eigene Untersuchung legt jedoch nahe, dass diese Zeiger nicht früh im Systemaufruf execve erstellt werden, wenn möglicherweise immer noch ein Fehler E2BIG An den aufrufenden Prozess zurückgegeben wird (obwohl Zeiger auf jeden argv string werden sicherlich später erstellt).

    Außerdem sind die Zeichenfolgen, soweit ich sehen kann, im Speicher zusammenhängend, sodass hier keine Speicherlücken aufgrund der Ausrichtung auftreten. Obwohl es sehr wahrscheinlich ist, dass ein Faktor innerhalb dessen ist, was den zusätzlichen Speicher verbraucht. Um zu verstehen, was den zusätzlichen Speicherplatz nutzt, müssen Sie detaillierter wissen, wie der Kernel Speicher zuweist (was nützliches Wissen ist, damit ich es später untersuchen und aktualisieren kann).

ARG_MAX Verwirrung

Seit Linux 2.6.23 (als Ergebnis von this commit ) wurden Änderungen an der Art und Weise vorgenommen, wie Befehlsargumentmaxima behandelt werden, wodurch sich Linux von anderen Unix-ähnlichen Systemen unterscheidet. Zusätzlich zum Hinzufügen von MAX_ARG_STRLEN Und MAX_ARG_STRINGS Hängt das Ergebnis von getconf ARG_MAX Jetzt von der Stapelgröße ab und kann sich von ARG_MAX In limits.h Unterscheiden. ].

Normalerweise ist das Ergebnis von getconf ARG_MAX1/4 Der Stapelgröße. Betrachten Sie Folgendes in bash mit ulimit, um die Stapelgröße zu erhalten:

$ echo $(( $(ulimit -s)*1024 / 4 ))  # ulimit output in KiB
2097152
$ getconf ARG_MAX
2097152

Das obige Verhalten wurde jedoch durch dieses commit (hinzugefügt in Linux 2.6.25-rc4 ~ 121) geringfügig geändert. ARG_MAX In limits.h Dient nun als harte Untergrenze für das Ergebnis von getconf ARG_MAX. Wenn die Stapelgröße so eingestellt ist, dass 1/4 Der Stapelgröße in ARG_MAX Kleiner als limits.h Ist, wird der Wert limits.h Verwendet:

$ 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

Beachten Sie auch, dass, wenn die Stapelgröße niedriger als das minimal mögliche ARG_MAX Eingestellt ist, die Größe des Stapels (RLIMIT_STACK) Die Obergrenze der Argument-/Umgebungsgröße vor E2BIG Wird. wird zurückgegeben (obwohl getconf ARG_MAX weiterhin den Wert in limits.h anzeigt).

Als letztes ist zu beachten, dass wenn der Kernel ohne CONFIG_MMU (Unterstützung für Speicherverwaltungshardware) erstellt wird, die Überprüfung von ARG_MAX Deaktiviert ist, sodass das Limit nicht gilt. Obwohl MAX_ARG_STRLEN Und MAX_ARG_STRINGS Immer noch gelten.

Weiterführende Literatur

53
Graeme

In eglibc-2.18/NEWS

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

In 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;

In linux/include/uapi/linux/limits.h

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

Und 131072 Ist Ihre $(getconf ARG_MAX)/16-1, vielleicht sollten Sie bei 0 beginnen.

Sie haben es mit glibc und Linux zu tun. Es wäre gut, getconf auch zu patchen, um den "richtigen" ARG_MAX Wert zurückzugeben.

Edit :

Um ein wenig zu klären (nach einer kurzen, aber heißen Diskussion)

Die in ARG_MAX Definierte Konstante limits.h Gibt die maximale Länge eines mit exec übergebenen Arguments an.

Der Befehl getconf ARG_MAX Gibt den Maximalwert der Größe der kumulierten Argumente und der Umgebungsgröße zurück, die an exec übergeben wurden.

0
user55518