Lo hice y lo entendí

El blog de Vicente Navarro
08 nov

Memoria swap en un fichero. ¿Cuánta memoria swap necesitamos?

Hace tiempo que lo venía haciendo en algunos sistemas Linux, pero últimamente, cada vez me he vuelto más estricto con esto: ¡Se acabaron las particiones de memoria de virtual o de swap en sistemas domésticos!

Si lo pensamos detenidamente, tener una partición de swap, es tener una cantidad de espacio ahí abandonada y que en rarísimas ocasiones veremos usar. Y si el kernel llega a usarla, hemos de prepararnos, en la mayoría de los casos, a experimentar un sistema extraordinariamente pesado, con lo que si es una situación habitual, más nos vale comprar una ampliación de la memoria. De nuevo, recalcar que me refiero a sistemas domésticos. En un servidor, la memoria swap nos puede ayudar a absorber un pico puntual de trabajo que sólo se da en ocasiones aisladas y que no podemos dejar de atender.

Para nuestro sistema doméstico, resulta mucho más conveniente un fichero de swap, igual que hacen todos los Windows basados en NT con el pagefile.sys. Las ventajas son muchas:

  • Lo podemos poner en la partición/sistema de ficheros que queramos
  • Podemos borrarlo para liberar espacio si éste nos hace falta
  • Podemos crear uno nuevo en cualquier momento
  • No supone tener un espacio en disco bloqueado inservible para otros propósitos
  • Posiblemente su rendimiento sea algo peor que el de una partición dedicada de swap, pero si la memoria swap llega a tener que usarse intensivamente, el rendimiento será ya nefasto, ¿qué más da que sea un poco peor?

Y, además, lo último, lo de que el rendimiento es peor parece que ni siquiera es cierto. En la LKML leemos que usando kernels 2.6 el rendimiento de un fichero de swap no es peor que el de una partición de swap (Jesper Juhl: a swap file is just as fast as a swap partition):

With a 2.4.x kernel swap files were slower than swap partitions, but with the 2.6 kernel a swap file is just as fast as a swap partition.

El cómo hacer esto, apenas tiene misterio. Partimos de una situación sin memoria swap (aunque no habría ningún problema en tener varios ficheros de swap o conbinaciones de particiones y ficheros de swap):

$ free
             total       used       free     shared    buffers     cached
Mem:       4051784    4022512      29272          0      20764    3324168
-/+ buffers/cache:     677580    3374204
Swap:            0          0          0

Creamos un fichero vacío con dd del tamaño deseado para la swap:

$ sudo dd if=/dev/zero of=swapfile bs=1G count=4
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 49.6084 s, 86.6 MB/s

Configuramos el fichero como memoria swap y activamos la memoria:

$ sudo mkswap /swapfile 
Setting up swapspace version 1, size = 4294963 kB
no label, UUID=f2fdfced-a3a9-4762-b6d0-db57edced413

$ sudo swapon /swapfile

Y, finalmente, comprobamos que ahora ya tenemos memoria swap disponible:

$ free
             total       used       free     shared    buffers     cached
Mem:       4051784    2339872    1711912          0      11080    1773356
-/+ buffers/cache:     555436    3496348
Swap:      4194296          0    4194296

Si queremos que esta memoria swap siempre se active automáticamente durante el arranque del sistema, no tenemos más que añadir una línea como esta al fichero /etc/fstab:

/swapfile      none            swap

Si en un momento dado, queremos desactivar el uso de dicho fichero como memoria swap (para hacerla más grande, más pequeña, eliminar el fichero), sólo tenemos que usar el comando swapoff (la memoria swap no se tiene que estar usando).

De nuevo en la LKML, leemos que (Andrew Morton: Re: Swap partition vs swap file) debemos procurar crear el fichero de swap al principio de generar el sistema de ficheros, cuando aún haya mucho sitio disponible, y el fichero se fragmente lo mínimo posible, ya que una gran fragmentación sí que podría perjudicar mucho el rendimiento:

In 2.6 they have the same reliability and they will have the same performance unless the swapfile is badly fragmented.

Create the swapfile when the filesystem is young and empty, it’ll be nice and contiguous. Once created the kernel will never add or remove blocks. The kernel won’t let you use a sparse file for a swapfile.

