12
May
2012

Forking de procesos en Linux

La creación de procesos en Linux implica dos etapas: forking y exec -me tomo la licencia de denominarlas así por las familias de funciones implicadas en cada caso-.

En la etapa de forking se duplica el proceso que realiza la llamada de creación. Para esta duplicación se crea un nuevo process descriptor -estructura donde se guarda la información del proceso- y se agrega a la lista de process descriptors que maneja el kernel. Este nuevo process descriptor es muy similar al del proceso padre pero difiere en el process id y en los procesos hijos -el proceso recién creado no tiene hijos y el proceso padre pasó a tener un hijo más-.

En cuanto al espacio de memoria del nuevo proceso, se utiliza la técnica de copy on write para lograr una mayor optimización. Empleando esta técnica ambos procesos tienen su espacio de memoria pero esta división comienza siendo “virtual”. A medida que se realizan operaciones de escritura en la memoria, se duplican las páginas que corresponden.

La etapa de exec permite la carga en memoria y ejecución de un binario. Veremos en este artículo solo forking.

A continuación la parte más divertida:

#include <stdio .h>
#include <stdlib .h>
#include <unistd .h>
 
int main(int argc, char* argv[]){
    pid_t id_del_proceso_hijo = fork();
    pid_t proceso_actual = getpid();
    printf("Mensaje de %d: Mi hijo es %d\n", proceso_actual, id_del_proceso_hijo);
    int* status_ptr = malloc(sizeof(int));
    *status_ptr = -1;
    pid_t estado_del_hijo = waitpid(id_del_proceso_hijo, status_ptr, 0x0);
    printf("Mensaje de %d: Espere hasta que se reportó el proceso %d. Info del reporte: %d\n", proceso_actual, estado_del_hijo, *status_ptr);
    return 0;
}
</unistd></stdlib></stdio>


Leer el resto del artículo »

3
Mar
2011

mtrace: debug de memoria en C (LibC)

La función mtrace proporcionada por LibC nos permite trazar todos las operaciones de obtención, redimensionamiento y liberación de memoria realizadas por las funciones malloc, realloc y free respectivamente. De esta forma, es posible detectar memory leaks (memoria sin liberar) en nuestra aplicación.

La función mtrace se vale de la variable de entorno MALLOC_TRACE para conocer la ruta donde guardar el resultado de las operaciones realizadas.

Con la función muntrace podemos detener el monitoreo iniciado por mtrace. Es posible encapsular bloques del programa con mtrace y muntrace.

Código de ejemplo:

#include <stdio .h>
#include <stdlib .h>
#include <mcheck .h>
 
int main () {
 
	setenv("MALLOC_TRACE", "/root/c/mtrace/output", 1);
 
	mtrace();
 
	void* puntero_a_memoria =  (void*)malloc(1);
 
	return 0;
}
</mcheck></stdlib></stdio>

Cambien el path /root/c/mtrace/out por el que les resulte conveniente. El proceso debe tener permisos de escritura allí.


Leer el resto del artículo »

3
Mar
2011

Variables de entorno con LibC

LibC provee algunas funciones para manejar las variables de entorno. Debemos incluir la Standard Library (stdlib.h) para acceder a ellas.

Limpiar las variables de entorno:

int clearenv(void);

Escribir o modificar una variable de entorno:

int putenv(char *string);

El parámetro string es de la forma VARIABLE=valor

Escribir o modificar una variable de entorno:

int setenv(const char *name, const char *value, int overwrite);

name es el nombre de la variable de entorno, value el valor y overwrite (0 o número distinto de 0) permite especificar la acción para el caso de que la variable de entorno ya exista con un valor determinado.

Remover una variable de entorno:

int unsetenv(const char *name);

name es el nombre de la variable de entorno.

3
Mar
2011

Variables de entorno en bash

Las variables de entorno de una shell Linux se utilizan para trabajar sobre ella o para dar información de contexto a los procesos que desde ella se ejecuten. Ese contexto sirve de datos al proceso y le permite, por ejemplo, modificar su compartamiento.

Las shells tienen un conjunto de variables de entorno cargadas por defecto. Los usuarios pueden definir más variables y modificar los valores existentes. Sin embargo, cualquier cambio realizado es temporal: al abrir una nueva shell, nos encontraremos nuevamente en el estado por defecto.

Los siguientes comandos aplican a la shell bash.

Para ver las variables de entorno:

printenv

Para imprimir el valor de una variable específica:

echo $VARIABLE

En caso de que la variable sea utilizada únicamente en la shell, podemos definirla de la siguiente forma:

VARIABLE=valor


Leer el resto del artículo »

2
Mar
2011

Disowning procesos en Linux

disown procesos linux

Cuando ejecutamos procesos en Linux desde un terminal, los procesos quedan “atados”. Esto significa que al cerrar el terminal, los procesos mueren. Voy a comentarles en este artículo dos formas de crear procesos independientes, que pertenecen al usuario desde el que se ejecutaron pero a ningún terminal en particular.

nohup

Lanzamos el proceso de la siguiente forma:

nohup ping 127.0.0.1

Con Ctrl + z detenemos el proceso para volver al terminal. Ingresando bg lo enviamos al background y podremos cerrar el terminal sin que el proceso muera.


