Descripcion del proceso init

Titulo: INIT e Introduccion a los RUNLEVELs o Niveles de Ejecucion en Linux. . Categoria: Software Libre/Linux


El presente documento ilustra y explica el funcionamiento de init, el proceso padre al cual el kernel da paso despues de su correcta pre-inicializacion, tambien es una introduccion a los shells de runlevels

- Analisis del Stage Ejecutado Cuando el Nucleo 'finaliza' ejecutando el proceso INIT.

El proceso de arranque del nucleo Linux tiene basicamente la misma distribucion jerarquica en cuanto al proceso que se ejecuta despues del arranque e inicializacion del nucleo que los Unixes convencionales, lo que sera descrito a continuacion es una descripcion de los entresijos que ocurren durante esa etapa de inicializacion.
Desde su primera version y al ser un OS tipo Unix cuando el kernel 'termina' su ejecucion (realmente no lo hace, solo cede el control a un proceso padre [init] aunque continuara ejecutandose, planificando procesos, etc) hace una busqueda de un programa llamado init, mas adelante veremos que es y para que sirve init pero para ir dando un seguimiento correcto mostremos a continuacion lo que ejecuta el kernel para encontrar el ejecutable init.

Definiciones en C para buscar/encontrar init.
762 if (execute_command) {
763               run_init_process(execute_command);
764                printk(KERN_WARNING "Failed to execute %s.  Attempting "
765                                       "defaults...\n", execute_command);
766        }
767       run_init_process("/sbin/init");
768        run_init_process("/etc/init");
769       run_init_process("/bin/init");
770        run_init_process("/bin/sh");
771
772       panic("No init found.  Try passing init= option to kernel.");
773 }

----
  • Donde run_init_process se encuentra definida de esta manera en el mismo fichero.
727 static void run_init_process(char *init_filename)
728 {
729  argv_init[0] = init_filename;
730 kernel_execve(init_filename, argv_init, envp_init);
731 }
  • Donde argv_init se encuentra definida en el mismo fichero y donde se define que el argumento debe = "init".
184 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
Estos segmentos de codigo estan definidos en $KERNELSOURCE/init/main.c (donde $KERNELSOURCE es el arbol de las fuentes ), las lineas a analizar en cuestion son desde 767 -> 770, siendo todo esto basado en el nucleo 2.6.21.1, esas definiciones en C quieren decir basicamente que en este momento el nucleo esta haciendo una busqueda de los posibles lugares donde el proceso init se aloja haciendo llamadas a varias funciones, si la busqueda es efectiva ejecuta el proceso con permisos de super usuario (UID=0) de lo contrario si el kernel no puede encontrar a init el mensaje posterior en la linea 772 es bastante claro en ese sentido. A diferencia de algunas variantes De Unix o BSD Unix, en Linux, init se encuentra siempre en /sbin/init, definido asi por el Linux Filesystem Hierarchy (LFH), un estandar de la distribucion de ejecutables, directorios, etc en un sistema Linux.





Stage Post INIT.

Una vez que init es encontrado Linux le cede el control de inicializacion de procesos, los cuales seran descritos todos mas adelante.
Se puede visualizar el momento en el que init ha sido iniciado por el kernel ya que cuando esto ocurre se muestra un mensaje tipo "Startting Init 2.86" por poner un ejemplo.
Notese que el comando dmesg no muestra los mensajes pre-init sino los posteriores.
A partir de ese momento init toma control de la inicializacion de TODOS los procesos post-kernel, es por eso que init es considerado el padre de todos los procesos, podemos caer en cuenta de esto ejecutando el comando 'pstree', sin comillas y observando desde donde viene cada proceso desde el arbol jerarquico superior. Un fragmento de salida podria ser el siguiente.