En realidad, aparte de crear el fichero de swap con el sistema de ficheros muy vacío, tampoco podemos hacer gran cosa para evitar su fragmentación en sistemas ext3, ya que aunque la fragmentación se mantiene en niveles bajos (Why doesn’t Linux need defragmenting?), haberla, hayla, y no tenemos más que usar el comando filefrag para ver cuánta:

$ sudo filefrag swapfile
swapfile: 2720 extents found, perfection would be 34 extents

Para ext3 no hay herramientas de desfragmentación, para ReiserFS tampoco, pero para XFS y ext4 (recién incluido en el kernel 2.6.28 por primera vez como código estable) sí que hay.

Además, se puede hibernar (suspender a disco) en los ficheros de swap, sin necesidad de particiones de swap. Si usamos el sistema propio del kernel, el swsusp, tal y como leemos en las fuentes del kernel, en Documentation/power/swsusp-and-swap-files.txt, podemos usar incluso ficheros fragmentados (Suspend to disk amb fitxers swap).

Si usamos el TuxOnIce (Hibernación en Linux con TuxOnIce. Notas sobre los initrd y sobre cpio.), el otro sistema importante para hibernar en Linux, también podremos usar ficheros de swap: TuxOnIce: Using a swapfile.

Sin embargo, no debemos de olvidar que la hibernación en Linux aún no está muy perfeccionada: Fijémonos en que la idea “Fix Suspend and Hibernate” es la primera y con diferencia en Ubuntu Brainstorm.

Hasta aquí, hemos visto que es buena idea usar un fichero de swap en lugar de una partición de swap y cómo crearlo. Pero, ¿de qué tamaño deberíamos ser?

Los usuarios de Linux nos hemos hartado de oír la famosa regla de oro que hay que tener de swap el doble de la memoria física (Red Hat Enterprise Linux 4: System Administration Guide: What is Swap Space?):

The size of your swap should be equal to twice your computer’s physical RAM for up to 2 GB of physical RAM. For physical RAM above 2 GB, the size of your swap should be equal to the amount of physical RAM above 2 GB. The size of your swap should never less than 32 MB.

Pero por muchas veces que lo leamos, no por eso es más cierto. Yo ahora tengo 4 GiB de memoria RAM instalados en mi sistema. El uso de la memoria swap es de 0% en la inmensa mayoría de las ocasiones, incluso cuando tenía 2 GiB. No tiene sentido malgastar 8 GiB de espacio en disco para la memoria swap. Entonces, ¿de dónde viene dicha regla?

Los procesos normalmente van pidiéndole memoria al sistema operativo, pero muchas veces no la usan toda en ese momento, sino que la piden para usarla más adelante. En sistemas UNIX clásicos, si un proceso pedía memoria, el sistema garantizaba absolutamente que el proceso tendría esa memoria cuando la necesitara. Para poder garantizar toda la memoria pedida, lo que se hacía era reservar de verdad esa memoria en la swap: el sistema aseguraba que la memoria pedida estaría disponible, aunque ésta fuera virtual. Así, resulta que era conveniente tener dos veces la cantidad de RAM en la swap, para que los procesos pudieran pedir hasta dos veces la memoria RAM, aunque realmente no la usaran, y sólo por el hecho de que la memoria pedida se reservaba de verdad (Why are there so many stories about allocating swap space?).

En Linux, el sistema deja a los procesos pedir más memoria de la que existe, aunque no haya tanta. En muchos casos, los procesos no usarán realmente tanta y podrán funcionar como si esa memoria pedida existiera. Si en un momento dado los procesos pidieran usar esa memoria que no existe, el kernel elegirá un proceso y lo matará, así de sencillo. El excelente documento When Linux Runs Out of Memory nos los explica muy en detalle.

En el siguiente ejemplo, ejecuto dos veces en diferentes terminales la pequeña utilidad malloc4g para llenar la memoria que escribí en La odisea de ampliar la memoria a 4 GiB. malloc4g primero pide un bloque muy grande de memoria y después va escribiendo en ella. He configurado la utilidad para llenar 3GiB, y en el momento de hacer la prueba, el sistema tiene 4 GiB de RAM y un fichero de swap de 512 MiB. Si ejecuto los dos a la vez, aunque la utilidad pida toda la memoria al principio, no me da problemas de ejecución, aunque entre los dos hayan pedido 6 GiB, más de la memoria que el sistema tiene realmente, pero en un momento dado, el kernel mata a uno de los dos:

