कमांड एकल तर्क के लिए अधिकतम आकार क्या परिभाषित करता है?


48

मैं इस धारणा के अधीन था कि किसी एकल तर्क की अधिकतम लंबाई यहाँ समस्या नहीं थी, क्योंकि कुल तर्क सरणी का कुल आकार और पर्यावरण का आकार, जो कि सीमित है 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

थोड़ी देर तक खेलने के बाद, मैंने पाया कि अधिकतम छोटे आकार का एक पूर्ण हेक्स क्रम था:

/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

प्रशन:

  1. क्या यह सही व्यवहार है, या कहीं एक बग है?
  2. यदि नहीं, तो क्या यह व्यवहार कहीं प्रलेखित है? क्या एक और पैरामीटर है जो एक एकल तर्क के लिए अधिकतम परिभाषित करता है?
  3. क्या यह व्यवहार लिनक्स (या ऐसे विशेष संस्करण) तक सीमित है?
  4. तर्क सरणी के वास्तविक अधिकतम आकार और पर्यावरण के अनुमानित आकार के बीच अतिरिक्त ~ 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

5
लिनक्स पर, यह 32 पृष्ठों (128kiB) के लिए कठिन कोडित है। स्रोत में MAX_ARG_STRLEN देखें।
स्टीफन चेज़लस


1
कम से कम मेरी मशीन getconf ARG_MAXपर, वर्तमान पर निर्भर करता है ulimit -s। इसे असीमित पर सेट करें, और ARG_MAX के लिए एक अद्भुत 4611686018427387903 प्राप्त करें।
derobert


आप पथ / खरीद / $ $ / environ का उपयोग क्यों करते हैं? linux में procfs सिमलिंक / proc / self का समर्थन करता है, तो आप / proc / self / environ का उपयोग कर सकते हैं। सभी पैच को संसाधित करने के लिए सौंपा गया है, जब एक ही प्रक्रिया यह जांचती है, तो / proc / self को इंगित करती है। वही devfs के साथ है, उदाहरण के लिए अंदर / देव, डिवाइस stdout को fd / 1 के लिए सहानुभूति है, लेकिन fd अंक / स्वयं / fd को इंगित करता है। कई सिस्टम इस व्यवहार की नकल करते हैं।
ज़नीक

जवाबों:


48

जवाब

  1. निश्चित रूप से बग नहीं।
  2. वह पैरामीटर जो एक तर्क के लिए अधिकतम आकार को परिभाषित करता है 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
    

    जैसा कि दिखाया गया है, कमांड में तर्कों की संख्या पर लिनक्स की एक (बहुत बड़ी) सीमा होती है।

  3. एकल तर्क के आकार पर एक सीमा (जो तर्कों और पर्यावरण पर समग्र सीमा से भिन्न होती है) लिनक्स के लिए विशिष्ट प्रतीत होती है। यह लेखARG_MAX यूनिक्स जैसी प्रणालियों पर एक विस्तृत तुलना और समानता देता है । MAX_ARG_STRLENलिनक्स के लिए चर्चा की है, लेकिन किसी भी अन्य सिस्टम पर किसी भी समकक्ष का कोई उल्लेख नहीं है।

    उपरोक्त आलेख में यह भी कहा MAX_ARG_STRLENगया है कि कमांड लॉक्स मैक्सिमम (नीचे चर्चा की गई) से संबंधित कई अन्य परिवर्तनों के साथ लिनक्स 2.6.23 में पेश किया गया था। कमिट के लिए लॉग / डिफरेंस यहां पाया जा सकता है

  4. यह अभी भी स्पष्ट नहीं है कि getconf ARG_MAXतर्कों और पर्यावरण के वास्तविक अधिकतम संभव आकार के परिणाम के बीच अतिरिक्त विसंगति के लिए क्या खाते हैं। स्टीफन चेज़ेलस के संबंधित उत्तर से पता चलता है कि अंतरिक्ष के हिस्से को प्रत्येक तर्क / पर्यावरणीय स्ट्रिंग के लिए संकेतकर्ता द्वारा हिसाब किया जाता है। हालाँकि, मेरी स्वयं की जांच बताती है कि ये संकेत execveसिस्टम कॉल में जल्दी नहीं बनते हैं जब यह अभी भी E2BIGकॉलिंग प्रक्रिया में त्रुटि लौटा सकता है (हालांकि प्रत्येक argvस्ट्रिंग के लिए संकेत निश्चित रूप से बाद में बनाए जाते हैं)।

    इसके अलावा, तार जहाँ तक मैं देख सकता हूँ स्मृति में सन्निहित हैं, इसलिए यहाँ संरेखण करने के कारण कोई भी स्मृति अंतराल नहीं है। हालांकि जो भी अतिरिक्त मेमोरी का उपयोग करता है उसके भीतर एक कारक होने की बहुत संभावना है । यह समझने से कि अतिरिक्त स्थान का उपयोग करने के लिए कर्नेल को मेमोरी कैसे आवंटित की जाती है (जो कि उपयोगी ज्ञान है, इसलिए मैं बाद में जांच और अद्यतन करूँगा) के अधिक विस्तृत ज्ञान की आवश्यकता है।

