Cómo analizar el uso del SWAP en un sistema Linux

En informática existe la regla básica de que todo debe caber en RAM. El espacio SWAP no debería utilizarse. La realidad es más complicada y depende del sistema operativo. En Solaris, por ejemplo, no hay una distinción clara entre RAM y SWAP y, de hecho, al medir el espacio disponible se muestra la suma de ambos recursos y cuando un proceso reserva memoria, se reserva de ese total. En Linux, en cambio, existe el concepto de overcommit de memoria: un proceso puede pedir la memoria que le de la gana, pero no se reservarán bloques de memoria hasta que el proceso los vaya utilizando. Esto permite que un proceso tenga un espacio de direcciones inmenso si no lo utiliza de forma intensiva, pero la contrapartida es que si un proceso realmente emplea toda la memoria que ha solicitado, puede fallar a mitad de ejecución porque no hay recursos suficientes. En Solaris fallaría directamente en la solicitud de memoria inicial.

Sobre el SWAP hay muchos mitos. Por ejemplo, es frecuente reservar tanto espacio de SWAP como RAM tengas en la máquina o el doble de dicha cantidad. ¿Por qué? La respuesta habitual es "porque sí, todo el mundo sabe que es lo que hay que hacer". Hay algunas razones históricas, pero hoy en día no tienen sentido.

La verdadera regla de oro respecto al SWAP es que todo el working set de los procesos en ejecución en el sistema debe caber en RAM. Si tu working set no cabe en RAM, tu rendimiento será ridículo. Una vez que tienes RAM suficiente, el tamaño del SWAP depende de tus necesidades. En Solaris, por ejemplo, es conveniente proporcionar un SWAP amplio porque el sistema lo usará para enviar allí lo que menos se use y así hacer hueco en RAM para cosas más importantes. En Linux el SWAP es más secundario, en buena parte debido al overcommit. Hay que señalar que los entornos Linux que no pueden permitirse que mueran procesos arbitrarios por escasez de recursos, se configuran para desactivar el overcommit y las necesidades de SWAP cambian completamente.

¿Cómo sabremos cuánto SWAP está utilizando un proceso concreto? En Linux esto es fácil:

jcea@jcea:~$ cat /proc/3421/status | grep ^Vm
VmPeak:  5857292 kB
VmSize:  5849312 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:   3327276 kB
VmRSS:   3222616 kB
VmData:  4758264 kB
VmStk:       188 kB
VmExe:       148 kB
VmLib:    225096 kB
VmPTE:      8632 kB
VmPMD:        40 kB
VmSwap:   141120 kB

Esta secuencia de comandos muestra el estado de la memoria utilizada por el navegador Firefox en mi sistema. Vemos que está reservando 5.8 Gigabytes de RAM (VmSize), aunque realmente está usando solo 3.2 Gigabytes (VmRSS) en memoria y 141 megabytes en el SWAP (VmSwap).

Cuando nuestra máquina Linux empieza a tirar de SWAP, el disco duro está permanentemente activo y el rendimiento se resiente, entonces podemos ver qué procesos están usando más el SWAP. En vez de revisar proceso a proceso, podemos hacer una revisión global y ordenar los procesos por uso de SWAP:

root@jcea:~$ { date; for f in /proc/[0-9]*/status; \
               do awk '{k[$1]=$2} \
               END { if (k["VmSwap:"]) print k["Pid:"],k["Name:"],k["VmRSS:"],k["VmSwap:"];}' \
               $f 2>/dev/null; done | sort -n ; } | sort -n -k 4
