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 y
que 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 >out
antes 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 ...
vmstat 1>somefile
diretamente do sistema e ver o que ele relata depois que o sistema volta à vida. Eu vou tentar isso.
swappiness
é o padrão 60 (não que a alteração traga um resultado melhor). O kernel usado com a vmstat
execuçã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.