什麼定義了命令單個參數的最大大小?
我的印像是單個參數的最大長度在這裡不是問題,而是整個參數數組的總大小加上環境的大小,環境的大小限制為
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
足以說明 shell 中的環境大小和echo
程序之間的差異。相反,我得到了錯誤:bash: /bin/echo: Argument list too long
玩了一會兒後,我發現最大值是一個完整的十六進制數量級:
/bin/echo \ $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \ >/dev/null
當減一被刪除時,錯誤返回。看起來單個參數的最大值實際上是
ARG_MAX/16
和-1
放在參數數組中字元串末尾的空字節的帳戶。另一個問題是,當參數重複時,參數數組的總大小可能更接近
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
問題:
- 這是正確的行為,還是某處有錯誤?
- 如果沒有,這種行為是否記錄在任何地方?是否有另一個參數定義單個參數的最大值?
- 這種行為是否僅限於 Linux(甚至是特定版本)?
- 是什麼導致了參數數組的實際最大大小加上環境的近似大小之間額外的約 5KB 差異
ARG_MAX
?附加資訊:
uname -a Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
答案
- 絕對不是bug。
- 定義一個參數的最大大小的參數是
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 的。本文詳細比較了類 Unix 系統上的等價物。討論了 Linux,但沒有提到任何其他系統上的任何等效項。
ARG_MAX``MAX_ARG_STRLEN
上面的文章還說明了它
MAX_ARG_STRLEN
是在 Linux 2.6.23 中引入的,以及與命令參數最大值相關的一些其他更改(下面討論)。可以在此處找到送出的日誌/差異。 4. 目前尚不清楚是什麼導致了getconf ARG_MAX
參數加環境的結果與實際最大可能大小之間的額外差異。Stephane Chazelas 的相關回答表明,部分空間是由指向每個參數/環境字元串的指針來計算的。但是,我自己的調查表明,這些指針不是在execve
系統呼叫的早期創建的,因為它可能仍然E2BIG
向呼叫程序返回錯誤(儘管指向每個argv
字元串的指針肯定是稍後創建的)。此外,就我所見,字元串在記憶體中是連續的,因此這裡沒有由於對齊而導致的記憶體間隙。儘管很可能是消耗額外記憶體的一個因素。了解什麼使用額外空間需要更詳細地了解核心如何分配記憶體(這是有用的知識,因此我將在稍後進行調查和更新)。
ARG_MAX 混亂
自 Linux 2.6.23 以來(作為此送出的結果),處理命令參數最大值的方式發生了變化,這使得 Linux 與其他類 Unix 系統不同。除了
MAX_ARG_STRLEN
和之外MAX_ARG_STRINGS
, now 的結果getconf ARG_MAX
取決於堆棧大小,可能與ARG_MAX
in不同limits.h
。通常,結果
getconf ARG_MAX
將是1/4
堆棧大小。bash
在使用ulimit
獲取堆棧大小時考慮以下內容:$ echo $(( $(ulimit -s)*1024 / 4 )) # ulimit output in KiB 2097152 $ getconf ARG_MAX 2097152
但是,此送出(在 Linux 2.6.25-rc4~121 中添加)對上述行為進行了輕微更改。
ARG_MAX
inlimits.h
現在用作 的結果的硬下限getconf ARG_MAX
。如果設置堆棧大小使得1/4
堆棧大小小於ARG_MAX
inlimits.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
另請注意,如果堆棧大小設置為低於 minimum possible
ARG_MAX
,則堆棧 (RLIMIT_STACK
) 的大小將成為參數/環境大小的上限,然後E2BIG
返回(儘管getconf ARG_MAX
仍會顯示 中的值limits.h
)。最後要注意的是,如果核心是在沒有
CONFIG_MMU
(支持記憶體管理硬體)的情況下建構的,ARG_MAX
則禁用檢查,因此限制不適用。雖然MAX_ARG_STRLEN
並且MAX_ARG_STRINGS
仍然適用。延伸閱讀
- Stephane Chazelas 的相關回答 - https://unix.stackexchange.com/a/110301/48083
- 在涵蓋上述大部分內容的詳細頁面中。包括
ARG_MAX
其他類 Unix 系統上的(和等效的)值表 - http://www.in-ulm.de/~mascheck/various/argmax/- 似乎引入
MAX_ARG_STRLEN
Automake 導致了一個錯誤,該錯誤使用http://www.mail-archive.com/bug-make@gnu.org/msg05522.htmlsh -c
將 shell 腳本嵌入到 Makefile中