Desfragmentando falha de RAM / OOM

11

Essa pergunta é bastante longa, então vou fazer as perguntas no topo e depois seguir meu método de chegar às perguntas:

  1. O rm (baseado no Busybox) não foi executado porque não havia RAM contígua suficiente?
  2. Em caso afirmativo, existe um método leve de desfragmentar o DMA - sem recorrer à reinicialização do sistema?
  3. Se não, o que causou isso? Como posso impedir que isso aconteça no futuro?

Depois que nosso sistema de teste esteve funcionando intensivamente nos últimos dias - eu liguei para o sistema e verifiquei os resultados do teste. Quando vim para excluir alguns dados, o sistema retornou a linha de comando (como se o comando tivesse sido executado corretamente). Quando cheguei a verificar o diretório para outro conjunto de resultados, vi que o arquivo ainda existia (usando ls).

Depois disso, notei que mais e mais dos meus comandos do shell não estavam funcionando conforme o esperado.

Começarei com uma saída do dmesg depois que o rm falhou ao executar corretamente:

Falha na alocação do comprimento 61440 do processo 6821 (rm)

DMA por CPU:

CPU 0: oi: 0, btch: 1 usd: 0

Active_anon: 0 active_file: 1 inactive_anon: 0 inactive_file: 0 inevitável: 6 sujo: 0 write-back: 0 instável: 0 grátis: 821 slab: 353 mapeado: 0 pagetables: 0 rejeição: 0

Livre de DMA: 3284kB min: 360kB baixo: 448kB alto: 540kB ativo_anon: 0kB inativo_anon: 0kB arquivo ativo - 4kB arquivo ativo - 0kB arquivo ativo - 0kB inevitável: 24kB presente: 8128kB páginas_scaneadas: 0 all_unreclaimable? não

lowmem_reserve []: 0 0 0

DMA: 31 * 4kB 47 * 8kB 42 * 16kB 64 * 32kB 1 * 64kB 0 * 128kB 0 * 256kB 0 * 512kB 0 * 1024kB 0 * 2048kB 0 * 4096kB = 3284kB

14 total de páginas de pagecache

Não foi possível alocar RAM para dados do processo, erro 12

Inicialmente, pensei que não conseguiria executar o programa na maior parte da memória contígua. Significando que o DMA estava muito fragmentado e eu teria que encontrar uma maneira de fazer com que o sistema desfragmentasse a memória.

Fiz uma rápida verificação de matemática / sanidade e percebi que o programa deveria ser capaz de rodar no único slot de memória contígua de 64kB. A Rm estava solicitando 61440 bytes (60kB).

Eu fiz um bom e velho "desfragmentador manual" e reiniciei o sistema. Quando reiniciei o sistema, produzi / proc / buddyinfo:

Node 0, zone DMA 2 8 3 12 0 1 0 1 0 1 0

O qual suspeito suspeito mapear:

  • 2 x 4 kB
  • 8 x 8 kB
  • 3 x 16 kB
  • 12 x 32 kB
  • 1 x 128 kB
  • 1 x 512 kB

Mas se alguém soma a lista de valores acima, ela não corresponde à saída de / proc / meminfo :

