명령 단일 인수의 최대 크기를 정의하는 것은 무엇입니까?


47

전체 인수 배열의 총 크기에 환경의 크기를 더한 것만 큼 단일 인수의 최대 길이가 문제가되지 않았다는 인상을 받았습니다 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 진수 크기보다 작다는 것을 알았습니다.

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

빼기 1을 제거하면 오류가 반환됩니다. 겉으로는 하나의 인수에 대한 최대 값은 실제로 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. 이 동작이 Linux (또는 특정 버전)로 제한됩니까?
  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
Linux에서는 32 페이지 (128kiB)로 하드 코딩되어 있습니다. 소스에서 MAX_ARG_STRLEN을 참조하십시오.
Stéphane Chazelas


1
적어도 내 컴퓨터에서는 getconf ARG_MAXcurrent에 의존합니다 ulimit -s. 무제한으로 설정하고 ARG_MAX를 위해 놀라운 4611686018427387903을 얻으십시오.
derobert


왜 / proc / $$ / environ 경로를 사용합니까? 리눅스의 procfs는 symlink / proc / self를 지원하고, / proc / self / environ을 사용할 수 있습니다. 프로세스에 할당 된 모든 패치는 동일한 프로세스가이를 검사 할 때 / proc / self를 가리 킵니다. devfs와 동일합니다 (예 : / dev 내부). stdout 장치는 fd / 1로 심볼릭 링크되지만 fd는 / self / fd를 가리 킵니다. 많은 시스템이이 동작을 복사합니다.
Znik

답변:


47

답변

  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. 단일 인수의 크기 제한 (인수 및 환경에 대한 전체 제한과 다른)은 Linux에만 해당되는 것으로 보입니다. 이 기사ARG_MAX유닉스 계열 시스템에 대한 자세한 비교 와 동등한 내용을 제공 합니다. MAX_ARG_STRLENLinux에 대해서는 논의되었지만 다른 시스템에서는 이에 대한 언급이 없습니다.

    위의 기사는 또한 MAX_ARG_STRLENLinux 2.6.23에서 도입되었으며 명령 인수 최대 값 (아래에서 논의 됨)과 관련된 여러 가지 다른 변경 사항과 함께 설명합니다. 커밋에 대한 로그 / 차이는 여기 에서 찾을 수 있습니다 .

  4. getconf ARG_MAX인수의 실제 크기와 가능한 최대 크기 및 환경 간의 추가 불일치를 설명하는 것은 여전히 ​​명확하지 않습니다 . Stephane Chazelas의 관련 답변 에 따르면 공간의 일부는 각 인수 / 환경 문자열에 대한 포인터로 설명됩니다. 그러나 필자의 조사에 따르면 호출 프로세스에 execve여전히 E2BIG오류를 반환 할 수있을 때 시스템 호출 초기에 이러한 포인터가 생성되지 않는다고 제안합니다 (각 argv문자열에 대한 포인터 는 나중에 생성 되지만 ).

    또한 문자열은 내가 볼 수있는 한 메모리에서 연속적이므로 메모리 간격이 여기에 정렬되지 않습니다. 비록 매우 가능성이 무엇이든지 내에서 요소가 될 것입니다 않는 여분의 메모리를 사용합니다. 여분의 공간을 사용하는 것을 이해하려면 커널이 메모리를 할당하는 방법에 대한 더 자세한 지식이 필요합니다 (유용한 지식이므로 나중에 조사하고 업데이트하겠습니다).

ARG_MAX 혼란

Linux 2.6.23 이후 ( 이 커밋 결과 ) Linux에서 다른 유닉스 계열 시스템과 다른 명령 인수 최대 값 처리 방식이 변경되었습니다. 추가 외에 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

그러나 위의 동작은이 커밋에 의해 약간 변경되었습니다 (Linux 2.6.25-rc4 ~ 121에 추가됨). ARG_MAXlimits.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_STRLENMAX_ARG_STRINGS여전히 적용됩니다.

추가 자료


2
이것은 내 대답보다 확실히 좋은 대답입니다. 그러나 우리가 요구하는 답은 항상 우리가 받아야 할 답이 아닙니다. 그것이 우리가 모르는 이유 때문에 우리가 묻는 이유입니다. 처음에는이 문제에 대해 일대일을 일으킨 작업 흐름 문제를 해결하지 못합니다. 나는 그것이 내 자신의 대답에서 어떻게 완화 될 수 있는지, 그리고 길이가 2MB 이상인 단일 쉘 변수 문자열 인수가 단지 몇 줄의 쉘 스크립트로 새로 실행 된 프로세스로 전달되는 방법을 보여줍니다.
mikeserv

