É assim que a paginação do Linux deve se comportar?


26

Quando meu sistema Linux se aproximar da paginação (no meu caso, 16 GB de RAM quase cheia, 16 GB de swap completamente vazios) se um novo processo X tentar alocar alguma memória, o sistema trava completamente. Ou seja, até que uma quantidade desproporcional de páginas (com o tamanho total e a taxa das solicitações de alocação de memória de X) seja trocada. Observe que não apenas o gui fica completamente sem resposta, mas mesmo os serviços básicos como o sshd são completamente bloqueados.

Esses são dois pedaços de código (reconhecidamente grosseiros) que eu uso para desencadear esse comportamento de uma maneira mais "científica". O primeiro obtém dois números x, y na linha de comando e continua alocando e inicializando vários blocos de y bytes até que mais de x bytes totais tenham sido alocados. E então apenas dorme indefinidamente. Isso será usado para trazer o sistema à beira da paginação.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char** argv) {
   long int max = -1;
   int mb = 0;
   long int size = 0;
   long int total = 0;
   char* buffer;

   if(argc > 1)
     {
       max = atol(argv[1]);
       size = atol(argv[2]);
     }
   printf("Max: %lu bytes\n", max);
   while((buffer=malloc(size)) != NULL && total < max) {
       memset(buffer, 0, size);
       mb++;
       total=mb*size;
       printf("Allocated %lu bytes\n", total);       
   }      
   sleep(3000000);
   return 0;
}

O segundo pedaço de código faz exatamente o que o primeiro faz, exceto que ele tem um sleep(1);direito após o printf(não vou repetir o código inteiro). Este será usado quando o sistema estiver à beira da paginação para trocar as páginas de maneira "suave", ou seja, solicitando lentamente a alocação de novos blocos de memória (para que o sistema certamente possa trocar as páginas) e acompanhe as novas solicitações).

Assim, com os dois pedaços de código compilados, vamos chamar os respectivos exes fasteater e sloweater, vamos fazer o seguinte:

1) inicie sua GUI favorita (não é estritamente necessário, é claro)

2) inicie um medidor de mem / swap (por exemplo watch -n 1 free)

3) inicie várias instâncias em fasteater x yque x é da ordem de gigabytes e y é da ordem de megabytes. Faça isso até quase encher o carneiro.

4) inicie uma instância de sloweater x y, novamente onde x é da ordem de gigabytes e y é da ordem de megabytes.

Após o passo 4), o que deve acontecer (e sempre acontece no meu sistema) é que, logo após ter esgotado o aríete, o sistema travará completamente. gui está bloqueado sshd está bloqueado etc. MAS, não para sempre! Depois que o sloweater terminar suas solicitações de alocação, o sistema retornará à vida útil (após minutos de bloqueio, não segundos ...) com esta situação:

a) ram está quase cheio

b) swap também está cheio (lembre-se, estava vazio no começo)

c) nenhuma intervenção matadora.

E observe que a partição de troca está em um SSD. Portanto, o sistema parece incapaz de mover gradualmente as páginas do ram para o swap (presumivelmente dos fasteaters que estão apenas inativos) para abrir espaço para as solicitações lentas (e de apenas alguns megabytes) do sloweater.

Agora, alguém me corrija se eu estiver errado, mas isso não parece o modo como um sistema moderno deve se comportar nesse cenário. Parece se comportar como nos sistemas antigos (não há suporte) quando não havia suporte para paginação e o sistema de memória virtual trocava todo o espaço de memória de algum processo em vez de poucas páginas.

Alguém pode testar isso também? E talvez alguém que também tenha um sistema BSD.

