jueves, 5 de enero de 2012

Usando el comando kill

For english version

En mi experiencia de trabajo, he escuchado muchas veces con preocupación que la técnica utilizada para terminar procesos / servicios del sistema, es utilizando kill -9, porque el proceso siempre finaliza (verídico). Esta acción presenta un inconveniente, se puede perder información.

Para explicar esto, primero hay que saber que hace el comando kill. El comando kill, lo que hace es enviar señales a los procesos (ver explicación). Este mecanismo de comunicación entre procesos es simple y asíncrono (generalmente). Si ya revisaste el manual (man 7 signal) o el enlace en wikipedia, veras que hay varios tipos de señales, por ejemplo: SIGINT, SIGTERM, SIGKILL, etc; y entre estas, hay ciertas señales que no se pueden manejar, por ejemplo: SIGKILL. ¿Qué significa que no se pueden manejar? Según mi experiencia es que no puedes programar una función que haga algo con la señal, en otras palabras, tu programa nunca la ve llegar, el sistema operativo simplemente la aplica al proceso.

Para comprender mejor este punto conviene programar un poco con nuestro querido lenguaje C (espero que tengas el compilador y todos los juguetes)

Primero creamos un archivo / fichero en C llamado test.c con lo siguiente

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

volatile int FINISH = 0;

void handler(int sig)
{
    if (sig == SIGINT) {
        fprintf(stderr, "Interrupted: Closing files\n");
        FINISH = 1;
    } else if (sig == SIGTERM) {
        fprintf(stderr, "Terminating: Closing files\n");
        FINISH = 1;
    } else {
        fprintf(stderr, "I don't know what to do with this signal\n");
    }
}

int main(void)
{
    FILE *f = NULL;
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = handler;
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction with SIGINT");
    }
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction with SIGTERM");
    }
    if ((f = fopen("test_file.txt", "w")) == NULL) {
        perror("Couldn't open test_file.txt for writing");
        exit(1);
    }
    while (!FINISH) {
        fprintf(f, "hello");
        fprintf(f, " ");
        sleep(5);
        fprintf(f, "world\n");
    }
    fclose(f);
    exit(0);
}

Como puntos importantes, podemos resaltar la función handler, que es la que maneja las señales que definimos con sigaction. Se nota que no intente manejar la señal SIGKILL a.k.a 9, porque no se puede manejar, y si lo intentas el sistema operativo ignorara tu petición. El código como se ve esta en un bucle infinito escribiendo hola mundo en un archivo / fichero. Cuando le enviemos la señal de interrumpir o de terminar, el programa terminara de escribir un último hola mundo y procederá a cerrar el archivo / fichero. Si en cambio terminamos el proceso con SIGKILL, es muy probable que el archivo / fichero termine vacío o con una linea que no diga hola mundo. A probarlo.

Compilar
$ cc -Wall -o test test.c

Ejecutar
$ ./test

En otro terminal ejecutamos alguno de los siguientes tres comandos
pkill -SIGINT test
pkill -SIGTERM test
pkill -SIGKILL test

Entre ejecuciones revisamos el archivo / fichero test_file.txt y comparamos los resultados. Seguramente la vez que ejecutaste el kill con SIGKILL los resultados no fueron los esperados. Ahora que sabes porque por ejemplo, no es conveniente terminar procesos de un manejador de bases e datos con SIGKILL. Puedes terminar con información incompleta o corrupta (cantidades infinitas de diversión ^_^).

Con esta moraleja en mente, ya sabes que primero se intenta terminar un proceso pidiendoselo diplomáticamente (SIGTERM), y ya si despues de un tiempo no responde, llamas a tu amigo sicario (el sistema operativo) para que le haga el trabajo sucio (SIGKILL).

Hasta otra oportunidad.

Alejandro

lunes, 31 de enero de 2011

Ejecutar programas sin necesidad de usar chroot

For english version