3273 ksysguardd 1816 64
2145 acpid 1132 68
2815 anvil 1592 88
2970 start_kdeinit 0 88
3496 sh 504 92
7671 gvfs-udisks2-vo 3084 104
2867 startkde 1380 116
3433 gpg-agent 1604 116
4170 imap 37236 136
4174 imap 35400 136
4173 imap 27384 140
7679 gvfsd-trash 3520 140
2807 agetty 1384 148
3608 vmstat 740 164
7717 gvfsd-metadata 1644 168
4171 imap 12200 176
2131 atd 1060 180
2805 dovecot 1916 180
3007 dconf-service 1564 180
2160 cron 1328 196
1480 lvmetad 456 208
2020 systemd-timesyn 1256 212
1447 systemd-journal 7628 224
2164 systemd-logind 1460 228
2816 log 1368 228
2908 ssh-agent 568 232
3176 bluetoothd 1536 232
2166 avahi-daemon 1200 236
2965 dbus-daemon 1964 244
2133 zed 1108 272
2302 avahi-daemon 40 292
2544 dnsmasq 1824 312
3153 rtkit-daemon 1832 312
2320 accounts-daemon 2996 316
2912 dbus-daemon 2164 316
2956 at-spi-bus-laun 1904 340
2167 dbus-daemon 2948 360
2291 openvpn 2928 372
2736 dbus-daemon 1124 380
3663 imap 5664 396
2377 polkitd 4756 400
3495 cron 1616 408
3662 gconfd-2 2204 452
2727 dbus-launch 996 464
3470 gvfsd 1920 464
2911 dbus-launch 1148 468
2992 kwrapper5 1700 472
2968 at-spi2-registr 2012 512
2509 wpa_supplicant 1464 540
2819 config 2792 592
5100 usbmuxd 4612 612
2152 rsyslogd 1436 640
3784 ssh 2228 652
2856 systemd 1252 672
3785 ssh 2432 684
3957 ssh 2212 684
3752 ssh 2260 688
2520 systemd 940 700
3956 ssh 2200 724
4427 dbus 1912 780
1 systemd 3184 788
2831 upowerd 4592 796
4420 cupsd 2904 796
2418 sddm 2580 816
2533 dhclient 1532 860
3772 ssh 1952 892
3764 ssh 1896 904
3771 ssh 2008 916
2314 ofonod 1596 992
4421 cups-browsed 2324 1012
2855 sddm-helper 2980 1064
2824 udisksd 5900 1108
11175 bash 2548 1152
7699 bash 3304 1268
11172 bash 1648 1284
5650 bash 2960 1300
3102 kscreen_backend 3948 1308
3175 obexd 1536 1352
5230 bash 2864 1384
2676 whoopsie 2356 1524
2135 ModemManager 1344 1572
3152 pulseaudio 4660 1576
4565 bash 2148 1588
3897 bash 2056 1684
2857 (sd-pam) 592 1724
5213 bash 2308 1736
2524 (sd-pam) 432 1852
2321 NetworkManager 3284 1868
4579 bash 1984 1960
4546 bash 1968 1972
5289 vi 4580 1988
3619 bash 1700 2048
3717 bash 1756 2048
3586 bash 1604 2052
3702 bash 1740 2052
3706 bash 1700 2052
3728 bash 1672 2052
3749 bash 1664 2052
3753 bash 1672 2052
3873 bash 1740 2052
3923 bash 1604 2052
3932 bash 1748 2052
3908 bash 1792 2116
3431 bash 1756 2120
3880 bash 1852 2128
1507 systemd-udevd 1396 2160
3864 bash 1896 2300
3238 akonadi_baloo_i 4956 2720
4620 vi 1988 2760
3138 xembedsniproxy 3888 2952
2864 kwalletd 3276 2996
2971 kdeinit5 3256 3016
2866 kwalletd5 4764 3072
3398 kuiserver 2980 3224
3241 akonadi_contact 4244 3256
3263 akonadi_migrati 4476 3272
3122 polkit-kde-auth 4100 3284
2993 kaccess 3684 3300
3246 akonadi_maildir 5176 3316
2974 klauncher 4368 3452
3098 mission-control 3340 3456
3642 arcstat 4344 3488
3242 akonadi_followu 4084 3492
3239 akonadi_birthda 3564 3560
3393 kdeconnectd 4364 3568
3251 akonadi_maildis 6388 3604
2998 kglobalaccel5 5144 3616
3569 python3 3712 3684
3182 akonadi_control 4392 3700
3272 akonadi_newmail 3756 3960
3193 akonadiserver 7000 4016
2315 snapd 4936 5072
3144 korgac 5084 5312
3105 kactivitymanage 4848 5316
3236 akonadi_akonote 5400 5352
3244 akonadi_ical_re 5544 5708
2994 ksmserver 8688 6788
3283 akonadi_notes_a 7072 6872
3286 akonadi_sendlat 6648 6980
3256 akonadi_mailfil 3836 8016
3419 konsole 42524 8508
4000 python3 2580 9288
3237 akonadi_archive 4484 9500
4043 python3 5540 10672
2499 Xorg 58384 12000
3024 kwin_x11 38296 12204
3091 krunner 16644 16308
4172 imap 13676 21476
3094 plasmashell 96604 29556
2976 kded5 21628 30836
3497 getmail 29008 39116
3197 mysqld 37448 110728
3421 firefox 3252680 140492
3428 thunderbird 2117600 226724

