내 생각에 여기서는 단일 매개변수의 최대 길이가 문제가 아니라 전체 매개변수 배열의 총 크기와 환경 크기( 로 제한됨)가 문제인 것 같습니다 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
음수가 제거되면 오류가 반환됩니다. 단일 매개변수의 최대값으로 나타나는 것은 실제로 매개변수 배열의 문자열 끝에 있는 널 바이트의 합계 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(또는 특정 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
답변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에는 명령이 사용할 수 있는 인수 수에 대한 (매우 큰) 제한이 있습니다.
개별 매개변수의 크기에 대한 제한(매개변수와 환경에 대한 전체 제한과 반대)은 Linux에만 적용되는 것으로 보입니다. 이것기사Unix 계열 시스템에 대한 자세한 비교
ARG_MAX
및 이에 상응하는 내용이 제공됩니다.MAX_ARG_STRLEN
토론은 Linux에 관한 것이지만 다른 시스템에 대한 언급은 없습니다.위 기사
MAX_ARG_STRLEN
에서는 Linux 2.6.23에 도입된 내용과 명령 매개변수 최대값과 관련된 기타 여러 변경 사항도 지적합니다(아래에서 설명). 커밋 로그/차이점을 찾을 수 있습니다.여기.getconf ARG_MAX
매개변수와 환경의 결과와 실제 가능한 최대 크기 사이에 추가적인 불일치가 발생하는 원인이 무엇인지는 확실하지 않습니다 .Stephane Chazelas의 관련 답변, 각 인수/환경 문자열에 대한 포인터가 공간의 일부를 차지함을 나타냅니다. 그러나 내 조사에 따르면 이러한 포인터는 시스템 호출 초기에 생성되지 않습니다 . 호출 프로세스에 오류를 반환하는 것이execve
여전히 가능하기 때문입니다 ( 각 문자열에 대한 포인터는 확실히 나중에 생성되지만).E2BIG
argv
또한, 제가 아는 한, 문자열은 메모리 내에서 연속되어 있으므로 여기서 정렬로 인한 메모리 공백은 없습니다. 요인이 될 수도 있지만하다추가 메모리가 부족합니다. 추가 공간을 사용하는 것이 무엇인지 이해하려면 커널이 메모리를 할당하는 방법에 대한 더 자세한 이해가 필요합니다(이것은 유용한 지식이므로 나중에 조사하고 업데이트하겠습니다).
ARG_MAX 카오스
Linux 2.6.23 이후(이번에 제출하세요), 명령 매개변수 최대값을 처리하는 방식이 변경되어 Linux가 다른 Unix 계열 시스템과 다릅니다. 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
. 스택 크기가 in 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
stack()의 크기는 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/ - 이 도입으로 인해 -를 사용하여 Makefile에 쉘 스크립트를 삽입하는
MAX_ARG_STRLEN
Automake에 버그가 발생한 것 같습니다.sh -c
http://www.mail-archive.com/[이메일 보호됨]/msg05522.html
답변2
존재하다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를 다루고 있습니다. "올바른" 값을 반환하려면 getconf도 패치하는 것이 가장 좋습니다 ARG_MAX
.
편집하다:
무엇인가를 명확히 하기 위해(간단하지만 강렬한 토론 후에)
ARG_MAX
에 정의된 상수는 limits.h
exec에 전달되는 인수의 최대 길이를 제공합니다.
이 getconf ARG_MAX
명령은 exec에 전달된 최대 누적 인수 크기와 환경 크기를 반환합니다.
답변3
따라서 @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