ARG_MAX भ्रम

चूंकि Linux 2.6.23 ( इस कमिट के परिणाम के रूप में ) में, इस तरह से बदलाव किए गए हैं कि कमांड लॉजिक मैक्सिमम को हैंडल किया जाता है जो लिनक्स को अन्य यूनिक्स जैसी प्रणालियों से अलग बनाता है। जोड़ने MAX_ARG_STRLENऔर इसके अलावा MAX_ARG_STRINGS, का परिणाम getconf ARG_MAXस्टैक आकार पर निर्भर करता है और इसमें से भिन्न हो सकता ARG_MAXहै limits.h

आम तौर पर का परिणाम getconf ARG_MAXहो सकता है 1/4ढेर आकार की। में निम्नलिखित पर विचार करें bashका उपयोग कर ulimitढेर आकार पाने के लिए:

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

हालांकि, इसके बाद के संस्करण व्यवहार इस से थोड़ा बदल गया था प्रतिबद्ध (लिनक्स 2.6.25-RC4 ~ 121 में जोड़ा गया)। अब के परिणाम पर एक कठिन कम बाध्य के रूप ARG_MAXमें limits.hकार्य करता है getconf ARG_MAX। ढेर आकार इस तरह के सेट है, तो यह है कि 1/4ढेर आकार की तुलना में कम है ARG_MAXमें limits.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अभी भी लागू होते हैं।

आगे की पढाई

  • स्टीफन चेज़लस द्वारा संबंधित उत्तर - https://unix.stackexchange.com/a/110301/48083
  • ऊपर दिए गए अधिकांश पेज को कवर करते हुए। ARG_MAXअन्य यूनिक्स जैसी प्रणालियों पर (और समतुल्य) मूल्यों की एक तालिका शामिल है - http://www.in-ulm.de/~mascheck/various/argmax/
  • MAX_ARG_STRLENस्वचालित रूप से बग के कारण बग की शुरूआत, जो मेकफाइल्स में शेल स्क्रिप्ट का उपयोग कर रहा था sh -c- http://www.mail-archive.com/bug-make@gnu.org/msg05522.html

2
यह एक अच्छा जवाब है, निश्चित रूप से मेरी तुलना में बेहतर है - मैंने इसे बढ़ा दिया। लेकिन जो जवाब हम हमेशा नहीं मांगते हैं, उसका जवाब हमें मिलना चाहिए - यही कारण है कि हम पूछ रहे हैं, क्योंकि हम नहीं जानते। यह आपके काम के प्रवाह के साथ समस्या को संबोधित नहीं करता है जो आपको पहली बार इस मुद्दे के साथ सिर पर लाया है। मैं प्रदर्शित करता हूं कि मेरे स्वयं के उत्तर में इसे कैसे कम किया जा सकता है, और शेल में 2mbs से अधिक एकल शेल चर स्ट्रिंग तर्क को शेल स्क्रिप्ट की सिर्फ एक दो पंक्तियों के साथ नव निष्पादित प्रक्रियाओं में कैसे पारित किया जा सकता है।
मोकेसर

मैंने एक पायथन स्क्रिप्ट बनाई है जो डिफ़ॉल्ट लिनक्स पर 32 * 4KB पेज = 128 KB पर्यावरण चर की सीमा प्रदर्शित करती है।
nh2

0

में 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मान वापस करने के लिए गेटकॉन्फ़ को भी पैच करना अच्छा होगा ।

संपादित करें:

थोड़ा स्पष्ट करने के लिए (एक छोटी लेकिन गर्म चर्चा के बाद)

जिस ARG_MAXस्थिरांक में परिभाषित किया गया है limits.h, निष्पादन के साथ पारित एक तर्क की अधिकतम लंबाई देता है।

getconf ARG_MAXआदेश संचयी तर्क आकार और पर्यावरण आकार कार्यकारी के लिए पारित की अधिकतम मान देता है।