Leer el resto del artículo »

26
Feb
2011

ACLs en OS X Server y Linux

El esquema de permisos tradicional en los sistemas UNIX diferencia a los usuarios que pueden leer, escribir o ejecutar un archivo o directorio en tres clases: propietario, grupo y otros. Si bien para muchos escenarios esto es suficiente, puede necesitarse mayor granularidad.

Imaginemos que necesitamos darle al usuario test permisos de lectura sobre el archivo archivo. Supongamos que el usuario y grupo propietarios de archivo son root y wheel respectivamente. Además, supongamos que no queremos que test pertenezca a wheel para no tener permisos sobre archivos que no le corresponde. ¿Cómo resolvemos la situación?


Leer el resto del artículo »

7
Feb
2011

Evitar modificaciones al archivo /etc/resolv.conf

resolv.conf
El archivo /etc/resolv.conf en Linux guarda la información sobre los servidores recursivos que se utilizarán para resolver las consultas de DNS. El software Network Manager modifica automáticamente ese archivo, por ejemplo con información obtenida de un servidor de DHCP. En algunos casos nos interesa que no se modifiquen estos servidores, porque queremos utilizar alguno específico o porque, como me estaba sucediendo a mí, se está grabando el archivo sin información.

Si modificamos manualmente el archivo, en el próximo reinicio Network Manager hará de las suyas.

El workarround que encontré es aprovechar una característica de Linux para setear atributos en los archivos y, en este caso, hacerlo inmodificable. Ni siquera como root podremos modificarlo.

Para setear el atributo de inmutabilidad:

chattr +i /etc/resolv.conf


Leer el resto del artículo »

29
Jan
2011

SetUID y SetGID en Linux

El esquema de permisos de UNIX permite definir qué usuarios pueden ejecutar un binario: el propietario, el grupo al que pertenece el propietario o el resto de los usuarios. Cada binario se ejecuta con los permisos que tenga asignados el usuario que lo está invocando. Ese esquema básico no es suficiente para realizar tareas normalmente requeridas por el usuarios. SetUID y SetGID son bits que le permiten a la aplicación ejecutarse con los permisos de su propietario. En caso de que el propietario sea root -caso general- la aplicación ejecuta con permisos administrativos por más que la invoque un usuario de permisos limitados. No cualquier usuario puede setearle estos bits a una aplicación, solamente su propietario/grupo y el usuario root.

¿Para qué se usa?

Supongamos que un usuario desea cambiar su contraseña. ¿Le daríamos permisos de modificar el archivo /etc/shadow? No. Lo que podríamos hacer es que la modificación de este archivo se haga a través de la aplicación passwd. Esta aplicación sí requiere permisos sobre el archivo. La diferencia es que la aplicación modifica el archivo con sus propias restricciones y no le brinda al usuario información que no debería saber.


Leer el resto del artículo »

26
Jan
2011

Alta disponibilidad en Red Hat Enterprise Linux

Imaginemos un servidor de datos críticos con requerimientos de alta disponibilidad y el riesgo identificado de que falle la tarjeta de red. Una medida, de bajo costo, para mitigar este riesgo sería instalarle dos tarjetas de red y en caso de que una falle, continuar operando automáticamente con la otra.

A nivel lógico esto es posible en Red Hat Enterprise Linux mediante las bonding interfaces. Se trata de un módulo del kernel que permite asignar a dos interfaces de red la misma dirección IP y utilizarlas como si fueran una sola. En situaciones normales, se distribuye la carga entre ambas. En la situación de que una falle, actúa la restante como contingencia.

Para configurarlas, agregamos en la carpeta /etc/sysconfig/network-scripts/ un archivo llamado ifcfg-bond<N>, siendo N un número como por ejemplo 0.

Ese archivo podría tener por ejemplo las siguientes líneas:

DEVICE=bond<N>
IPADDR=192.168.42.131
NETMASK=255.255.255.0
ONBOOT=yes
BOOTPROTO=none
USERCTL=no


Leer el resto del artículo »

21
Jan
2011

Levanta tu propio servidor DNS Recursivo

Normalmente en Uruguay los servidores de DNS Recursivos utilizados para transformar un FQDN (Fully Qualified Domain Name) en una dirección IPv4 son los de Antel (200.40.30.245 y 200.40.220.245). En ámbitos corporativos pueden utilizarse servidores DNS propios para aprovechar las ventajas de guardar nombres resueltos en memoria cache y minimizar el tráfico a Internet.

Levantar nuestro propio servidor de DNS Recursivo es fácil. Tenemos en este caso dos Linux: un servidor de DNS (192.168.56.100 o ubuntu-server) y una workstation desde las que haremos las consultas (192.168.56.101 o martin-VirtualBox). Luego de seguir los pasos mencionados en el artículo anterior, agregamos los siguientes parámetros al archivo de configuración /etc/bind/named.conf dentro del bloque options:

recursion yes;
allow-recursion { 192.168.56.101; };
allow-query { 192.168.56.101;  };
allow-query-cache { 192.168.56.101;  };
max-cache-size 256M;

Cambiamos los valores a nuestras IPs.


Leer el resto del artículo »