8.06.2011

Command line length limit in Solaris

Solaris: コマンドラインの長さ制限に関する調査

シェル上のコマンドラインから外部コマンドを実行する場合、そのパラメータが長すぎるとエラーとなってしまう。
大量のファイルがヒットするような場所でワイルドカードを使う場合と再現できるだろう。

$ rm -f ./*
-bash: /usr/gnu/bin/rm: Arg list too long

回避するには「xargs」を使えばいいのだが、実際のところ制限値はどれほどなのか。
それは「getconf ARG_MAX」コマンドで確認できる。

$ getconf ARG_MAX
1048320

bash などのシェルでは、ワイルドカードや変数を展開し
その後で子プロセス生成のためにシステムコール execve(2) を呼び出す。

この展開後のパラメータが制限値よりも長いと execve(2) がエラーとなる。これが「Arg list too long」である。

尚、パラメータには「env」コマンドで表示される環境変数も含まれているので注意。
従って実際の制限値は「getconf ARG_MAX」の結果から「env |wc -c」の結果を減算したものとなる。

以下のようなコマンドで検証してみる。

$ s=x; for i in {1..19}; do s=$s$s; /bin/echo $s; done > /dev/null
$ s=x; for i in {1..20}; do s=$s$s; /bin/echo $s; done > /dev/null
-bash: /bin/echo: Arg list too long

最初に変数「s」に「x」(適当な文字1文字)を代入。
for ループでその変数の長さを2倍にしていくと、n回目のループで変数sは2のn乗の長さに展開される。
そのパラメータを外部コマンドの echo に渡している。

文字列の長さが 2**19=524,288 文字までは問題なく、2**20=1,048,576 文字ではエラーが発生している
ことが分かる。

ちなみに、bash 組み込みコマンド(内部コマンド)の「echo」ではシステムコールが発生しないので
エラーは発生しない。

$ s=x; for i in {1..20}; do s=$s$s; echo $s; done > /dev/null

ループ回数を増やしていくとメモリを使い果たし、やがてメモリアロケーションエラーとなってしまった。
(これは ksh で実行)

$ s=x; for i in {1..100}; do s=$1 s$s; echo $s; done > /dev/null
-ksh: storage allocator out of space on 402653184 byte request ( region 545947648 segments 2 busy 315:358065680:223719752 free 2:187879352:187879336 ) [Resource temporarily unavailable]
-ksh: warning: vmbusy() inside job_reap() -- should not happen
-ksh: warning: vmbusy() inside job_reap() -- should not happen
-ksh: warning: vmbusy() inside job_reap() -- should not happen
-ksh: warning: vmbusy() inside job_reap() -- should not happen
^C-ksh: cannot fork [Interrupted system call]

参考:
http://www.cyberciti.biz/faq/argument-list-too-long-error-solution/

0 件のコメント:

コメントを投稿