Quando il mio sistema Linux si avvicina al paging (ovvero, nel mio caso, 16 GB di RAM quasi piena, 16 GB di scambio completamente vuoti) se un nuovo processo X tenta di allocare un po 'di memoria, il sistema si blocca completamente. Cioè, fino a quando una quantità sproporzionata di pagine, (wrt la dimensione totale e la velocità delle richieste di allocazione di memoria di X) sono state scambiate. Si noti che non solo la GUI non risponde completamente, ma anche i servizi di base come sshd sono completamente bloccati.
Sono due pezzi di codice (dichiaratamente rozzo) che utilizzo per innescare questo comportamento in modo più "scientifico". Il primo ottiene due numeri x, y dalla riga di comando e procede all'allocazione e all'inizializzazione di più blocchi di y byte fino a quando non sono stati allocati più di x byte totali. E poi dorme solo a tempo indeterminato. Questo verrà utilizzato per portare il sistema sull'orlo del paging.
#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;
}
Il secondo pezzo di codice fa esattamente quello che fa il primo tranne che ha un sleep(1);
diritto dopo il printf
(non ripeterò l'intero codice). Questo verrà usato quando il sistema è sull'orlo del paging per farlo scambiare le pagine in modo "delicato", cioè richiedendo lentamente l'allocazione di nuovi blocchi di memoria (in modo che il sistema dovrebbe sicuramente essere in grado di scambiare pagine e tenere il passo con le nuove richieste).
Quindi, con i due pezzi di codice compilati, chiamiamo i rispettivi ex fasteater e sloweater, facciamo questo:
1) avvia la tua interfaccia grafica preferita (ovviamente non strettamente necessaria)
2) avviare alcuni mem / swap meter (es. watch -n 1 free
)
3) avviare più istanze in fasteater x y
cui x è dell'ordine dei gigabyte e y è dell'ordine dei megabyte. Fallo fino a quasi riempire il montone.
4) avvia un'istanza di sloweater x y
, sempre dove x è dell'ordine dei gigabyte e y è dell'ordine dei megabyte.
Dopo il passaggio 4) ciò che dovrebbe accadere (e succede sempre per il mio sistema) è che, dopo aver esaurito il pistone, il sistema si bloccherà completamente. gui è bloccato sshd è bloccato ecc. MA, non per sempre! Dopo che sloweater ha terminato le sue richieste di allocazione, il sistema tornerà in vita (dopo minuti di blocco, non secondi ...) con questa situazione:
a) la ram è quasi piena
b) lo swap è anche al completo (ricorda, all'inizio era vuoto)
c) nessun intervento killer.
E nota che la partizione di swap è su un SSD. Quindi, il sistema sembra non essere in grado di spostare gradualmente le pagine dalla ram allo swap (presumibilmente dai fasteater che stanno solo dormendo) per fare spazio alle richieste lente (e di pochi megabyte) del sloweater.
Ora, qualcuno mi correggerà se sbaglio, ma questo non sembra il modo in cui un sistema moderno dovrebbe comportarsi in questa impostazione. Sembra comportarsi come i vecchi sistemi (waaaaay indietro) quando non c'era supporto per il paging e il sistema di memoria virtuale ha appena scambiato l'intero spazio di memoria di qualche processo anziché poche pagine.
Qualcuno può provare anche questo? E forse qualcuno che ha anche un sistema BSD.
AGGIORNAMENTO 1
Ho seguito i consigli di Mark Plotnick di seguito nei commenti e ho iniziato vmstat 1 >out
prima di procedere con il test di paging. Puoi vedere il risultato qui sotto (ho tagliato l'intera parte iniziale dove viene riempito il ram senza coinvolgimento di swap):
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
Come puoi vedere, non appena lo swap viene coinvolto c'è un enorme scambio di 15916880 Kbyte in una sola volta che, immagino, dura per l'intera durata del blocco del sistema. E tutto ciò è apparentemente causato da un processo (il rallentatore) che richiede solo 10 MB al secondo.
AGGIORNAMENTO 2: Ho fatto una rapida installazione di FreeBSD e ripetuto lo stesso schema di allocazione usato con Linux ... ed è stato fluido come dovrebbe essere. FreeBSD ha scambiato le pagine gradualmente mentre il sloweater ha allocato tutti i suoi blocchi di memoria da 10 MB. Neanche un intoppo di alcun tipo ... WTF sta succedendo qui ?!
AGGIORNAMENTO 3: Ho archiviato un bug con il bugtracker del kernel. Sembra che stia ottenendo qualche attenzione, quindi ... incrociamo le dita ...
vmstat 1>somefile
direttamente dal sistema e quindi vedere cosa riporta dopo che il sistema è tornato in vita. Ci proverò.
swappiness
è il valore predefinito 60 (non che cambiarlo dia un risultato migliore). Il kernel usato con la vmstat
corsa è 4.14.35 ma ho provato 4.15, 4.16 e sono persino tornato alla serie 4.0 (!): Sempre lo stesso comportamento. E non sto usando una strana distribuzione, è solo debian. Non uso le immagini del kernel di debian (non che le mie abbiano configurazioni insolite) ma ho provato uno di questi ... stesso comportamento.