Esta página puede ser redistribuida libremente bajo los términos de la licencia GPL. Vease ( GPL texto original ) o si lo prefiere (Traducción española no oficial de la GPL) Al margen de las obligaciones legales que se derivan del uso de esta licencia rogamos sea respetada la referencia a su lugar de publicación original www.ciberdroide.com. y a su autor original Antonio Castro Snurmacher (Madrid 01/01/2000).

Ausencia de Garantía

Esta ausencia de garantía se hace extensa a cualquier tipo de uso de este material y muy especialmente a las prácticas, ejercicios, y de ejemplos que encuentre en estas páginas. Deberá trabajar siempre salvo indicación contraria con un SO Linux y con un usario distinto de 'root' sin privilegios especiales. Como directorio de trabajo se procurará usar el directorio '/tmp' o algún otro que no contenga información valiosa. Tampoco se considera buena idea practicar en una máquina que contenga información valiosa.

Todo esto son recomendaciones de prudencia. En cualquier caso si algo sale mal toda la responsabilidad será únicamente suya. En ningún caso podrá reclamar a nadie por daños y perjuicios derivados del uso de este material. Para más información lea el contenido de la licencia GPL y abstengase de hacer prácticas si no está dispuesto a asumir toda la responsabilidad.

La Memoria Virtual en Linux

Introducción
Este curso no trata de la administración del sistema pero en este capítulo se van a explicar algunas cosas que son materia de administración del sistema pero que un usuario también debe conocer.

Muchas veces un administrador tiene dificultades para gestionar los escasos recursos de una máquina porque los usuarios no los utilizan del modo más racional.

En un sistema con muchos usuarios estos deben compartir los recursos de modo racional, pero generalmente ocurre lo contrario. El no comprender como funcionan las cosas y la avaricia de recursos puede conducir a conductas absurdas.

Por ejemplo un usuario que tiene que compilar 500 programas sabe que tendrá que esperar mucho. Si este usuario es suficientemente ignorante y egoísta puede pensar que lanzando las 500 compilaciones simultáneamente causará un retraso a los demás pero el terminará antes. Lo cierto es que los recursos del sistema se desperdician en trabajos internos totalmente inútiles para el usuario cuando el sistema está sobrecargado.

Tanto si se persigue compartir los recursos de forma razonable con los demás usuarios, como si lo que se pretende es usar los recursos de todo el sistema para sacar el mayor beneficio personal, se necesita comprender como funcionan algunas cosas.

Esta lección será totalmente teórica porque para un usuario normal no resulta prudente cierto tipo de experimentos que podrían provocar incomodidades o incluso algunos problemas en el sistema. De todas formas si el sistema empieza a tener problemas, si que es bueno tener los conocimientos suficientes para averiguar si eso tiene que ver con algo que estemos haciendo nosotros. Quizás podamos incluso advertir a algún compañero que trabaje en el mismo equipo que el problema en el sistema tiene que ver con lo que el hace.

Limitar el uso de recursos
En un sistema se suele llamar recursos a ciertos elementos que no pueden ser utilizados de forma ilimitada. Por ejemplo. 'yes' es un programa que genera una salida infinita y no es buena idea redirigir su salida a un fichero porque llenaremos nuestro espacio de disco. En algunos sistemas el administrador establece un limite de espacio para cada usuario. Si no fuera el caso llenaríamos el disco. Si la salida de 'yes' la redirigimos a /dev/null no pasará nada. El número de ficheros que podemos tener abiertos simultáneamente también tiene un límite. Lo mismo puede decirse de otras muchas cosas.

La cantidad de memoria es limitada pero existen distintos limites que afectan al uso de la memoria. Para limitar el uso de ciertos recursos existe un comando interno de la shell llamado 'ulimit'.

Por ejemplo 'ulimit -c ' se usa para limitar el tamaño de los ficheros core producidos cuando un proceso muere bajo determinadas circunstancias. Un usuario normalito no utilizará para nada estos ficheros así que podría usarse 'ulimit 0' para evitar la aparición de ficheros core.