(terminal 1) $ ./malloc4g 
Estamos en un sistema de 64 bits.
Llenando 3221225472 bytes
2163 MB / 3072 MiB
Killed

(terminal 2) $ ./malloc4g 
Estamos en un sistema de 64 bits.
Llenando 3221225472 bytes
3071 MiB / 3072 MiB

En cambio, si espero un poco a ejecutar una segunda instancia de la utilidad, el sistema ya no me da la memoria:

$ ./malloc4g 
Estamos en un sistema de 64 bits.
El malloc no ha funcionado.

Vemos, por tanto, que el kernel concede alegremente 6 GiB de RAM a dos procesos cuando sólo tiene 4.5 GiB. Luego, si ambos usan la memoria concedida, el sistema tiene que elegir un proceso para matarlo. En sistemas UNIX clásicos que reservan memoria, los procesos hubieran tenido la memoria asegurada sin peligro a morir.

Con el modelo usado por Linux, el sistema vive por encima de sus posibilidades. Es como un hombre con una nómina y una tarjeta de crédito. Puntualmente podrá gastar más de lo que gana cada mes gracias al crédito, pero no es sostenible si el gasto no baja otros meses.

Tal y como nos apunta el documento When Linux Runs Out of Memory, las fuentes del kernel, en “Documentation/vm/overcommit-accounting“, nos explican cómo ajustarlo para que sea mucho más conservador en esta política, y que sea mucho más parecido a lo que hacían otros UNIX, algo que puede ser conveniente en sistemas críticos. Lo podemos hacer escribiendo un valor en /proc/sys/vm/overcommit_memory en función de nuestras necesidades:

  • 0 means that the kernel will use predefined heuristics when deciding whether to allow such an overcommit. This is the default.
  • 1 always overcommits. Perhaps you now realize the danger of this mode.
  • 2 prevents overcommit from exceeding a certain watermark. The watermark is also tunable through /proc/sys/vm/overcommit_ratio. Within this mode, the total commit can not exceed the swap space(s) size + overcommit_ratio percent * RAM size. By default, the overcommit ratio is 50.

En HP-UX, por ejemplo, el sistema tradicionalmente era el de reserva de memoria en la swap, pero conforme los sistemas tienen más RAM y ésta es a la vez más barata, esto puede resultar muy poco práctico. Por eso, en versiones más modernas, se permite configurar el sistema para que la swap no se tenga que reservar: HP-UX swapmem_on configurable kernel parameter, HP-UX Tuning Guide: How Much Swap Do I need?

Entonces, ¿cuánta memoria swap necesitamos en el sistema? Pues la realidad, es que sólo la experiencia de trabajo diario con el sistema nos lo puede decir. Es la monitorización diaria de los parámetros de memoria usada la que nos dirá si necesitamos más memoria virtual o menos. Cualquier decisión al respecto a priori, necesariamente ha de quedar corta, o ha de derrochar espacio de disco.

Y precisamente, si usamos particiones de swap quedamos obligados a decidir el espacio que le vamos a dedicar antes siquiera de haber instalado el sistema operativo, a tomar la decisión a priori y con pocas facilidades para modificarla más adelante. En cambio, usando ficheros de swap, nos resultará extremadamente sencillo probar con unos valores o con otros. En algunos casos, podremos incluso prescindir completamente de cualquier espacio de swap con la tranquilidad de que podemos añadirlo en el mismo instante que nos sea necesario.

Un sencillo top o el comando free nos ayudarán a verlo instantáneamente:

 $ top

top - 20:15:22 up 33 min,  4 users,  load average: 4.24, 1.92, 2.43
Tasks: 165 total,   1 running, 163 sleeping,   0 stopped,   1 zombie
Cpu(s): 62.5%us,  6.4%sy,  0.0%ni,  3.3%id, 25.8%wa,  0.0%hi,  2.0%si,  0.0%st
Mem:   4056040k total,  4034360k used,    21680k free,      528k buffers
Swap:   524280k total,   293328k used,   230952k free,    83716k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
 7800 vicente   20   0 3075m 1.7g  368 D   64 42.8   1:05.81 malloc4g           
 7799 vicente   20   0 3075m 1.7g  368 D   60 44.3   1:09.24 malloc4g