ATUALIZAÇÃO 1 Segui os conselhos de Mark Plotnick abaixo nos comentários e comecei vmstat 1 >outantes de prosseguir com o teste de paginação. Você pode ver o resultado abaixo (cortei toda a parte inicial em que o carneiro é preenchido sem o envolvimento da troca):

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0   6144 160792      8 272868    0    0     0     0  281 1839  1  0 99  0  0
0  0   6144 177844      8 246096    0    0     0     0  425 2300  1  1 99  0  0
0  0   6144 168528      8 246112    0    0    16     0  293 1939  1  0 99  0  0
0  0   6144 158320      8 246116    0    0     0     0  261 1245  0  0 100  0  0
2  0  10752 161624      8 229024    0 4820 17148  4820  845 3656  1  2 97  0  0
2  0  10752 157300      8 228096    0    0 88348     0 2114 8902  0  5 94  1  0
0  0  10752 176108      8 200052    0    0 108312     0 2466 9772  1  5 91  3  0
0  0  10752 170040      8 196780    0    0 17380     0  507 1895  0  1 99  0  0
0 10  10752 160436      8 191244    0    0 346872    20 4184 17274  1  9 64 26  0
0 29 12033856 152888      8 116696 5992 15916880 1074132 15925816 819374 2473643  0 94  0  6  0
3 21 12031552 295644      8 136536 1188    0 11348     0 1362 3913  0  1 10 89  0
0 11 12030528 394072      8 151000 2016    0 17304     0  907 2867  0  1 13 86  0
0 11 12030016 485252      8 158528  708    0  7472     0  566 1680  0  1 23 77  0
0 11 12029248 605820      8 159608  900    0  2024     0  371 1289  0  0 31 69  0
0 11 12028992 725344      8 160472 1076    0  1204     0  387 1381  0  1 33 66  0
0 12 12028480 842276      8 162056  724    0  3112     0  357 1142  0  1 38 61  0
0 13 12027968 937828      8 162652  776    0  1312     0  363 1191  0  1 31 68  0
0  9 12027456 1085672      8 163260  656    0  1520     0  439 1497  0  0 30 69  0
0 10 12027200 1207624      8 163684  728    0   992     0  411 1268  0  0 42 58  0
0  9 12026688 1331492      8 164740  600    0  1732     0  392 1203  0  0 36 64  0
0  9 12026432 1458312      8 166020  628    0  1644     0  366 1176  0  0 33 66  0

Como você pode ver, assim que a troca é envolvida, há uma troca maciça de 15916880 Kbytes de uma só vez, que, eu acho, dura toda a duração do congelamento do sistema. E tudo isso é aparentemente causado por um processo (o sloweater) que apenas pede 10 MB por segundo.

ATUALIZAÇÃO 2: Fiz uma instalação rápida do FreeBSD e repeti o mesmo esquema de alocação usado no Linux ... e foi o mais suave possível. O FreeBSD trocou as páginas gradualmente enquanto o sloweater alocava todos os seus 10 MB de memória. Nenhum tipo de problema ... WTF está acontecendo aqui ?!

ATUALIZAÇÃO 3: Arquivei um bug no bugtracker do kernel. Parece estar recebendo alguma atenção, então ... dedos cruzados ...


2
Como eu mencionei, tudo está bloqueado. Eu tentei ssh'ing de outro sistema que apenas expira.
John Terragon

2
Se eu iniciar o vmstat 1 com saída stdout, acho que vai congelar. Mas você está certo, eu poderia começar vmstat 1>somefilediretamente do sistema e ver o que ele relata depois que o sistema volta à vida. Eu vou tentar isso.
John Terragon

2
Eu usei vmstat. Resultados na atualização acima.
John Terragon

3
swappinessé o padrão 60 (não que a alteração traga um resultado melhor). O kernel usado com a vmstatexecução é a 4.14.35, mas eu tentei a 4.15, 4.16 e até voltei à série 4.0 (!): Sempre com o mesmo comportamento. E não é que eu esteja usando alguma distribuição estranha, é apenas debian. Eu não uso as imagens do kernel do debian (não que as minhas tenham configurações incomuns), mas tentei uma dessas ... mesmo comportamento.
John Terragon

2
Discussão muito interessante sobre o bug do kernel! E parece que você isolou esse problema para trocar a partição criptografada com LUKS. Você pode editar sua resposta ou possivelmente postar uma resposta você mesmo (com as soluções alternativas conhecidas até agora e talvez continue atualizando-a à medida que a discussão sobre o LKML obtém resultados mais conclusivos). Realmente impressionante ver a comunidade de kernel do Linux em funcionamento! Fil
filbranden 12/10

Respostas:


1

É exatamente para isso que existe a proteção contra thrash .

Ele monitora constantemente o estado de troca e, quando algo acidentalmente começa a ocupar muita RAM, congela temporariamente os processos gananciosos da RAM, para que o kernel tenha tempo para trocar alguma memória sem fazer com que todo o sistema não responda.


-3

Você está apenas alocando memória - na verdade, não coloca nada nela. Um programa "normal" alocaria um pedaço e começaria a usá-lo. Alocação não é o mesmo que uso de memória.


3
Bem-vindo à postagem no Unix StackExchange. Ele coloca dados nele, esses dados simplesmente são zero. Veja o memset (). O kernel do Linux fornece uma página física de RAM assim que você escreve na página virtual; não olha para o valor específico que está escrito.
sourcejedi

Na verdade, eu compilei e executei isso no meu desktop, começando com 2 GB usados ​​e 6 GB gratuitos. Na verdade, ele trocou a uma taxa lenta inicialmente e somente quando atingiu o limite foi agressivamente - o que causou várias ações da GUI.
Jeremy Boden
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.