También se pueden limitar otras muchas cosas como la cantidad de CPU por segundo, tamaño máximo de un fichero, máximo número de procesos, máximo numero de ficheros abiertos, cantidad máxima de memoria virtual, etc...

Para ver las limitaciones con las que estamos trabajando haremos lo siguiente:

$ ulimit -a
core file size (blocks)     0
data seg size (kbytes)      unlimited
file size (blocks)          unlimited
max locked memory (kbytes)  unlimited
max memory size (kbytes)    unlimited
open files                  1024
pipe size (512 bytes)       8
stack size (kbytes)         8192
cpu time (seconds)          unlimited
max user processes          256
virtual memory (kbytes)     unlimited

Bueno en este ejemplo vemos que estamos en una sesión con muy pocas limitaciones. Eso es señal de que el administrador confía en nosotros y nos concede mucha libertad.

'ulimit' permite establecer dos clases de límites. Límites duros (solo se pueden poner una vez y generalmente esto lo hace root) y límites blandos que se pueden cambiar varias veces pero no se permite sobrepasar los valores establecidos en el límite duro. root establece estos límites en algún fichero de configuración y los usuarios pueden tener en su $HOME/.bashrc alterar los limites blandos. Para más información consulte la página man de 'ulimit'.

Multitarea
Ya hemos hablado algo de ella cuando estudiamos los procesos. Queda claro que en un sistema en el que se están ejecutando en apariencia varios procesos simultáneamente lo que ocurre realmente es que el tiempo de la CPU se va repartiendo entre los distintos procesos concediendo rápidamente sucesivas rodajas de tiempo a cada uno en función de su prioridad y de la política de reparto de ese tiempo. Esta política no nos interesa ahora. Se supone que las cosas están pensadas para que el funcionamiento global de todo el sistema sea el más adecuado.

Volvamos al caso del usuario ignorante y egoísta del capítulo anterior. El de las 500 compilaciones simultáneas. El sistema necesita parar cada uno de los procesos que usan la CPU una vez que han consumido su rodaja de tiempo para tomar el siguiente proceso en espera de CPU.

Cuando un proceso necesita usar algo y en ese instante no está disponible pasa a un estado de inactividad. Se dice que queda dormido.

Este cambio de un proceso a otro no es gratuito porque una vez parado el proceso hay que anotar la situación en la cual ese proceso ha sido detenido para luego poder volver a continuar exactamente en el mismo punto cuando le vuelva a tocar y quizás parte del código o de los datos del proceso ya no estén en memoria RAM (también la llamaremos indistintamente memoria física. Ver más adelante paginación). Todo esto consume muchos recursos y se puede alcanzar la situación en la cual el SO consume la mayor parte del tiempo en esta gestión de procesos intercambiando datos entre memoria y disco constantemente porque la memoria física no puede con todo por el contrario los procesos que son la parte realmente útil apenas dispondrán de recursos. En esta situación el sistema se vuelve muy lento y los discos del sistema tienen una actividad altísima. En realidad es una situación de auténtico agobio para el sistema y su comportamiento se vuelve torpe. Es como si el sistema actuara a la desesperada.

Por esa razón decimos que el usuario del capítulo anterior además de ser egoísta es ignorante porque está provocando un enorme retraso no solo a los demás usuarios (cosa que ya sabe) sino también a si mismo.

Si alguna vez notamos que el sistema va muy lento y sospechamos la causa tendremos que parar en primer lugar el proceso que está provocando la sobrecarga. Si no sabemos cual es o si se trata de un proceso que no es nuestro podemos investigar con el comando 'top'. Tenga en cuenta que de este comando se sale con 'q'.

Este comando saca en cabecera un resumen de la situación general del sistema y luego saca solo la información de los procesos que mayor consumo de recursos están provocando en el sistema.

