Descripción del proceso init

Titulo: INIT e Introducción a los RUNLEVELs o Niveles de Ejecucion en Linux. Fecha: Mayo 26 2007. Categoria: Software Libre/Linux

El presente documento ilustra y explica el funcionamiento de init, el proceso padre al cual el kernel da paso después de su correcta pre-inicializacion, también es una introducción 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 básicamente la misma distribucion jerarquica en cuanto al proceso que se ejecuta después 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 código están definidos en $KERNELSOURCE/init/main.c (donde $KERNELSOURCE es el arbol de las fuentes ), las líneas 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 básicamente 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 línea 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 así por el Linux Filesystem Hierarchy (LFH), un estandar de la distribucion de ejecutables, directorios, etc en un sistema Linux.


Stage Post INIT. editar

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 línea:

|-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 línea para que los usuarios puedan loguearse.

Runlevels o Niveles de Ejecucion.

Extractos de la Página del Manual. editar

Un runlevel es una configuración del sistema que permite sólo 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 envía señales apropiadas a init, indicándole a que runlevel cambiar.

Los runlevels 0, 1 y 6 están reservados. El nivel de ejecución 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.

Después de que init es ejecutado como el último paso de la secuencia de arranque del kernel, se realiza una busqueda del inittab en /etc para ver si encuentra una línea 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 líneas del inittab debemos tener en cuenta la siguiente sintaxis:

id:runlevels:acción:proceso

Por ejemplo: 1:2345:respawn:/sbin/agetty tty1 9600


En resumen el id aquí 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 = número de tty, y la velocidad (en baudios).


Conociendo ya básicamente 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 número de 0 a 6, ej: telinit 3, en este caso si estuviesemos en las X pasariamos autamaticamente al modo en línea de comandos ya que normalmente las X se ejecutan en el runlevel 5.


si::sysinit:/etc/rc.d/init.d/rc sysinit

Esta línea 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 líneas del script shell podrian ser las siguientes:

Entre otras cosas estas líneas 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 línea de comandos ) algo así como

============================ editar

Webdeveloper2 Linux2.6.21.1 tty1 i686 login:

============================ editar

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 así 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 básicamente 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 ningún 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 número del runlevel, la estructura de los dir es la siguiente, /etc/rc.d/rcX.d donde X es un número de 0 a 6, en el estilo que usa Debian es igual solo que se encuentran en /etc/rcX.d.

Quedando así 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 había 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 acción 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 número que prosiga a la letra donde los números 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 después ) 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 también 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 número menor ). Habiendo echo esto aseguramos que si reiniciamos o apagamos se detenga el servicio que se había 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 así

/etc/rc.d/init.d/bind start


Esto da la medida de como funcionan al nivel mas básico los scripts de runlevels sin ser el presente documento un manual de programación shell.

La línea 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, así 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


Espero que les haya sido de ayuda, buen dia a todos. "Knowledge is Power" orestesleal13022@cha.jovenclub.cu