기본 Linux에서 환경 변수의 32 * 4KB 페이지 = 128KB 제한을 보여주는 Python 스크립트 를 만들었습니다 .
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값을 반환 하려면 getconf를 패치하는 것이 좋습니다 .

편집하다:

조금만 명확하게하기 위해 (짧지 만 뜨거운 토론 후)

ARG_MAX정의 된 상수는 limits.hexec와 함께 전달 된 하나의 인수의 최대 길이를 제공합니다.

getconf ARG_MAX명령은 exec에 전달 된 누적 인수 크기 및 환경 크기의 최대 값을 리턴합니다.


2
ARG_MAX는 arg + env 크기 제한에 대해 보장 된 최소값이며 단일 인수의 최대 크기가 아닙니다 (MAX_ARG_STRLEN과 동일한 값이 발생하지만)
Stéphane Chazelas

eglibc-2.18/NEWS스 니펫 날짜가 있습니까? 이것을 특정 커널 버전으로 고정하는 것이 좋습니다.
Graeme

@ StephaneChazelas : 부품을 찾기에는 너무 게으르지 만 arg가 최대 값을 초과하면 env 크기를 계산할 필요가 없습니다.

@Graeme : getconf 값이 131072로 표시되는 오래된 Linux도 있습니다. eglibc> ??를 사용하는 최신 Linux에 속한다고 생각합니다. 뿐. 축하합니다. 버그 BTW를 발견했습니다.

2
당신은 glibc 코드를보고 있는데, 여기에는 관련이 없습니다. libc는 전달하는 인수의 크기를 신경 쓰지 않습니다. 인용하는 코드는 sysconf에 관한 것입니다. API는 execve (2)에 전달 된 argv + env의 최대 크기 (무엇이든)에 대한 아이디어를 사용자에게 제공합니다. execve () 시스템 호출과 함께 전달되는 arg 및 env 목록을 수락하거나 허용하지 않는 커널입니다. 이것은 getconf ARG_MAXarg + env의 누적 크기 (최근 Linux에서 가변적이며 참조 ulimit -s한 다른 질문)에 관한 것입니다 .sysconf / getconf 쿼리가없는 단일 arg의 최대 길이는 아닙니다.
Stéphane Chazelas

-1

따라서 @StephaneChazelas는 아래 주석에서 나를 올바르게 수정합니다. 쉘 자체는 시스템이 허용하는 최대 인수 크기를 나타내지 않고 커널에 의해 설정됩니다.

다른 사람들이 이미 말했듯이 커널은 처음 실행할 때 다른 프로세스에서 다른 프로세스로 전달할 수있는 최대 인수 크기를 128kb로 제한하는 것으로 보입니다. 이 문제는 특히 중첩 된 많은 $(command substitution)서브 쉘이 제 위치에서 실행되어야하고 전체 출력을 하나에서 다음으로 넘겨야 하기 때문에 발생 합니다.

그리고 이것은 일종의 거친 추측이지만 ~ 5kb의 불일치가 표준 시스템 페이지 크기와 너무 비슷해 보이므로 궁극적으로 출력을 전달하는 데 필요한 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
아니요, 셸과 관련이 없으며 단일 인수가 128kiB를 초과하면 E2BIG를 반환하는 execve (2) 시스템 호출입니다.
Stéphane Chazelas

쉘 내장에 제한이 없다는 것을 고려하십시오- echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null잘 실행될 것입니다. 외부 명령을 사용하는 경우에만 문제가 있습니다.
Graeme

@Graeme 글쎄, 나는 고양이와 함께 이것을했다-아무 문제가 없습니다. 변수는 마지막에 heredoc에서 평가됩니다. 마지막 편집을 참조하십시오. 매번 마지막 값을 더하기 때문에 총 수를 33으로 줄였습니다. 그리고 제로 패딩 ...
mikeserv

@StephaneChazelas-heredoc 스트림에서 인수를 평가하여 그 문제를 해결하고 있습니까? 아니면 bash어떻게 든 압축하고 있습니까?
mikeserv

1
@ mikeserv, 큰 인수 목록이있는 명령을 실행하는 인스턴스를 코딩하는 곳은 어디에서도 볼 수 없습니다. printf는 내장되어 있으므로 실행 되지 않으며 AFAICT에는 cat인수가 제공되지 않습니다.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.