MemTotal:           6580 kB
MemFree:            3164 kB
Buffers:               0 kB
Cached:              728 kB
SwapCached:            0 kB
Active:              176 kB
Inactive:            524 kB
Active(anon):          0 kB
Inactive(anon):        0 kB
Active(file):        176 kB
Inactive(file):      524 kB`
Unevictable:           0 kB
Mlocked:               0 kB
MmapCopy:            844 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:             0 kB
Mapped:                0 kB
Slab:               1268 kB
SReclaimable:        196 kB
SUnreclaim:         1072 kB
PageTables:            0 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:        3288 kB
Committed_AS:          0 kB
VmallocTotal:          0 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB

Para recapitular, minhas perguntas são:

  1. O rm não foi executado porque não havia RAM contígua suficiente?
  2. Em caso afirmativo, existe um método leve de desfragmentar o DMA - sem recorrer à reinicialização do sistema?
  3. Se não, o que causou isso? Como posso impedir que isso aconteça no futuro?

Estou usando o XPort Pro da Lantronix (8 MB, Linux OS) executando o uClinux versão 2.6.30. A concha em uso é silenciosa.

OldTinfoil
fonte
Ponto secundário: você deixou de fora 1 x 2048 kB da sua lista de blocos de memória. Se você incluir isso, a soma será 3192 kB, muito próxima dos 3164 kB listados em / proc / meminfo.
Alex Selby

Respostas:

11

Na sua pergunta 2 (desfragmentando a memória), citando https://www.kernel.org/doc/Documentation/sysctl/vm.txt :

compact_memory

Disponível apenas quando CONFIG_COMPACTION está definido. Quando 1 é gravado no arquivo, todas as zonas são compactadas para que a memória livre esteja disponível em blocos contíguos sempre que possível. Isso pode ser importante, por exemplo, na alocação de páginas enormes, embora os processos também compactem diretamente a memória, conforme necessário.

isso implica que o seguinte comando (executado com privilégios de root e se a opção do kernel mencionada acima foi ativada)

echo 1 > /proc/sys/vm/compact_memory

deve dizer ao kernel para tentar desfragmentar a memória o máximo possível. Cuidado, por exemplo, em algumas versões do RHEL6, isso pode travar o kernel ...

Andre Holzner
fonte
1
Obrigado por dedicar seu tempo a voltar e comentar uma pergunta antiga!
precisa saber é o seguinte
7

Demorou um pouco de tempo, mas pensei em adiar a resposta até ter respostas para todas as três das minhas sub-perguntas.

Antes de começar, mencionarei que o termo correto quando se trata de "des-fragmentar" a memória de trabalho é referido como "compactar" a memória de trabalho.

1. O rm não foi executado porque não havia RAM contígua suficiente?

Eu estava certo em minha conclusão - a rm não foi executada porque havia RAM contígua insuficiente. O sistema estava adquirindo RAM e fragmentando-o, tornando-o irrecuperável.

2. Se sim, existe um método leve de desfragmentar o DMA - sem recorrer à reinicialização do sistema?

Acontece que não há como compactar a memória, exceto reiniciar o sistema incorporado. No caso de um sistema sem MMU, prevenção é o nome do jogo.

Parte de mim pondera se é possível hackear o kernel do linux para emular o MMU no software. Eu acho que se fosse possível, alguém já teria feito isso. Não consigo imaginar que seja um conceito totalmente novo;)

3. Como posso impedir que isso aconteça no futuro?

Para este projeto, eu estava usando o cron para iniciar manualmente o programa sempre que necessário. Uma maneira muito melhor de fazer isso é chamar o programa na inicialização e forçar o programa a dormir até que seja necessário. Dessa forma, a memória não precisa ser alocada em cada uso. Reduzindo assim a fragmentação.

Na primeira iteração do projeto, contamos com minhas chamadas de shell script para executar funções críticas (como rm). Não vimos a necessidade de reinventar a roda, se não precisássemos.

No entanto, eu recomendaria evitar o shell sempre que possível para um sistema sem MMU -

( Pergunta , o que acontece se você executar ls -la /path/to/directory/ | grep file-i-seek?)

( Resposta : inicia um novo subprocesso)

Se você precisar implementar algumas das principais funcionalidades do script shell no programa C, recomendo verificar o código-fonte usado no BusyBox . Provavelmente, você usará C em seu sistema incorporado.

OldTinfoil
fonte
Obrigado por dedicar um tempo para voltar e compartilhar suas descobertas.
Caleb
3
[Eu sei que isso é antigo] Emular uma MMU é difícil ... Sem uma MMU, todo programa usa diretamente os endereços físicos como eles aparecem no barramento de memória. Você pode emular um, mas precisará interceptar todos os acessos à memória (assim como uma MMU real). O desempenho seria terrível. Como alternativa, você pode usar ponteiros indiretos (como o Mac OS Classic, chamando-os de "alças"), mas então você tem uma API completamente difícil e muito difícil diante da prevenção (o Mac OS Classic usava multitarefa cooperativa) .
21313 derobert
Obrigado por voltar e dedicar tempo para escrever essa resposta. Não sabia que o MacOS classic fazia isso.
OldTinfoil