web-dev-qa-db-ja.com

コマンドの単一の引数の最大サイズを定義するものは何ですか?

ここでは、単一の引数の最大長は、引数配列全体の合計サイズと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

しばらく遊んでみたところ、最大値が完全な16進数で1桁小さいことがわかりました。

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

マイナス1が削除されると、エラーが返されます。単一の引数の最大値は実際にはARG_MAX/16であり、-1は引数配列内の文字列の末尾に配置されたnullバイトを考慮しているようです。

別の問題は、引数が繰り返されると、引数配列の合計サイズが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

質問:

  1. これは正しい動作ですか、それともどこかにバグがありますか?
  2. そうでない場合、この動作はどこかに文書化されていますか?単一の引数の最大値を定義する別のパラメーターはありますか?
  3. この動作はLinux(またはそのような特定のバージョン)に限定されていますか?
  4. 引数配列の実際の最大サイズと環境のおおよそのサイズとARG_MAXの間の追加の〜5KBの不一致を説明するものは何ですか?

追加情報:

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

答え

  1. 間違いなくバグではありません。
  2. 1つの引数の最大サイズを定義するパラメーターはMAX_ARG_STRLENです。 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
    

    示されているように、Linuxにはコマンドへの引数の数にも(非常に大きな)制限があります。

  3. 単一の引数のサイズの制限(引数と環境の全体的な制限とは異なります)は、Linuxに固有のようです。これ article は、UNIXのようなシステムでのARG_MAXと同等の詳細な比較を提供します。 LinuxについてはMAX_ARG_STRLENについて説明していますが、他のシステムでは同等のものについての言及はありません。

    上記の記事では、MAX_ARG_STRLENがLinux 2.6.23で導入されたこと、およびコマンド引数の最大値に関連するその他の変更点についても説明しています(以下で説明します)。コミットのログ/差分は here にあります。

  4. getconf ARG_MAXの結果と引数と環境の実際の最大可能サイズとの間の追加の不一致を説明するものはまだ明確ではありません。 Stephane Chazelasの関連する回答 は、スペースの一部が各引数/環境文字列へのポインターによって占められていることを示唆しています。ただし、私自身の調査によると、これらのポインターは、呼び出しプロセスにE2BIGエラーを返す可能性がある場合、execveシステムコールの早い段階では作成されません(ただし、各argv文字列へのポインターは確実に後で作成されます)。

    また、文字列は私が見る限りではメモリ内で連続しているため、ここで調整を行うことによるメモリギャップはありません。 does内の要素である可能性が非常に高いですが、余分なメモリを使い果たします。余分なスペースを何が使用するかを理解するには、カーネルがメモリを割り当てる方法に関するより詳細な知識が必要です(これは知っておくと役立つ知識なので、後で調査して更新します)。

ARG_MAXの混乱

Linux 2.6.23以降( this commit の結果として)、コマンド引数の最大値の処理方法が変更され、Linuxが他のUnixライクなシステムと異なっている。 MAX_ARG_STRLENMAX_ARG_STRINGSの追加に加えて、getconf ARG_MAXの結果はスタックサイズに依存するようになり、ARG_MAXlimits.hとは異なる場合があります。

通常、getconf ARG_MAXの結果はスタックサイズの1/4になります。スタックサイズを取得するためにbashを使用して、ulimitで以下を検討します。

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

ただし、上記の動作はこれによって若干変更されました commit (Linux 2.6.25-rc4〜121で追加)。 ARG_MAXlimits.hgetconf ARG_MAXの結果のハード下限として機能するようになりました。スタックサイズの1/4ARG_MAXlimits.hより小さいようにスタックサイズが設定されている場合、limits.h値が使用されます。

$ 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

スタックサイズが可能な最小ARG_MAXよりも小さい場合、スタックのサイズ(RLIMIT_STACK)は、E2BIGが返される前に引数/環境サイズの上限になることにも注意してください(ただし、getconf ARG_MAXにはlimits.hの値が表示されます)。

最後に注意すべきことは、カーネルがCONFIG_MMU(メモリ管理ハードウェアのサポート)なしでビルドされている場合、ARG_MAXのチェックが無効になっているため、制限は適用されません。ただし、MAX_ARG_STRLENおよびMAX_ARG_STRINGSは引き続き適用されます。

参考文献

53
Graeme

_eglibc-2.18/NEWS_内

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

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

_linux/include/uapi/linux/limits.h_内

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

そして、_131072_は$(getconf ARG_MAX)/16-1です。おそらく0から始めるべきです。

あなたはglibcとLinuxを扱っています。 「正しい」_ARG_MAX_値が返されるようにするには、getconfにもパッチを適用するとよいでしょう。

編集:

少し明確にするために(短いが熱い議論の後)

_ARG_MAX_で定義されている_limits.h_定数は、execで渡される1つの引数の最大長を示します。

_getconf ARG_MAX_コマンドは、execに渡された引数の合計サイズと環境サイズの最大値を返します。

0
user55518