Advertencia: Lo siguiente puede que no te funcione. Si al hacer lo aquí expuesto tu máquina explota o no funciona como debería es tu única e intransferible responsabilidad. Debiste probar primero en un sistema de pruebas y no en producción. Ojala tengas un respaldo. No es mi culpa (^_^) .

Hace unos días, tuve la necesidad de ejecutar una aplicación en 32 bits en un sistema en 64 bits. El típico caso de uso para usar chroot. El problema era que la aplicación debe ser ejecutada por otra aplicación, la cual se ejecuta sin privilegios de root (ser root es estar sin protección, be safe kid (^_^) ). Esto limita el uso de chroot (sólo puede ser ejecutado por root), además no quería lidiar con conflictos en las versiones de las librerías  (puede ser nocivo para la salud).

Después de un poco de google y man, llegué a la solución ld.so. Todo consistía en indicarle a la aplicación la ruta de las librerías a usar . Para hacer esto, se ponen primero las rutas a las librerías en la variable de entorno LD_LIBRARY_PATH. Posiblemente esto haga servir tu aplicación. En mi caso no funciono (;_;) . Me salieron errores como por ejemplo problemas de símbolos, etc. Seguí leyendo de ld.so y conseguí la solución. Se estaba usando el loader del sistema, no el que la aplicación debería utilizar. La solución que encontré, además de recompilar la aplicación, es invocar directamente al loader especifico. Para hacer esto, hay que revisar con: ldd <ruta completa aplicación> . Buscar la línea ld-linux, grep es tu amigo, y así obtenemos el ld-linux<algo>.so (enlace simbólico al ld-<version>.so) que utiliza la aplicación.

Un ejemplo

Suponiendo un sistema en 64 bits con una aplicación en 32 bits

Con la siguiente estructura de directorios








Ejecutando
$ ldd /home/<usuario>/<carpeta_aplicación>/bin/<aplicación> | grep ld-linux


Obtenemos (este archivo lo sacamos del sistema original donde funcionaba la aplicación)
/lib/ld-linux.so.2


Copiamos ese loader a
/home/<usuario>/<carpeta_aplicación>/lib/ld-linux.so.2

Para ejecutar la aplicación
$ env LD_LIBRARY_PATH=/home/<usuario>/<carpeta_aplicación>/lib/:$LD_LIBRARY_PATH \
/home/<usuario>/<carpeta_aplicación>/lib/ld-linux.so.2 \
/home/<usuario>/<carpeta_aplicación>/bin/<aplicación> <argumentos>*


Algo engorroso, pero se puede hacer un script. Para cualquier duda se vale preguntar.


Alejandro

domingo, 16 de enero de 2011

¿Estás viviendo tu vida?

Hecho por javierkiopo
www.openclipart.org

Ésta pregunta me la hago a veces. Hace poco tuve la oportunidad de repetírmela, era uno de esos días en los que recuerdas que todos tenemos tiempo de expiración. Me encontraba escuchando atentamente cuando esto resonó en mi mente “no dejes que el tiempo te use, utiliza tu el tiempo”.  Me dieron ganas de sonreír, ¿cómo olvidar cosas tan simples? Todas las veces que he caído en la procrastinación pensado que las cosas se pueden hacer otro día. Con la cabeza llena de proyectos por hacer.

Lo mejor que he ideado para evitar la trampa del “no tengo tiempo ahora, sera para más tarde” y evitar que el tiempo te use. Es ponerle fecha a lo que me propongo a hacer, de algo tenía que servir el project management además de hacer diagramas. Sino pasan los días, las semanas, los años. Y al final resulta que no hiciste nada fuera de la rutina y del día día.

Así que a no malgastar el tiempo que tenemos, es un recurso no renovable, y a cumplir nuestras metas. El como vivimos cada instante es lo único que al final importa.

Alejandro

sábado, 8 de enero de 2011

¡Hola mundo!

Nace un blog para hablar de todo y muy directo.

Siendo fiel a mi palabra, este es todo el primer post.

Alejandro