Tenga en cuenta que 'top' sirve para diagnosticar el uso de recursos y permite identificar procesos que consumen demasiado pero en si mismo 'top' también consume bastantes recursos. Por eso en situaciones de sobrecarga del sistema conviene no abusar de su uso. 'top' refresca cada cierto tiempo la información de la pantalla. Por defecto lo hace cada 5 segundos. Podemos aumentar a 15 segundos. Para ello usaremos 'top d 15'. Esto consumirá menos recursos y nos permitirá mirar con más tranquilidad la información de top.

Vamos a ver un ejemplo del uso de 'top' que corresponde a un sistema con muy poca actividad. Solo 5 usuarios. La CPU está ociosa 99.6% del tiempo, la cantidad de memoria disponible es alta y no se está usando la swap.

$ top
12:17pm  up  2:06,  5 users,  load average: 0.00, 0.00, 0.00
55 processes: 54 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:  0.0% user,  0.3% system,  0.0% nice, 99.6% idle
Mem:  258100K av,  93320K used, 164780K free,  66800K shrd,   7652K buff
Swap: 393552K av,      0K used, 393552K free                 40744K cached

 PID USER     PRI  NI  SIZE  RSS SHARE STAT  LIB %CPU %MEM   TIME COMMAND
1002 root      11   0  1312 1312   700 R       0  0.3  0.5   0:00 top
   1 root       0   0   464  464   404 S       0  0.0  0.1   0:05 init
   2 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 kflushd
   3 root       0   0     0    0     0 SW      0  0.0  0.0   0:00 kupdate
.... .....     ..  ..   ...  ...   ... ..     ..  ...  ...   .:.. ......  
.... .....     ..  ..   ...  ...   ... ..     ..  ...  ...   .:.. ......  
.... .....     ..  ..   ...  ...   ... ..     ..  ...  ...   .:.. ......  

USER es es usuario que arrancó el proceso, %CPU es el porcentaje de tiempo de CPU usado por el proceso. %MEM Porcentaje de memoria física usada por el proceso. Podría incluso superar el 100%. Un valor alto puede delatar a un proceso como el causante de los problemas de paginación (swaping) en el sistema.

Otro comando que nos da información sobre la carga del sistema es 'uptime'

$ uptime 

  12:27pm  up  2:16,  5 users,  load average: 0.00, 0.00, 0.00

Coincide con la primera linea de 'top'. Los datos son hora actual tiempo transcurrido desde el arranque del sistema, número de usuarios y las tres últimas cifras se corresponden con promedios de carga en el último minuto, 5 últimos minutos y 15 últimos minutos respectivamente.

Memoria virtual
Vamos a explicar en primer lugar que significa eso de memoria virtual. En muchos sistemas operativos el SO ofrece a los programas un espacio de memoria superior al que realmente existe. La memoria física es la memoria RAM. La CPU no puede hacer nada con los datos si antes no pasan a la RAM. A pesar de esto un sistema con 64Mbytes de RAM puede ejecutar por ejemplo un programa use variables con el doble de tamaño que el de la memoria RAM disponible. Si el programa accediera mucho y de forma aleatoria dentro de esta enorme variable, se produciría una fuerte sobrecarga en el sistema. Si alguien se pregunta si una barbaridad como esta, puede hacerla un usuario normalito sin privilegios especiales, la respuesta es, usuario normal si, pero autorizado.

Eso no significa que estemos burlando la seguridad del sistema ni que seamos más listos que el administrador.

Si el administrador configura el sistema de forma paranoica para evitar cualquier posible riesgo, los usuarios se verán sometidos a unas restricciones bastante incómodas para trabajar.

Por eso lo normal es que el administrador asuma que los usuarios autorizados utilizaran los recursos de forma razonable.

Para gestionar la memoria virtual lo que se hace es recurrir al espacio de disco para ampliar este espacio. La memoria RAM tiene una velocidad de acceso mucho más rápida que un disco duro y para conseguir que todo funcione de forma transparente y de forma eficiente se utilizan unas técnicas de segmentación y paginación que describiremos a continuación.