init-+-abiword
     |-4*[agetty]
     |-bash---startx---xinit-+-X
     |                       `-fluxbox-+-gkrellm
     |                                 `-konsole-+-bash---mplayer
     |                                           |-bash---vim
     |                                           |-bash
     |                                           |-bash---man---less
     |                                           `-bash---pstree
Claramente se puede observar desde donde provienen cada uno de los procesos y los procesos ejecutados a traves de otros procesos por ejemplo en este caso la siguiente linea:
|-bash---startx---xinit-+-X
Esto quiere decir que desde el shell actual (bash) se ha ejecutado el proceso startx que a su vez inicia a xinit y finaliza ejecutando el proceso X que actualmente se encuentra en ejecucion y donde bash es un proceso directo de init como se puede observar.
Habiendo visto esto vamos a entrar en detalles del porque y como init funciona dando paso a todos estos procesos.
Init crea los procesos leyendo un fichero de configuracion que se puede decir que es global ya que siempre se encuentra en la misma ruta, el fichero /etc/inittab es leido e interpretado por init para la inicializacion de los procesos en los denominados run-leves o niveles de ejecucion. En inittab entre otras cosas se definen varias entradas que causan que init expanda gettys
getty, agetty comunmente usado en linux para abrir un puerto tty pidiendo login e invoca al ejecutable /bin/login. en cada linea para que los usuarios puedan loguearse.
Runlevels o Niveles de Ejecucion.

Extractos de la Pagina del Manual.

Un runlevel es una configuracion del sistema que permite solo a un grupo de procesos existir. Los procesos que ha sido expandidos por init para cada runlevel son definidos en /etc/inittab. Init puede estar en uno de 8 runlevels: 0 al 6 y S, s. El runlevel puede ser cambiado corriendo como usuario privilegiado y corriendo el comando telinit, el cual envia senales apropiadas a init, indicandole a que runlevele cambiar.
Los runlevels 0, 1 y 6 estan reservados. El nivel de ejecucion 0 es usado para detener el sistema, el 6 para reiniciarlo, y el 1 para el modo monousuario. El runlevel S no debe ser usado directamente solo para los scripts que son ejecutados cuando se esta accediendo al runlevel 1.
Despues de que init es ejecutado como el ultimo paso de la secuencia de arranque del kernel, se realiza una busqueda del inittab en /etc para ver si encuentra una linea del tipo initdefault, la cual establece cual es el runlevel por defecto o inicial del sistema. Si no existe la entrada o no se encuentra el inittab un runlevel debe ser establecido en la consola del sistema.

En algunas lineas del inittab debemos tener en cuenta la siguiente sintaxis:
id:runlevels:accion:proceso
Por ejemplo: 1:2345:respawn:/sbin/agetty tty1 9600

En resumen el id aqui es 1, el 2345 quiere decir que se inicializara en cualquiera de esos runlevels (recordar que init siempre inicia por defecto en algun runlevel), la palabra respawn quiere decir que el proceso sera reiniciado en el momento que sea terminado, /sbin/agetty en este caso es el proceso a ejecutar donde se le pasan 2 parametros requeridos, el ttyX, donde X = numero de tty, y la velocidad (en baudios).

Conociendo ya basicamente como funciona init damos paso a la descripcion del fichero inittab, un fichero de ejemplo de este file es el siguiente
id:3:initdefault:
si::sysinit:/etc/rc.d/init.d/rc sysinit
l0:0:wait:/etc/rc.d/init.d/rc 0 l1:S1:wait:/etc/rc.d/init.d/rc 1 l2:2:wait:/etc/rc.d/init.d/rc 2 l3:3:wait:/etc/rc.d/init.d/rc 3 l4:4:wait:/etc/rc.d/init.d/rc 4 l5:5:wait:/etc/rc.d/init.d/rc 5 l6:6:wait:/etc/rc.d/init.d/rc 6
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now

1:2345:respawn:/sbin/agetty tty1 9600 2:2345:respawn:/sbin/agetty tty2 9600 3:2345:respawn:/sbin/agetty tty3 9600 4:2345:respawn:/sbin/agetty tty4 9600 5:2345:respawn:/sbin/agetty tty5 9600 6:2345:respawn:/sbin/agetty tty6 9600 7:2345:respawn:/sbin/agetty tty7 9600 8:2345:respawn:/sbin/agetty tty8 9600 9:2345:respawn:/sbin/agetty tty9 9600

id:3:initdefault:
Como se mencionaba anteriormente la entrada initdefault establece el runlevel por defecto que init va a leer posteriormente en el inicio del sistema o con el comando telinit RUNLEVEL donde RUNLEVEL es un numero de 0 a 6, ej: telinit 3, en este caso si estuviesemos en las X pasariamos autamaticamente al modo en linea de comandos ya que normalmente las X se ejecutan en el runlevel 5.

si::sysinit:/etc/rc.d/init.d/rc sysinit
Esta linea es de suma importancia ( y varia de una distribucion a otra con toda seguridad ), es parte del mecanismo que se encarga de inicializar los scripts de arranque (S) y de apagado (K). Por ejemplo unas lineas del script shell podrian ser las siguientes:
Entre otras cosas estas lineas se encargan de inicializar lo antes mencionado, scripts de inicio y apagado en sus coincidencias S[numeros de 0 a 9] o K[lo mismo].
for i in $( ls -v ${rc_base}/rc${runlevel}.d/S* 2> /dev/null) do if [ "${previous}" != "N" ]; then suffix=${i#$rc_base/rc$runlevel.d/S[0-9][0-9]} stop=$rc_base/rc$runlevel.d/K[0-9][0-9]$suffix prev_start=$rc_base/rc$previous.d/S[0-9][0-9]$suffix
[ -f ${prev_start} ] && [ ! -f ${stop} ] && continue fi Un detalle a tener en cuenta es el parametro del final, 'sysinit', que causara que se ejecute antes que cualquiera de las definiciones boot o bootwait durante el arranque del sistema.

Estas definiciones albergan el mismo significado solo que 'wait' quiere decir que el proceso sera iniciado cuando el runlevel especificado sea iniciado e init esperara por su finalizacion.

l0:0:wait:/etc/rc.d/init.d/rc 0 l1:S1:wait:/etc/rc.d/init.d/rc 1 l2:2:wait:/etc/rc.d/init.d/rc 2 l3:3:wait:/etc/rc.d/init.d/rc 3 l4:4:wait:/etc/rc.d/init.d/rc 4 l5:5:wait:/etc/rc.d/init.d/rc 5 l6:6:wait:/etc/rc.d/init.d/rc 6

La siguiente definicion quiere decir que se hara cuando se presione la combinacion mas conocida del planeta (Ctrl, Alt, DEL), cabe decir que los runlevels 0 y 6 no se incluyen como es obvio pues ya estos runlevels de por si son apagado y reinicio respectivamente.
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now

Estas definiciones ya han sido explicadas por lo cual no debe haber duda.
1:2345:respawn:/sbin/agetty tty1 9600 2:2345:respawn:/sbin/agetty tty2 9600 3:2345:respawn:/sbin/agetty tty3 9600 4:2345:respawn:/sbin/agetty tty4 9600 5:2345:respawn:/sbin/agetty tty5 9600 6:2345:respawn:/sbin/agetty tty6 9600 7:2345:respawn:/sbin/agetty tty7 9600 8:2345:respawn:/sbin/agetty tty8 9600 9:2345:respawn:/sbin/agetty tty9 9600

Normalmente los inittab convencionales son algo elaborados aunque un inittab podria ser lo siguiente y funcionar adecuadamente.
id:1:initdefault:
             rc::bootwait:/etc/rc
             1:1:respawn:/etc/getty 9600 tty1
             2:1:respawn:/etc/getty 9600 tty2
             3:1:respawn:/etc/getty 9600 tty3
             4:1:respawn:/etc/getty 9600 tty4
Donde bootwait define que sera ejecutado al arranque del sistema, mientras que init espera a que termine, el campo de los runlevels sera ignorado.
En la parte final de toda esta ejecucion init lee las entradas de aggety o getty y es cuando se nos presenta el prompt de login que todos conocemos (en linea de comandos ) algo asi como
============================
Webdeveloper2 Linux2.6.21.1 tty1 i686 login:
============================
Funcionamiento y distribucion de los scripts de arranque.
Con las bases de todo lo mencionado en el tema anterior veamos como, porque y cuando seran ejecutados scripts de arranque mas comunmente llamados shells init o scripts de runlevels.
Para comenzar hay que mencionar el directorio que contiene CASI TODOS los scripts de inicio los cuales seran iniciados en el runlevel al que pertenezcan es el dir /etc/init.d, comunmente usado en distribuciones como Debian no siendo asi en Red Hat que usa el estilo BSD muy parecido al estilo del Debian pero con otra ubicacion en los ficheros shell y alguna que otra diferencia aunque en el fondo se logra basicamente el mismo objetivo.
Se mencionaba CASI TODOS pues hay scripts que son 'fijos', esos shells son los que se encargan de montar el sistema de ficheros, chequearlo, iniciar Udev, Limpiar el FS, establecer la consola de linux, activar la swap, entre otras, por ejemplo en una distribucion GNU/Linux con estilo de arranque System V al estilo BSD el directorio /etc/init.d no existe propiamente en ese lugar sino que se encuentra en /etc/rc.d/init por lo que los scripts fijos de arranque se encuentran normalmente en el directorio /etc/rc.d/init.d/rcsysinit y los leidos por init antes que ningun otro, es necesario explicar esto para que se comprenda en caso de confusion al tener contacto con alguna de las dos variantes.
Volviendo a los scripts de runlevel NORMALES estos se alojan en /etc/rc.d/init.d o en /etc/init.d habiendo enlaces simbolicos desde cada directorio que es identificado por el numero del runlevel, la estructura de los dir es la siguiente, /etc/rc.d/rcX.d donde X es un numero de 0 a 6, en el estilo que usa Debian es igual solo que se encuentran en /etc/rcX.d.
Quedando asi mas o menos la estructura:
En los directorios /etc/ o /etc/rc.d
- rc0.d
     - rc1.d
     - rc2.d
     - rc3.d
     - rc4.d
     - rc5.d
     - rc6.d

Como se habia mencionado antes los scripts se encuentran en init.d, ejemplificado vamos a ver un script REAL alojando en init.d
/etc/rc.d/init.d/bind
Donde bind es un shell script de runlevel.
De por si bind alojado en init.d no sera ejecutado por defecto sino que tiene que ser enlazado a algun runlevel existente ( rc0.d, rc1.d, etc ), para enlazarlo es necesario conocer que prioridades y accion tienen los enlaces sinbolicos. Digamos que vamos a enlazar a bind al runlevel3 para esto hacemos

Ejemplo de Enlazar un script a un runlevel para que sea iniciado en el arranque del sistema:
cd /etc/rc.d/rc3.d ln -sv ../init.d/bind S92bind
Esto quiere decir que estamos creando el enlace simbolico S92bind que apunta al script shell ../init.d/bind
La norma o sintaxis es muy facil de comprender, los enlaces que comienzen con S (de START) seran iniciados dependiendo del numero que prosiga a la letra donde los numeros mas bajos tienen mayor prioridad en la ejecucion. Todo esto quiere decir que cuando init entre en el runlevel 3 ( en el arran que del sistema o poco despues ) y ejecute todos los enlazes que comienzen con S llegara el punto en que ejecutara el script del ejemplo anterior.
Ejemplo de Enlazar un script a un runlevel para que sea detenido.
Como es logico, los servicios que son iniciados tambien deben ser detenidos de manera automatica cuando el sistema se vaya a reiniciar o apagar para esto en este ejemplo vamos a enlazar a bind en los runlevels 0 y 6 para que se detenga en un reinicio o apagado.
cd /etc/rc.d/rc6.d ln -sv ../init.d/bind K12bind cd /etc/rc.d/rc0.d ln -sv ../init.d/bind K12bind
Aqui la norma es la misma aunque con una diferencia los que comienzen con K (KILL) seran detenidos de igual manera que lo antes explicado ( la prioridad la tiene el numero menor ). Habiendo echo esto aseguramos que si reiniciamos o apagamos se detenga el servicio que se habia iniciado en el arranque del sistema.

A continuacion un ejemplo del script bind algunos detalles sobre que sucede cuando es S y K y que tiene que ver con el script.
  1. !/bin/sh
  2. script bind.
  3. dns Bind9 {named}

case "$1" in start) echo "Iniciando Servidor DNS..." /usr/sbin/named -u named -t /srv/named -c /etc/named.conf ;;
stop) echo "Deteniendo el Servidor DNS..." killall /usr/sbin/named ;;
restart) $0 stop sleep 1 $0 start ;;
reload) echo "Recargando el servidor DNS..." /usr/sbin/rndc -c /etc/rndc.conf reload ;;
*) echo "Uso: $0 {start|stop|restart|reload}" exit 1 ;; esac

Esta es la parte interesante, cuando un enlace comienza con K, init sabe que tiene que matar ese proceso por lo cual envia al script bind un paramtro de la siguiente manera
/etc/rc.d/init.d/bind stop
Por el contrario si comenzara con S seria asi
/etc/rc.d/init.d/bind start

Esto da la medida de como funcionan al nivel mas basico los scripts de runlevels sin ser el presente documento un manual de programacion shell.
La linea case "$1 in" es propia del interprete de comandos (bash, sh, tcsh, ksh, etc) lo que quiere decir es "en caso de darse el primer parametro y que sea igual a los admitidos (start, stop, ect)"
Los parametros admitidos en este script son start, stop, restart, reload, ( el parametro start es obligatorio al igual que stop si deseamos tener un script que sea un autentico shell de runlevel, el parametro start es enviado al script al arranque del sistema, asi como el stop al apagado o reinicio y los otros dos son muy usados para recargar la configuracion del servicio y reiniciar el servicio lo cual no se aplica en el inicio, apagado o reinicio por cuestiones logicas.
Referencias Importantes:
  • man init
  • man inittab
  • man getty
  • man login
  • man bash

No hay comentarios :

Publicar un comentario

DEJA TU COMENTARIOS CON TUS DUDAS Y SUGERENCIAS,ASI COMO TAMBIEN UN PEDIDO EN PARTICULAR.
TAMBIEN PUEDES TU CORREO ELECTRONICO PARA UNA RESPUESTA MAS RAPIDA.