2
यह ARG_MAX, arg + env आकार की सीमा के लिए न्यूनतम गारंटी है, यह एक एकल तर्क का अधिकतम आकार नहीं है (हालांकि यह MAX_ARG_STRLEN के समान मूल्य का होता है)
स्टीफन चेज़लर

क्या आपके पास अपने eglibc-2.18/NEWSस्निपेट के लिए कोई तारीख है ? यह एक विशेष कर्नेल संस्करण के लिए नीचे पिन करने के लिए अच्छा होगा।
ग्रीम

@ स्टेफ़ेनचेज़लस: मैं भाग खोजने के लिए बहुत आलसी हूँ, लेकिन यदि arg अधिकतम मान से अधिक है तो यह आवश्यक नहीं है कि env आकार का पता लगाना।

@ ग्रेमी: मेरे पास कुछ पुराने लिनेक्स भी हैं, जहां गेटकॉफ वैल्यू 131072 दिखाता है। मुझे लगता है कि यह नए लिग्नेश से संबंधित है>> केवल। बधाई हो, आपको एक बग BTW मिला।

2
आप glibc कोड को देख रहे हैं, यह यहाँ अप्रासंगिक है। Libc यह परवाह नहीं करता कि आप किस तर्क से गुजर रहे हैं। जिस कोड को आप उद्धृत कर रहे हैं, वह sysconf के बारे में है, उपयोगकर्ताओं को अधिकतम आकार (जो भी मतलब है) का एक विचार देने के लिए एक एपीआई (2) argv + env को दिया गया। यह कर्नेल है जो एक्जीक्यूट () सिस्टम कॉल के साथ पारित arg और env सूची को स्वीकार या नहीं करता है। getconf ARG_MAXआर्ग + env की संचयी आकार के बारे में है (हाल ही में लिनक्स में चर, देखने ulimit -sऔर अन्य प्रश्न मैं जुड़ा हुआ), यह एक एकल आर्ग जिसके लिए कोई sysconf / getconf क्वेरी की अधिकतम लंबाई के बारे में नहीं है।
स्टीफन चेज़लस

-1

इसलिए @StephaneChazelas ने मुझे नीचे दी गई टिप्पणियों में सही ढंग से ठीक किया - शेल स्वयं किसी भी तरह से आपके सिस्टम द्वारा अधिकतम तर्क आकार की अनुमति नहीं देता है, बल्कि यह आपके कर्नेल द्वारा सेट किया गया है।

जैसा कि कई अन्य लोग पहले ही कह चुके हैं, यह कर्नेल की सीमा 128kb है जो अधिकतम तर्क आकार को आप किसी अन्य प्रक्रिया से पहली बार निष्पादित करने पर किसी नई प्रक्रिया को सौंप सकते हैं। आप इस समस्या का अनुभव विशेष रूप से कई नेस्टेड $(command substitution)उपखंडों के कारण करते हैं जो एक स्थान से दूसरे स्थान पर निष्पादित होना चाहिए और एक से दूसरे तक उनके आउटपुट की संपूर्णता को सौंपना चाहिए।

और यह एक जंगली अनुमान है, लेकिन जैसा कि ~ 5 केबी विसंगति मानक प्रणाली पृष्ठ आकार के इतने करीब लगती है, मेरा संदेह यह है कि यह पृष्ठ के bashउपयोग के लिए समर्पित है जो आपके द्वारा $(command substitution)आवश्यक रूप से इसके उत्पादन और / या वितरित करने के लिए आवश्यक उपधारा को संभालने के लिए उपयोग करता है। फ़ंक्शन स्टैक यह array tableआपके डेटा के साथ जुड़ने में कार्यरत है । मैं केवल यह मान सकता हूं कि न तो कोई स्वतंत्र है।

मैं नीचे प्रदर्शित करता हूं, जबकि यह थोड़ा मुश्किल हो सकता है, आह्वान पर नई प्रक्रियाओं के लिए बहुत बड़े शेल चर मानों को पारित करना संभव है, इसलिए जब तक आप इसे स्ट्रीम करने का प्रबंधन कर सकते हैं।

ऐसा करने के लिए, मैंने मुख्य रूप से पाइप का इस्तेमाल किया। लेकिन मैंने नीचे दिए गए परिणामों here-documentपर एक बिंदु में शेल सरणी का मूल्यांकन किया cat's stdin.