Segmentación de la memoria
La memoria de un ordenador es una especie de enorme casillero. Cada casilla contendrá cierta información pero un proceso no puede acceder a toda la información del sistema. Cada casilla tiene una dirección y cada proceso puede usar ciertas direcciones para almacenar información. De esta forma se evita que un proceso interfiera accidentalmente o intencionadamente a la información de otro proceso distinto.

Las casillas son las posiciones de memoria y a pesar de lo dicho existen posiciones de memoria compartidas. Por ejemplo el código de un programa que se está ejecutando en varios procesos será compartido en modo exclusivo de lectura por los procesos que estén ejecutando ese programa. Es decir que si por ejemplo hay varias personas ejecutando el editor 'vim' el código de ese programa no estará repetido en memoria. Sería un desperdicio porque el código es el mismo, no se puede alterar y la CPU se limita a leer la información consistente en instrucciones para la CPU.

Todo este tipo de información es lo que se denomina segmento de texto. Los proceso manejan otros tipos de información distintas y para ello se organiza en segmentos de distinto tipo. Por ejemplo hay segmentos de datos de solo lectura, segmentos de datos de lectura escritura, segmentos de datos de pila, segmentos datos de memoria compartida, segmentos de memoria dinámica. No vamos a explicar estas cosas porque excede en mucho los propósitos de este capítulo pero si queremos dejar una idea. Los procesos tienen la memoria organizada en segmentos de distinto tipo. Gracias a esto se consigue utilizar la memoria RAM de forma muy eficiente.

Pese a que el código de un programa puede desaparecer de la RAM para dejar espacio a otros programas, no será necesario salvar la información a disco. Si fuera necesario volver a cargarlo en RAM bastará acudir al fichero que contiene su código ejecutable.

Esto hace que los ficheros que contienen programas que se están ejecutando no puedan ser borrados ni modificados por nadie, ni siquiera por root.

Copie el ejecutable 'top' en el directorio /tmp (Seguramente estará en 'bin/top' y sino deberiá ser capaz de encontrarlo). Ejecutelo como /tmp/top y desde otra sesión intente borrar el fichero. Sencillamente le resultará imposible pese a que es usted es dueño de esa copia.

Cuidado. Esto no es aplicable a los scripts de bash o a otros scripts porque en realidad estos programas son datos que se ejecutan dentro de un programa que los interpreta en lugar de hacerlo en la CPU.

Paginación
La información se organiza en bloques y los bloques que la CPU usa se mantienen el mayor tiempo posible en RAM. A estos bloques de información se les llama páginas.

Se intenta reducir el número de lecturas y escrituras a disco. Para ello se actúa en base a suposiciones de uso de la memoria. Las páginas recientemente utilizadas tienen mayor probabilidad de volver a ser usadas pronto que aquellas que ya hace tiempo que no han sido accedidas. Intentaremos explicar porque.

Los programas se comportan generalmente usando pequeñas partes de código durante la mayor parte del tiempo mientras que otras partes se ejecutarán de forma muy ocasional.

Además los programas usan partes comunes de código que pertenecen a librerías compartidas. El aprovechamiento de la RAM se consigue manteniendo una sola copia de ese código compartido en memoria RAM.

Los accesos a estas páginas de memoria que contienen código pueden serán para leer información. En el caso de datos de lectura escritura también podrán ser accedidas para su modificación. Cuando una página ha sido modifica se marca para escribir su contenido en disco en el caso de que se necesite el espacio que ocupa para otra página distinta.

El area de disco que se utiliza para ello se llama swap (también se denomina memoria de intercambio) y un usuario normal no puede modificarla aunque root si puede hacerlo para aumentarla o disminuirla de tamaño por ejemplo.

La memoria virtual es la suma de la memoria física (RAM) más el swap (area de intercambio) menos algunas páginas de reserva. Las paginas de reserva están limpias de información para poder cargar en ellas información nueva desde la swap cuando se necesita.