$ free
             total       used       free     shared    buffers     cached
Mem:       4056040    4034460      21580          0        252      66184
-/+ buffers/cache:    3968024      88016
Swap:       524280     394392     129888

La utilidad vmstat, parte del paquete procps es otra buena herramienta para monitorizar los valores de uso de memoria. En el siguiente ejemplo, le pedimos que nos dé un 5 valores separados en el tiempo 10 segundos:

$ vmstat 10 5
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 3  0      0 2454476 109612 603852    0    0    70    72  295  492  9  2 87  2
 1  0      0 2182672 109616 603908    0    0     0     1  817  705 51  2 48  0
 1  0      0 1911972 109656 603920    0    0     2    18  846  762 51  2 47  0
 1  0      0 1648280 109684 603924    0    0     0    21  826  694 50  2 48  0
 1  0      0 1380100 109684 603924    0    0     0     6  808  704 51  2 48  0

También podemos monitorizarlo usando las gráficas de rendimiento que los escritorios modernos incluyen como applets, como es el caso del “System Monitor” de GNOME:

Pero definitivamente, la mejor forma de tener datos históricos que estudiar para ver la evolución del uso de memoria swap es instalar y usar el paquete sysstat (una colección de herramientas de monitorización de rendimiento) para que almacene dichos datos. La siguiente entrada: sysstat, la colección de herramientas de monitorización de rendimiento, detalla cómo usar estas herramientas.

Observando detenidamente el uso de memoria que tiene a lo largo del tiempo nuestro sistema con alguna de las múltiples herramientas que Linux pone a nuestras disposición, es como podremos decidir con autoridad cuánta swap necesitamos. No hemos de ser tacaños, ya que el kernel a veces envía a swap páginas de memoria que hace tiempo que no se usan sólo para tener más memoria para sus buffers y para sus cachés, algo que beneficia mucho al rendimiento del sistema. Pero tampoco tiene sentido que despilfarremos disco.

Los ficheros de swap son nuestra mejor arma para conseguir tener el tamaño de memoria swap apropiado a nuestras necesidades.

Actualización 20/11/08: Swap Space: There is a wide-spread myth that swap space should be twice the size of RAM…

:wq

Entradas relacionadas