लेकिन एक आखिरी नोट - अगर आपको पोर्टेबल कोड की कोई विशेष आवश्यकता नहीं है, तो यह मुझे चौंका देता है जो mapfileआपके शेल नौकरियों को थोड़ा सरल कर सकता है।

time bash <<-\CMD
    ( for arg in `seq 1 6533` ; do
        printf 'args+=(' ; printf b%.0b `seq 1 6533` ; echo ')'
    done ;
    for arg in `seq 1 6533` ; do
        printf %s\\n printf\ '%s\\n'\ \""\${args[$arg]}"\" ;
    done ) | . /dev/stdin >&2
CMD
bash <<<''  66.19s user 3.75s system 84% cpu 1:22.65 total

संभवतः आप इसे दोगुना कर सकते हैं और फिर इसे फिर से कर सकते हैं यदि आपने इसे धाराओं में किया है - मैं यह पता लगाने के लिए पर्याप्त रूप से रुग्ण नहीं हूं - लेकिन निश्चित रूप से यह काम करता है यदि आप इसे स्ट्रीम करते हैं।

मैंने printfजनरेटर भाग को दो लाइन में बदलने की कोशिश की:

printf \ b%.0b

यह भी काम करता है:

bash <<<''  123.78s user 5.42s system 91% cpu 2:20.53 total

तो शायद मैं थोड़ा रुग्ण हूं। मैं zero padding hereपिछले "$arg"मूल्य में वर्तमान "$arg"मूल्य का उपयोग करता हूं और जोड़ता हूं । मैं 6500 से आगे का रास्ता ...

time bash <<-\CMD
    ( for arg in `seq 1 33` ; do
        echo $arg >&2
        printf 'args+=('"${args[$((a=arg-1))]}$(printf "%0${arg}0d" \
            `seq 1 6533` ; printf $((arg-1)))"')\n'
    done ;
    for arg in `seq 1 33` ; do
        printf '/usr/bin/cat <<HERE\n%s\nHERE\n' "\${args[$arg]}"
    done ) | . /dev/stdin >&2
CMD

bash <<<''  14.08s user 2.45s system 94% cpu 17.492 total

और अगर मैं catइस तरह दिखने के लिए लाइन बदलता हूं :

printf '/usr/bin/cat <<HERE | { printf '$arg'\  ; wc -c ;}
    %s\nHERE\n' "\${args[$arg]}"

मुझे wc.याद है कि इन argsसरणी में प्रत्येक कुंजी के आकार हैं बाइट मायने रखता है । सरणी का कुल आकार इन सभी मूल्यों का योग है।

1 130662
2 195992
3 261322
4 326652
5 391982
6 457312
7 522642
8 587972
9 653302
10 718633
11 783963
12 849293
13 914623
14 979953
15 1045283
16 1110613
17 1175943
18 1241273
19 1306603
20 1371933
21 1437263
22 1502593
23 1567923
24 1633253
25 1698583
26 1763913
27 1829243
28 1894573
29 1959903
30 2025233
31 2090563
32 2155893
33 2221223

2
नहीं, शेल के साथ कुछ भी नहीं करना है, यह निष्पादित (2) सिस्टम कॉल है E2BIG जब एक भी तर्क 128kiB से अधिक है।
स्टीफन चेजलस

यह भी विचार करें कि शेल बिल्डरों पर कोई सीमा नहीं है - echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/nullठीक चलेगा। यह केवल तभी होता है जब आप किसी बाहरी कमांड का उपयोग करते हैं कि कोई समस्या है।
ग्रीम

@Graeme खैर, मैंने यह बिल्ली के साथ भी किया - कोई बात नहीं। चर का मूल्यांकन अंत में एक हेरेडोक में किया जाता है। मेरा अंतिम संपादन देखें। मैंने कुल संख्या 33 में कटौती की क्योंकि मैं हर बार अंतिम मूल्य में जोड़ रहा हूं। और शून्य गद्दी ...
mikeserv

@ स्टेफ़ेनचेज़लस - तो क्या मैं एक हेरेडोक स्ट्रीम में तर्क का मूल्यांकन करके चारों ओर हो रहा हूं? या bashइसे किसी तरह से संकुचित कर रहा है?
मोकेसर

1
@mikeserv, मैं कहीं भी नहीं देख सकता हूँ कि आप किसी भी उदाहरण को बड़े arg सूची के साथ कमांड निष्पादित करने के लिए कोड कर रहे हैं। printfएक बिलिन है, इसलिए निष्पादित नहीं किया जाता है , और AFAICT, आपके catकिसी भी तर्क को नहीं दिया जाता है।
स्टीफन चेज़लस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.