Al proceso de traer una página de la swap a memoria física se le llama (swap in). Esto necesita un tiempo y normalmente el proceso espera dormido. Al proceso de volcar una página de memoria física a swap se le llama (swap out) y generalmente el proceso ya está dormido cuando esto ocurre.

Cuando se agota la memoria física (RAM) se empezará a usar la swap.

Cuando la memoria virtual se agota el kernel empezará a matar procesos. Dicho de otro modo un proceso que intenta usar memoria virtual cuando está agotada terminará recibiendo una señal del kernel que provocará su muerte.

Utilidades para monitorizar el uso de memoria virtual

Ya vimos el uso de 'top'. Nos sirvió para introducir el tema. Se trata de un comando realmente bueno para localizar los procesos responsables del mayor consumo de CPU y memoria.

La información del estado de núcleo del sistema se encuentra en /proc/ Ya comentamos en capítulos anteriores parte de su cometido y también resulta muy útil para obtener información sobre el uso de la memoria en el sistema.

$ cat /proc/meminfo
        total:    used:    free:  shared: buffers:  cached:
Mem:  264294400 103956480 160337920 58118144 10465280 53153792
Swap: 402997248        0 402997248
MemTotal:    258100 kB
MemFree:     156580 kB
MemShared:    56756 kB
Buffers:      10220 kB
Cached:       51908 kB
SwapTotal:   393552 kB
SwapFree:    393552 kB

También 'ps v' puede darnos información sobre el consumo de memoria virtual de procesos. Con top solo salía la información de los procesos que más consumen pero quizás necesitemos información de determinados procesos. Por ejemplo para sacar la información de los procesos asociados al nuestro terminal bastará usar 'ps v'. Si necesita precisar otras condiciones consulte las opciones de ps en su página man.

$ ps v
  PID TTY      STAT   TIME  MAJFL   TRS   DRS  RSS %MEM COMMAND
  301 tty3     S      0:00    600   420  1743 1300  0.5 -bash
 1640 tty3     S      0:00    326   518  1829 1568  0.6 vi recursyst.dat
 1665 tty3     S      0:00    194   420  1451  832  0.3 /bin/bash -c (ps v) >/tmp/vok2gkuv 2>&1
 1666 tty3     R      0:00    240    55  2844 1176  0.4 ps v

Si solo deseamos saber cuanta memoria queda libre podemos usar 'free'. Este comando también indica si se está usando la swap.

$ free
             total       used       free     shared    buffers     cached
Mem:        258100     101668     156432      56800      10248      51988
-/+ buffers/cache:      39432     218668
Swap:       393552          0     393552

Con 'vmstat' podemos tener información del uso de memoria virtual, cpu, y del consumo de entrada salida.

$ vmstat
   procs                      memory    swap          io     system         cpu
 r  b  w   swpd   free   buff  cache  si  so    bi    bo   in    cs  us  sy  id
 0  0  0      0 156404  10252  52000   0   0     2     1  141   132   1   0  98

Con 'memstat' podemos ver el uso de memoria virtual de los procesos y de las librerías compartidas.

$ memstat
     44k: PID     1 (/sbin/init)
     60k: PID    98 (/sbin/portmap)
     80k: PID   156 (/sbin/syslogd)
    468k: PID   158 (/sbin/klogd)
     40k: PID   165 (/sbin/kerneld)
     52k: PID   170 (/sbin/rpc.statd)
    140k: /lib/libreadline.so.4.1 228
     24k: /lib/libwrap.so.0.7.6 98 271
     72k: /lib/ld-2.1.3.so 1 98 156 158 165 170 178 179 180 181 182 188 193 ...
    864k: /lib/libc-2.1.3.so 1 98 156 158 165 170 178 179 180 181 182 188 19...
     20k: /lib/libcrypt-2.1.3.so 228 292 295 306 307 308 309 310 311 461 966...
     ...: .........................
     ...: .........................
     ...: .........................
--------
3768092k

Para mostrar solo la informacion del proceso 271 (en nuestro caso es sendmail) haríamos lo siguiente:

