Fiquei com a impressão de que o tamanho máximo de um único argumento não era o problema aqui, tanto quanto o tamanho total da matriz geral de argumentos mais o tamanho do ambiente, ao qual está limitado ARG_MAX
. Assim, pensei que algo como o seguinte seria bem-sucedido:
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
Sendo o suficiente para explicar a diferença entre o tamanho do ambiente no shell e o echo
processo. Em vez disso, recebi o erro:
bash: /bin/echo: Argument list too long
Depois de brincar um pouco, descobri que o máximo era uma ordem hexadecimal completa de magnitude menor:
/bin/echo \
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
>/dev/null
Quando o menos um é removido, o erro retorna. Aparentemente, o máximo para um único argumento é realmente ARG_MAX/16
e as -1
contas para o byte nulo são colocadas no final da cadeia de caracteres na matriz de argumentos.
Outra questão é que, quando o argumento é repetido, o tamanho total da matriz de argumentos pode estar mais próximo ARG_MAX
, mas ainda não está lá:
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
O uso "${args[0]:6533}"
aqui torna o último argumento 1 byte mais longo e fornece o Argument list too long
erro. É improvável que essa diferença seja explicada pelo tamanho do ambiente fornecido:
$ cat /proc/$$/environ | wc -c
1045
Questões:
- Esse comportamento é correto ou existe algum bug em algum lugar?
- Caso contrário, esse comportamento está documentado em algum lugar? Existe outro parâmetro que define o máximo para um único argumento?
- Esse comportamento é limitado ao Linux (ou mesmo a versões específicas)?
- O que explica a discrepância adicional de ~ 5 KB entre o tamanho máximo real da matriz de argumentos mais o tamanho aproximado do ambiente e
ARG_MAX
?
Informação adicional:
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
getconf ARG_MAX
depende da correnteulimit -s
. Defina-o como ilimitado e obtenha um incrível 4611686018427387903 para ARG_MAX.Respostas:
Respostas
O parâmetro que define o tamanho máximo para um argumento é
MAX_ARG_STRLEN
. Não há documentação para esse parâmetro além dos comentários embinfmts.h
:Como é mostrado, o Linux também tem um limite (muito grande) no número de argumentos para um comando.
Um limite no tamanho de um único argumento (que difere do limite geral de argumentos mais ambiente) parece ser específico para o Linux. Este artigo fornece uma comparação detalhada
ARG_MAX
e equivalentes em sistemas similares ao Unix.MAX_ARG_STRLEN
é discutido para Linux, mas não há menção de equivalente em nenhum outro sistema.O artigo acima também afirma que
MAX_ARG_STRLEN
foi introduzido no Linux 2.6.23, juntamente com várias outras alterações relacionadas aos máximos dos argumentos de comando (discutidos abaixo). O log / diff para o commit pode ser encontrado aqui .Ainda não está claro o que explica a discrepância adicional entre o resultado
getconf ARG_MAX
e o tamanho máximo possível real dos argumentos mais o ambiente. A resposta relacionada a Stephane Chazelas sugere que parte do espaço é explicada por ponteiros para cada uma das seqüências de argumento / ambiente. No entanto, minha própria investigação sugere que esses ponteiros não são criados no início daexecve
chamada do sistema, quando ainda pode retornar umE2BIG
erro ao processo de chamada (embora os ponteiros para cadaargv
cadeia de caracteres certamente sejam criados posteriormente).Além disso, as seqüências de caracteres são contíguas na memória, tanto quanto eu posso ver, portanto, não há lacunas de memória devido ao alinhamento aqui. Embora seja muito provável que seja um fator no que quer que consuma memória extra. Para entender o que usa o espaço extra, é necessário um conhecimento mais detalhado de como o kernel aloca memória (o que é um conhecimento útil, por isso vou investigar e atualizar mais tarde).
Confusão ARG_MAX
Desde o Linux 2.6.23 (como resultado dessa confirmação ), houve mudanças na maneira como os máximos dos argumentos de comando são tratados, o que faz o Linux diferir de outros sistemas do tipo Unix. Além de adicionar
MAX_ARG_STRLEN
eMAX_ARG_STRINGS
, o resultado degetconf ARG_MAX
agora depende do tamanho da pilha e pode ser diferente daARG_MAX
delimits.h
.Normalmente o resultado de
getconf ARG_MAX
será1/4
do tamanho da pilha. Considere o seguinte embash
usarulimit
para obter o tamanho da pilha:No entanto, o comportamento acima foi ligeiramente alterado por esse commit (adicionado no Linux 2.6.25-rc4 ~ 121).
ARG_MAX
emlimits.h
agora serve como uma inferior com força ligado no resultado degetconf ARG_MAX
. Se o tamanho da pilha for definido de forma que1/4
o tamanho da pilha seja menor queARG_MAX
emlimits.h
, olimits.h
valor será usado:Observe também que, se o tamanho da pilha definido for menor que o mínimo possível
ARG_MAX
, o tamanho da pilha (RLIMIT_STACK
) se tornará o limite superior do tamanho do argumento / ambiente antes deE2BIG
ser retornado (emboragetconf ARG_MAX
ainda mostre o valor emlimits.h
).Uma coisa final a ser observada é que, se o kernel for construído sem
CONFIG_MMU
(suporte para hardware de gerenciamento de memória), a verificação deARG_MAX
será desativada, para que o limite não se aplique. EmboraMAX_ARG_STRLEN
eMAX_ARG_STRINGS
ainda se aplique.Leitura adicional
ARG_MAX
(e equivalentes) em outros sistemas similares ao Unix - http://www.in-ulm.de/~mascheck/various/argmax/MAX_ARG_STRLEN
causou um erro no Automake, que estava incorporando scripts de shell no Makefiles usandosh -c
- http://www.mail-archive.com/[email protected]/msg05522.htmlfonte
No
eglibc-2.18/NEWS
No
eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff
No
linux/include/uapi/linux/limits.h
E
131072
é seu$(getconf ARG_MAX)/16-1
, talvez você deva começar em 0.Você está lidando com glibc e Linux. Seria bom corrigir o getconf também para obter o
ARG_MAX
valor "correto" retornado.Editar:
Para esclarecer um pouco (após uma discussão curta, mas quente)
A
ARG_MAX
constante definida emlimits.h
fornece o comprimento máximo de um argumento passado com exec.O
getconf ARG_MAX
comando retorna o valor máximo do tamanho dos argumentos acumulados e do tamanho do ambiente passado para o exec.fonte
eglibc-2.18/NEWS
snippet? Seria bom atribuir isso a uma versão específica do kernel.getconf ARG_MAX
é sobre o tamanho cumulativo de arg + env (variável no Linux recente, vejaulimit -s
e a outra pergunta que eu vinculei), não é sobre o comprimento máximo de um único argumento para o qual não há consulta sysconf / getconf.Portanto, o @StephaneChazelas me corrige corretamente nos comentários abaixo - o próprio shell não determina de forma alguma o tamanho máximo do argumento permitido pelo seu sistema, mas é definido pelo seu kernel.
Como vários outros já disseram, parece que o kernel limita a 128kb o tamanho máximo de argumento que você pode entregar a um novo processo de qualquer outro quando o executar pela primeira vez. Você enfrenta esse problema especificamente devido aos muitos
$(command substitution)
subshells aninhados que devem ser executados no local e entregar a totalidade de sua saída de um para o outro.E essa é uma espécie de palpite, mas como a discrepância de ~ 5kb parece tão próxima do tamanho padrão da página do sistema, minha suspeita é que ela seja dedicada ao uso da página
bash
para lidar com o subshell$(command substitution)
necessário para finalmente fornecer sua saída e / ou a pilha de funções que ele emprega ao associar osarray table
seus dados. Só posso assumir que nem vem de graça.Demonstro abaixo que, embora possa ser um pouco complicado, é possível passar valores de variáveis de shell muito grandes para novos processos na chamada, desde que você consiga transmiti-lo.
Para fazer isso, usei principalmente tubos. Mas também avaliei o array de shell em Resultados
here-document
apontadoscat's stdin.
abaixo.Mas uma última observação - se você não precisa de código portátil, parece-me que isso
mapfile
pode simplificar um pouco seus trabalhos de shell.Possivelmente, você pode dobrar isso e fazê-lo novamente se fizer isso em fluxos - não sou mórbido o suficiente para descobrir - mas definitivamente funciona se você transmitir.
Tentei alterar a
printf
parte do gerador na linha dois para:Também funciona:
Então, talvez eu esteja um pouco mórbida. Eu uso
zero padding here
e adiciono o"$arg"
valor anterior ao"$arg"
valor atual . Eu vou muito além de 6500 ...E se eu mudar a
cat
linha para ficar assim:Posso obter contagens de bytes em
wc.
Lembre-se de que esses são os tamanhos de cada chave naargs
matriz. O tamanho total da matriz é a soma de todos esses valores.fonte
echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null
funcionará bem. Somente quando você usa um comando externo é que existe um problema.bash
comprimindo de alguma forma?printf
é um builtin, portanto não é executado e, AFAICT, seucat
argumento não é fornecido.