15 Comentarios a “Memoria swap en un fichero. ¿Cuánta memoria swap necesitamos?”

  • trinux dice:

    ¡Muy buen artículo!! Gracias, lo voy aplicar en mi caso.

  • En un sistema doméstico ya no vienen discos de menos de 120, 160 gb, no?
    En este tipo de sistemas, con 1, 2 o 4 gb de ram, el swap necesario no supera nunca 1gb… es más, con 500 mb estarían perfecto, o incluso sin nada de swap. Así que, en un disco de 160gb, hacer una partición de 1gb (como mucho) para swap… es desperdiciar mucho espacio?? es menos de un 1%… y te evitas todo este lío…

    Igualmente gracias por la información, en algunos casos puede ser útil.

    Saludos!

  • @trinux ¡Gracias! ¡Cuanto tiempo sin verte por aquí! Veo además que ahora tienes blog propio: ¡Me lo apunto!

    @CoskiBukowski Gracias por el comentario. Tienes razón en parte, pero fíjate en que yo mismo aún tengo un pequeño servidor con 512 MiB de RAM basado en VIA EPIA con un disco de 40 GiB y en el que siempre me falta sitio en disco y memoria. También está el caso de los nuevos ultraportátiles, que no tienen excesiva RAM y sí muy poco disco, normalmente de estado sólido. En estos casos, el prescindir de una partición de swap para usar un flexible fichero de swap nos da mucho juego para poder conjugar la escasez de memoria con la escasez de disco. Cuando hay abundancia de todo, lo último que nos preocupará es el espacio que dedicamos a la swap.

  • Super Coco, en esos casos tienes absoluta razón.

    Un saludo!

  • trinux dice:

    Siempre he estado siguiendo tu blog, no lo dudes… soy de comentar poco, ya ves :)

  • Maks3w dice:

    Encantado de volver a leerte de nuevo SuperCoco.

    Personalmente me quedo que la mayor ventaja es no redimensionar la partición de SWAP (que por otro lado no se fragmentaría igual que un fichero o podría ser colocada inicialmente en los sectores más próximos al disco)

    De todas formas muy buen artículo que me ha ayudado a comprender mejor la memoria virtual gracias a la extensa referencia.

  • Maks3w dice:

    Encantado de volver a leer tus artículos SuperCoco.

    La verdad es que la ventaja que yo le veo es no tener que redimensionar las particiones, por otra parte tener una partición sobre todo al inicio del disco no fragmentaría tanto como un fichero y garantiza que tiene un acceso más rápido. (Por decir algo a favor de una partición SWAP)

    PD: ¿Funciona en este blog el OpenID de Google? He utilizado https://www.google.com/accounts/o8/id y creo que no me ha ido como esperaba. Google ya es un proveedor de identificación OpenID

  • @Maks3w Gracias, me alegro de que haya parecido interesante.

    Sobre los comentarios, era sólo que Akismet te los había detectado como spam (¿?¿?). Ya ves que en el primero el OpenID sí ha funcionado.

  • Maks3w dice:

    Leche muestra los comentarios con mucho retraso y había pensado que no lo mandaba, lo siento SuperCoco.

  • AlBundy dice:

    De nuevo un excelente trabajo SuperCoco, recompilando toda la información
    que pulula dispersa por internet, ordenándola, y exponiéndola de manera
    clara y coherente. Enhorabuena.

    Mi granito de arena sobre el manejo de swap.

    Recuerdo haber compilado Wine en un pentium1 con 32 ó 64 MB de
    RAM, y con una partición swap de aproximadamente el tamaño de la
    RAM instalada. El Wine por aquella época (ahora supongo que también)
    llevaba un makefile que iba compilando los diversos subsistemas de Wine,
    generando ficheros \\*.o. Y cuando tenía todos los \\*.o los fusionaba en
    un único ejecutable. Mi máquina compilaba sin muchos problemas todos los
    \\*.o parciales, pero al llegar a la etapa de crear el “wine.exe” final
    se quedaba sin memoria, y saltaba el OOM killer, matando el proceso gcc.

    Primero intenté añadir al sistema otro disco duro IDE, en el que tenía
    otra instalación de linux vieja, con su propia partición de swap, y
    activar a la vez las 2 particiones de swap. El OOM killer volvía a saltar.

    Luego me dediqué a crear un fichero de swap, empezando con un tamaño
    pequeño y aumentando su tamaño hasta que el wine compiló con éxito.

    Conclusión: podemos usar simultáneamente varias particiones de swap y
    varios ficheros de swap: el tamaño total de swap será la suma de los
    tamaños de los elementos de swap individuales.

    Por otro lado, swapon tiene opciones para dar prioridades a los distintos
    elementos de swap individuales, priorizando así el uso de aquellos
    elementos situados en discos duros más rápidos.

    Estoy de acuerdo en que en sistemas modernos con un “gritón” de Gigas de
    RAM, la swap ha dejado de ser necesaria. Pero está creciendo el mercado
    de máquinas empotradas con pocos recursos de RAM (ARM y similares), en
    los que la swap seguirá siendo necesaria, al menos durante algún tiempo.

    Ya que has mencionado a los UNIX de HP, en algunos *BSD la partición
    de swap se usa también como “dump device” con propósitos de depuración:
    si el kernel falla (algo no muy raro si usamos versiones inestables del
    kernel), se vuelca un core del kernel a la partición de swap, de manera
    que pueda examinarse detenidamente las causas del fallo.

    Por último, mencionas que los Windoze usan swap bajo la forma de un
    fichero de paginación pagefile.sys. Pero no mencionas que podemos tener
    dicho fichero de paginación en una partición dedicada, partición que
    podemos situar convenientemente en los discos/sectores más rápidos de
    nuestro sistema. Y una vez tengamos una partición dedicada para la
    swap de Windows, también la podemos usar para swap de Linux.

    Puesto que Linux y Windows usan formatos distintos en su dispositivo de swap,
    sería necesario un script en el arranque de Linux que hiciese mkswap
    sobre la partición antes de montarla con swapon, y otro script en el
    apagado de Linux que restaurase la partición a un formato que entendiese
    Windows. Esto está explicado en varios HOWTO de Linux, y era cierto al
    menos con versiones antiguas de MS-DOS y Windows.

    Saludos, y perdón por la extensión de mi comentario.

    AlBundy

  • Sagman dice:

    Este artículo me viene como anillo al dedo Super ;) porque en mi Acer Aspire One alguna vez me quedo sin memoria principal ya que no tengo swap para aprovechar todo el espacio (8GB son muy poco, pero suficiente para el sistema y 4 cosas :) ). Lo que ahora temo es que con el archivo swap se me estropee la unidad SSD más rapido de lo que debiera (Tienen muy pocos ciclos de escritura). Quizás la solución pasa por crear esa unidad en una tarjeta SD o en un pendrive. Ya veré que hago :) .
    Saludos y gracias Super ;)

  • @AlBundy Para nada tenemos que perdonarte por la extensión del comentario, ya que dices cosas realmente interesantes. Por un lado, aunque ya lo decía por arriba de forma oscura, es importante recalcar que podemos combinar todas las particiones swap y ficheros de swap que queramos. Además, haces bien en indicar la posibilidad de establecer prioridades sobre qué dispositivo de swap usar primero. ¡Cómo rascaba el disco por la swap continuamente en aquella época! ¿Verdad?

    Sobre lo de sistemas UNIX que en caso de crash hacen un volcado a la swap, me ha recordado que el LKCD, que creo que ya no se usa, hacía algo parecido: LKCD Linux Kernel Crash Dump, Linux Crash HOWTO. Ahora parece que se hace con Kdump, incluido ya en el kernel, y con el que podemos arrancar un segundo kernel desde el colgado con el que analizar las causas de la caída del primero, pero no usa la swap. Más información en las fuentes del kernel en “Documentation/kdump/kdump.txt“.

    Sobre compatir la partición de swap entre Windows y Linux, si no me equivoco, la aproximación antigua (Linux Swap Space Mini-HOWTO) era usar una partición FAT y poner el fichero de swap de Windows en ella cuando se entraba en Windows, y hacer un mkswap cuando se volvía a Linux. Ahora parece que hay una forma más moderna de hacerlo (How To use Linux Swap Partition as windows Swap file usando el driver SwapFs.

    ¡Muchas gracias por tu detallado comentario!

    @sagman Pues sí, en un Acer Aspire One este tema puede resultar especialmente interesante. Pero crear un fichero de swap también puede ser muy dañino con el disco de estado sólido, que tiene peor reemplazo. Probablemente, como muy bien dices, la solución sea escribirlo en una tarjeta externa.

    ¡Gracias a ti por el comentario!

  • RuBiCK dice:

    Excelente artículo!!!!

    Si me permites, voy a añadir algo más de info referente a la swap; en HPUX existe la Pseudo Swap, que el concepto es hacer creer al sistema operativo que tenemos más swap de la que tenemos realmente ya que en realidad es ficticia (no existe físicamente) pero de manera logia sí la reconoce el sistema. Esto lo que nos permite es añadir entorno un 75% más de swap al sistema lo cual puede ser verdaderamente util en sistemas muy grandes que nunca usarán tanta cantidad de swap, pero es necesario tener.

    ¿ Conoceis algúna manera de generar o crecer un fichero de swap bajo demanda?

    Un saludo!

  • @RuBiCK ¡Gracias por el apunte! Respecto a hacer el crecer el fichero swap, no me suena, pero sí que puedes añadir tantos como sean necesarios, y si no se están usando, se pueden desactivar con swapoff. Se podría hacer fácilmente un script que añadiera y quitara ficheros de swap según fuera necesario.

  • Jan dice:

    Aunque leo el artículo un poco tarde también quería agradecerte las explicaciones tan detalladas que das, Super Coco.

    Además, y siguiendo el hilo de los comentarios, yo -hasta ahora- había siempre mantenido una partición para el pagefile.sys de Windows y otra para la Swap de Linux (además con la “regla” de una vez y media la RAM) porque no tenía ni idea de que se pudiese usar una misma partición para ambos archivos de intercambio.

    A ver si saco un poco de tiempo para probarlo, que parece muy interesante, y sobre todo, útil.

    ¡Gracias! :)

Tema LHYLE09, creado por Vicente Navarro