$ memstat | grep 271
    264k: PID   271 (/usr/sbin/sendmail)
     24k: /lib/libwrap.so.0.7.6 98 271
    232k: /lib/libdb-2.1.3.so 156 193 206 271 292 295 306 307 308 309 310 31...
     76k: /lib/libnsl-2.1.3.so 98 228 271 284 287 292 295 299 300 301 303 30...
     40k: /lib/libnss_compat-2.1.3.so 228 271 284 287 292 295 299 300 301 30...
     20k: /lib/libnss_db-2.1.3.so 156 193 206 271
     32k: /lib/libnss_files-2.1.3.so 156 170 193 206 271 292 295 306 307 308...
     48k: /lib/libresolv-2.1.3.so 271
    308k: /usr/sbin/sendmail 271

Con esto vemos lo bien aprovechado que está el sistema ya que muchas de las librerías aparecen compartidas con un montón de procesos que se están ejecutando.

Hemos conseguido ver las librerías compartidas que esta usando y cuanto ocupan en memoria pero la utilidad para saber usa o usará un ejecutable es 'ldd'. Este comando no necesita que el programa se esté ejecutando porque saca la información analizando el código del ejecutable.

$ ldd /usr/sbin/sendmail
	libdb.so.3 => /lib/libdb.so.3 (0x40019000)
	libwrap.so.0 => /lib/libwrap.so.0 (0x40054000)
	libnsl.so.1 => /lib/libnsl.so.1 (0x4005b000)
	libresolv.so.2 => /lib/libresolv.so.2 (0x40071000)
	libc.so.6 => /lib/libc.so.6 (0x40081000)
	/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Actuación con procesos conflictivos
Llegados a este punto conviene explicar que se puede hacer con un proceso conflictivo. Evidentemente la solución más drástica es matarlo. Para ello se usará primero un 'kill -15 PID'. La señal 15 es un SIGTERM que permite al proceso parar ordenadamente pero si el proceso ignora esta señal habrá que matarlo con un 'kill -9 PID'. La señal 9 es un SIGKILL que no puede ser ignorado.

De todas formas puede ocurrir que nos de pena matar a un proceso que se había comportado razonablemente durante bastante tiempo y que está llevando a cabo unas tareas que nos interesan.

Lo más suave que se puede hacer es bajarle la prioridad con 'renice' por ejemplo 'renice 20 PID'. Bajar la prioridad de un proceso no perjudica en nada al proceso cuando la CPU está ociosa por falta de trabajo. Si el proceso continua consumiendo demasiados recursos y no podemos bajar más su prioridad podemos pararlo sin matarlo. Para ello se envía al proceso una señal SIGSTOP mediante 'kill -19' Para volver a arrancarlo podemos hacerlo mandando una señal SIGCONT 'kill -18'

Test
Puede comprobar sus conocimientos respondiendo el siguiente test.
Para ello seleccione las opciones que se ajusten a la verdad y luego pulse el boton para ver el resultado de su test.

1 Procesos que consuman mucho es preferible ejecutarlos uno después de otro y no asimultaneamente.
2 ulimit Sirve para limitar los recursos del sistema y solo puede ser usado por el superusuario
3 En un sistema con fuerte carga conviene dejar una sesión con 'top d 1' para vigilar el sistema.
4 Memoria virtual y memoria swap es lo mismo.
5 Memoria física y memoria de intercambio es lo mismo.
6 Segmento de texto es aquel que contiene el código ejecutable de un programa.
7 El segmento de texto no se puede compartir por ser un segmento de lectura escritura.
8 Procesos de programas distintos no pueden compartir en memoria su código.
9 Un proceso que no puede continuar por falta de algún recurso queda esperando y consumiendo CPU.
10 Lo menos drástico que se puede hacer con un proceso que consuma muchos recursos es bajar su prioridad con 'renice -20 PID'
11 No se puede borrar un shell-script cuando se está ejecutando.

Resultado del test


Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos
contacto