El primer campo indica el PID del proceso. El segundo es su nombre. El tercer campo es cuánta memoria está usando el proceso en RAM mientras que el último campo es cuánto usa en el SWAP. Se observa que algunos procesos como getmail o mysqld tienen más memoria en el SWAP que en RAM. Esto es bueno porque si el proceso no necesita esos datos en memoria, prefiero que esa RAM se use para cosas más importantes.

Podemos ver el total de SWAP en uso (más o menos) cambiando el fragmento final por: [1]

| awk '{a+=$4} END {print(a);}'
[1]

En realidad en Linux es más práctico usar los comandos free o vmstat. Por ejemplo:

jcea@jcea:~$ free
              total        used        free      shared  buff/cache   available
Mem:        8172236     7227128      243568       66716      701540      184628
Swap:      14571868     1094884    13476984

Por ejemplo:

root@jcea:~$ { date; for f in /proc/[0-9]*/status; \
               do awk '{k[$1]=$2} \
               END { if (k["VmSwap:"]) print k["Pid:"],k["Name:"],k["VmRSS:"],k["VmSwap:"];}' \
               $f 2>/dev/null; done | sort -n ; } \
               | awk '{a+=$4} END {print(a);}'
922907

En este momento tengo 923 Megabytes en el SWAP. El rendimiento es bueno porque el working set cabe en RAM. Al SWAP se envían los datos que no se han utilizado recientemente. Como he dicho antes, esto es bueno. Estos 923 Megabytes en el SWAP son datos que no necesitan estar en RAM, y esa RAM se puede usar para cosas más importantes. La cuestión es que esos datos en el SWAP no se necesiten para el funcionamiento normal del programa (al menos a corto plazo). Esa es la clave que he comentado antes y que siempre tendrías que tener en cuenta: Lo que importa no es la cantidad de RAM que soliciten los procesos o cuánto SWAP estén usando. Lo importante es que los working sets de todos los procesos activos quepan en RAM.

¿Y eso cómo se sabe?. Podemos echar un vistazo con el comando vmstat:

    jcea@jcea:~$ vmstat 1
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
...
     5  0 1007940 493732     80 780728    0    0     0     0 1729 2829 29  4 67  0  0
...

La columna que nos interesa es el si dentro de swap. Esta columna nos informa del tráfico de entrada desde el SWAP hacia la RAM. Es decir, la frecuencia y la cantidad de datos que el sistema necesita, pero que están en el SWAP. Cuando un programa necesita un dato que está en el SWAP, el programa se detiene mientras el sistema operativo lo carga en RAM. Si hay más procesos pendientes de ser ejecutados, la máquina seguirá progresando en su trabajo. Si todo está esperando por el SWAP, verás actividad en la columna si de swap, pero también en la columna wa dentro de cpu. Si estás en esa situación habrás notado que el rendimiento empieza a empeorar y en el límite el ordenador será inusable.