Esta página puede ser redistribuida libremente bajo los términos de la licencia GPL. Vease ( GPL texto original ) o si lo prefiere (Traducción española no oficial de la GPL) Al margen de las obligaciones legales que se derivan del uso de esta licencia rogamos sea respetada la referencia a su lugar de publicación original www.ciberdroide.com. y a su autor original Antonio Castro Snurmacher (Madrid 01/01/2000).

Ausencia de Garantía

Esta ausencia de garantía se hace extensa a cualquier tipo de uso de este material y muy especialmente a las prácticas, ejercicios, y de ejemplos que encuentre en estas páginas. Deberá trabajar siempre salvo indicación contraria con un SO Linux y con un usario distinto de 'root' sin privilegios especiales. Como directorio de trabajo se procurará usar el directorio '/tmp' o algún otro que no contenga información valiosa. Tampoco se considera buena idea practicar en una máquina que contenga información valiosa.

Todo esto son recomendaciones de prudencia. En cualquier caso si algo sale mal toda la responsabilidad será únicamente suya. En ningún caso podrá reclamar a nadie por daños y perjuicios derivados del uso de este material. Para más información lea el contenido de la licencia GPL y abstengase de hacer prácticas si no está dispuesto a asumir toda la responsabilidad.

...
..

MAS SOBRE PROCESOS Y SEÑALES

Las formas de comunicación entre procesos
Los procesos no tiene una facilidad de acceso indiscriminada a otros procesos. El hecho de que un proceso pueda influir de alguna manera en otro es algo que tiene que estar perfectamente controlado por motivos de seguridad. Comentaremos solo muy por encima las diferentes formas de comunicación entre procesos.

  1. A través de variables de entorno:
    Solo es posible de padres a hijos.

  2. Mediante una señal:
    Solo indica que algo ha ocurrido y solo lleva como información de un número de señal.

  3. Mediante entrada salida:
    Es la forma más corriente a nivel de shell. Ya hemos comentado el operador pipe '|' que conecta dos procesos.

  4. Mediante tégnicas IPC u otras:
    Semáforos, Memoria compartida, Colas de mensages.

  5. Mediante sockets:
    Este sistema tiene la peculiaridad de que permite comunicar procesos que estén funcionando en máquinas distintas.
No profundizamos sobre esto porque ahora no estamos interesados en la programación. Más adelente si comentaremos bastante sobre variables y entrada salida porque daremos nociones de programación en shell-script. Tambien daremos a continuación unos pequeños detalles que tienen que ver con el arranque del sistema porque también nos resultará util para comprender como funcionan las cosas.

Secuencia de arranque en una sesión de consola
Para consultar la dependencia jerárquica entre unos procesos y otros existe en Linux el utilísimo comando pstree. No es esencial y quizas no lo tenga usted instalado pero resulta muy práctico. Si dispone de él pruebe los comandos 'pstree', y 'pstree -p'. Nosotros vamos a mostrar el resultado de ambos comandos en el sistema que estamos usando en este momento para que en cualquier caso pueda apreciar el resultado pero le recomendamos que lo instale si no dispone de él ya que resulta muy práctico. Tambien puede usar como sustituto de 'pstree' el comando 'ps axf' pero no espere un resultado tan bonito.

$ pstree

init-+-apache---7*[apache]
     |-atd
     |-bash---pstree
     |-2*[bash]
     |-bash---vi
     |-bash---xinit-+-XF86_S3V
     |              `-mwm-+-.xinitrc---xterm---bash
     |                    `-.xinitrc---xclock
     |-cron
     |-getty
     |-gpm
     |-inetd
     |-kflushd
     |-klogd
     |-kpiod
     |-kswapd
     |-kupdate
     |-lpd
     |-portmap
     |-postmaster
     |-sendmail
     |-sshd
     |-syslogd
     `-xfs

Este formato nos da un arbol de procesos abreviado en el que procesos con el mismo nombre y que tengan un mismo padre aparecerán de forma abreviada. En el ejemplo anterior aparece '2*[bash]' indicando que hay dos procesos bash como hijos de init, y tambien hay algunos proceso apache arrancados. El que existan muchos procesos arrancados no indica necesariamente un alto consumo de CPU. Puede que estén todos ellos haciendo el vago. Bueno en el caso de apache quizas estén haciendo el indio. (Esto último es una broma que no he podido evitar).

$ pstree -p

init(1)-+-apache(204)-+-apache(216)
        |             |-apache(217)
        |             |-apache(218)
        |             |-apache(219)
        |             |-apache(220)
        |             |-apache(1680)
        |             `-apache(1682)
        |-atd(196)
        |-bash(210)---pstree(1779)
        |-bash(211)
        |-bash(212)---vi(1695)
        |-bash(215)---xinit(1639)-+-XF86_S3V(1644)
        |                         `-mwm(1647)-+-.xinitrc(1652)---xterm(1660)---bash(1661)
        |                                     `-.xinitrc(1655)---xclock(1673)
        |-bash(214)
        |-cron(199)
        |-getty(213)
        |-gpm(143)
        |-inetd(138)
        |-kflushd(2)
        |-klogd(131)
        |-kpiod(4)
        |-kswapd(5)
        |-kupdate(3)
        |-lpd(153)
        |-portmap(136)
        |-postmaster(168)
        |-sendmail(179)
        |-sshd(183)
        |-syslogd(129)
        `-xfs(186)

En este otro formato. Aparece cada proceso con su PID. Podemos ver que el Proceso 'init' tiene pid = 1 y ha realizado varios forks() generando procesos con pid > 1. En algunos sistemas la generación de números de PID para procesos nuevos se realiza en secuencia. En otros resulta un número impredecible.

Entre los procesos generados por 'init' están los procesos 'getty'. Se arrancará un 'getty' por cada terminal. Este proceso configura la velocidad y otras cosas del terminal, manda un saludo y luego se transforma con exec el proceso 'login'. Todos estos procesos se ejecutan con EUID y UID = 0, es decir como procesos del superusuario root. Cuando el proceso 'login' conoce nuestra identidad después de validar usuario password se transformará con exec en la shell especificada e para nuestro usuario el fichero /etc/passwd

Para ver la linea que contiene sus datos pruebe a hacer lo siguiente:

$ grep `whoami` /etc/passwd

La linea tiene el siguiente formato. login:contraseña:UID:GID:nombre:dir:intérprete

Vamos a suponer que su shell por defecto sea la bash. Si esta shell arrancara con el EUID = 0 tendríamos todos los privilegios del super usuario pero esto no ocurre así. Esta shell ya tendrá nuestro UID y nuestro EUID. Vamos a representar todo esto marcando los puntos en los que ocurre algún fork() con un signo '+'.

[init]-+fork()->[getty]
       |
       +fork()->[getty]-exec()->[login]-exec()->[bash]+fork()-exec()->[comando]
       |
       +fork()->[getty]
       |

La shell puede arrancar un comando mediante un fork() y luego un exec() y esperar a que este muera. Recuerde que la función exec() no tiene retorno posible ya que finaliza con la muerte del proceso. En ese momento la shell detecta la muerte de su hijo y continua su ejecución solicitando la entrada de un nuevo comando. Cuando introducimos el comando 'exit' estamos indicando a la shell que finalice y su padre 'init' se encargará de lanzar nuevo proceso 'getty'. Lógicamente 'exit' es un comando interno de la shell. Quizas le llame la atención que la muerte de 'bash' termine provocando un nuevo 'getty' cuando 'getty' pasó a 'login' y este a 'bash' pero en esta secuencia getty-login-bash no hay ningún fork() por eso getty, login, y bash son en realidad el mismo proceso en distintos momentos con el mismo PID obtenido en el fork() realizado por 'init' solo que ha ido cambiando su personalidad manteniendo la misma identidad (mismo PID). Para 'init' siempre se trató del mismo hijo y la muerte de cualquiera de ellos (getty, login o bash) provoca que se arranque un nuevo 'getty' sobre ese mismo terminal con el fin de que ese terminal no quede sin servicio.

La presentación del mensaje de Login es mostrada por 'getty'. Una vez introducido el identificador de usuario será 'login' quien muestre la solicitud de introducción de la password, y una vez introducido el password será la shell quien muestre el introductor de comandos pero estamos hablando siempre del mismo proceso.

A modo de ejercicio compruebe estas cosas por usted mismo usando algunos de los comandos que ya conoce. Para hacer esta práctica no basta con usar un terminal remoto sino que necesitará un PC completo para ir haciendo cosas desde distintas sesiones. Le proponemos hacerlo más o menos de la siguiente manera:

  1. Entre en cada uno de los terminales disponibles de forma que todos los terminales esten ocupados por un interprete de comandos. Bastará con hacer login en todos ellos.
  2. Luego compruebe que no hay ningún proceso 'getty'.
  3. Haga un exit desde uno de estos terminales.
  4. Compruebe desde otro termina que ahora si existe un proceso 'getty' y anote su pid.
  5. Introduzca el nombre de usuario en ese terminal que quedó libre.
  6. Compruebe ahora desde otra sesion que existe un proceso login con el PID que usted anotó.
  7. Termine de indentificarse tecleando la password
  8. Compruebe desde otra sesión que ahora existe una shell con el PID que anotamos.

    Si no tiene el comando 'pstree' tendrá que usar 'ps' pero con pstree puede ver más facilmente lo que ocurrirá ahora.

  9. Ahora teclee el comando 'sleep 222' desde la sesión que tiene el PID anotado por usted.
  10. Compruebe desde otra sesión que el interprete de comandos ha realizado un fork() generando un comando que está ejecutando el comando indicado.

Si no ha podido realizar el ejercicio anterior tendrá que confiar en que las cosas son como decimos y ya está.

Comando ps
Muestra los procesos activos. Este comando es muy util para saber que comandos están funcionando en un determinado momento.

Siempre que se mencione un comando puede consultar la página man del mismo. En el caso de 'ps' se lo recomendamos ya que es un comando muy util con una gran cantidad de opciones. Nostros mencionaremos algunos ejemplos pero se aconseja probar 'ps' probando las distintas opciones que se mencionan en la página del manual.

Ejemplos:

# Para ver todos sus procesos que están 
# asociados a algún terminal.
$ ps 
 
PID TTY STAT TIME COMMAND
.......


# Para ver todos sus procesos y los de otros 
# usuarios siempre asociados a algún terminal.
$ ps a
 
PID TTY STAT TIME COMMAND
.......


# Para ver todos sus procesos estén asociados o 
# no a algún terminal.
$ ps x
 
PID TTY STAT TIME COMMAND
.......


# Para ver todos los proceso asociados al 
# terminal 1
$ ps t1
 
PID TTY STAT TIME COMMAND
.......


# Para ver todos los procesos del sistema.
$ ps ax
 
PID TTY STAT TIME COMMAND
.......


Estos ejemplos que acabamos de ver obtienen un mismo formato de datos. Explicaremos el significado de estos atributos.

PID Es el valor númerico que idenfica al proceso.
TTY Es el terminal asociado a ese proceso. Los demonios del sistema no tienen ningún terminal asociado y en este campo figurará un ?
STAT Tiene tres campos que indican el estado del proceso (R,S,D,T,Z) (W) (N) La S indica que el proceso está suspendido esperando la liberación de un recurso (CPU, Entrada Salida, etc) necesario para continuar. Explicaremos solo algunos de estos estados en su momento.
TIME Indica el tiempo de CPU que lleva consumido ese proceso desde que fué arrancado.
COMMAND Muestra el comando y los argumentos que le fueron comunicados.

Existen muchas opciones para el comando ps que ofrecen un formato distinto. Le recomendamos especialmente que pruebe 'ps u', 'ps l', y 'ps f'

En Unix los comandos suelen servir para una sola cosa, aunque suelen tener muchas opciones. La entrada de los comandos suele tener una estructura simple y la salida de los comandos tambíen. Si un comando no encuentra nada que hacer existe la costumbre de que termine de modo silencioso. Todo esto permite que los comandos puedan combinarse enganchado la salida de uno con la entrada de otro. Algunos comandos están especialmente diseñados para ser usados de esta forma y se les suele denominar filtros.

La salida del comando 'ps' se puede filtrar con 'grep' para que muestre solo las líneas que nos interesan.

Configuración del terminal
Conviene que comprobemos si su terminal está correctamente configurado para poder interrumpir un proceso. Normalmente se usa <Ctrl-C> pero esto depende de la configuración de su terminal. Si en algún momento su terminal queda desconfigurado haciendo cosas raras como por ejemplo mostrar caracteres extraños intente recuperar la situación tecleando el comando 'reset'. Esto solo es válido para Linux. Para otros sistemas puede ser util 'stty sane' que también funciona en Linux pero no es tan eficaz como el comando 'reset'. Para comprobar la configuración de su terminal puede hacer 'stty -a' aunque obtendrá demasiada información que no es capaz de interpretar, podemos indicarle que se fije en el valor de 'intr'. Debería venir como 'intr = ^C'. Si no lo localiza haga 'stty -a | grep intr'. De esta forma solo obtendrá una linea. Para configurar el terminal de forma que pueda interrumpir procesos con <Ctrl-C> puede intentar configurar su terminal haciendo 'stty ^V^C'. El carácter <Ctrl-V> no se mostrará en el terminal ya que actua evitando que el siguiente carater (<Ctrl-C> en nuestro caso) no sea interpretado como caracter de control.

No pretendemos ahora exiplicar los términales de Linux pero si queremos que compruebe su capacidad para interrumpir procesos con <Ctrl-C> ya que usaremos esto en las prácticas que siguen. Una prueba inofensiva para comprobar la interrupcion de un proceso es el siguiente comando que provoca una espera de un minuto. Deberá introducir el comando e interrumpirlo antes de que el tiempo establecido (60 segundos se agote).

$ sleep 60
<Ctrl-C>

Si no ha conseguido interrumpir el proceso no siga adelante para evitar que alguna de las prácticas deje un proceso demasiado tiempo consumiendo recursos de su máquina. Si esta usted solo en la máquina eso tampoco tendría mucha importancia pero es mejor que averigue la forma de interrumpir el proceso del ejemplo anterior.

Comando time
Da los tiempos de ejecucion. Este comando nos da tres valores cuya interpretacion es:

        real	Tiempo real gastado (Duración real)
	user	Tiempo CPU de usuario.
	sys.	Tiempo CPU consumido como proceso de kernel. 
		(Es decir dentro de las llamadas al kernel)

La mayoría de los comandos están gran parte del tiempo sin consumir CPU porque necesitan esperar para hacer entrada salida sobre dispositivos lentos que además pueden estar en uso compartidos por otros procesos. Existe un comando capaz de esperar tiempo sin gastar tiempo de CPU. Se trata del comando 'sleep'. Para usarlo le pasaremos un argumento que indique el número de segundos de dicha espera.

Por ejemplo vamos a comprobar cuanta CPU consume una espera de 6 segundos usando sleep

$ time sleep 6 

real    0m6.021s
user    0m0.020s
sys     0m0.000s

El resultado obtenido puede variar ligeramente en cada sistema pero básicamente obtendrá un tiempo 'real' de unos 6 segundos y un tiempo de CPU ( 'user' + 'sys' ) muy pequeño.

Vamos a medir tiempos en un comando que realice operaciones de entrada salida así como proceso de datos.

$ time ls /* > /dev/null


real    0m0.099s
user    0m0.080s
sys     0m0.010s

En este comando verá que el consumo total de CPU es superior al del comando sleep. En cualquier caso el tiempo real tardado en la ejecución del comando es siempre muy superior al consumo de CPU.

Vamos a probar un comando que apenas realice otra cosa que entrada salida. Vamos a enviar 10Mbytes al dispositivo /dev/null. Existe un comando 'yes' que provoca la salida continua de un caracter 'y' seguido de un caracter retorno de carro. Esta pensado para sustituir la entrada de un comando interactivo en el cual queremos contestar afirmativamente a todo lo que pregunte. Nosotros fitraremos la salida de 'yes' con el comando 'head' para obtener solo los 10Mbytes primeros producidos por 'yes' y los enviaremos al dispositivo nulo '/dev/null' que viene a ser un pozo sin fondo en el cual podemos introducir cualquier cosa sin que se llene, pero no podremos sacar absolutamente nada. En una palabra vamos a provocar proceso de entrada salida perfectamente inutil.

time yes | head --bytes=10000000 > /dev/null

Tubería rota

real    0m6.478s
user    0m5.440s
sys     0m0.900s

Podemos hacer un consumo fuerte de CPU si forzamos a cálculos masivos que no tengan apenas entrada salida. Por ejemplo podemos poner a calcular el número PI con 300 cifras decimales. 'bc' es un comando que consiste en una calculadora. Admite uso interactivo pero tambien acepta que le pasemos las operaciones desde otro proceso combinando entrada salida.

time ( echo "scale=300; 4*a(1)" | bc -l )

3.141592653589793238462643383279502884197169399375105820974944592307\
81640628620899862803482534211706798214808651328230664709384460955058\
22317253594081284811174502841027019385211055596446229489549303819644\
28810975665933446128475648233786783165271201909145648566923460348610\
454326648213393607260249141272

real    0m2.528s
user    0m2.520s
sys     0m0.010s

En un Pentium 200Mhz este comando tardó 3 segundos para 300 cifras y 20 segundos usando 600 cifras. Decimos esto para que vea que el tiempo que se tarda aumenta exponencialmente y que dependiendo de la potencia de su ordenador puede suponer bastante tiempo de proceso. Quizas tenga que variar el número de cifras significativas para poder medir tiempos con comodidad.

Este comando 'time' es un comando interno pero en Linux también hay un comando externo llamado de la misma forma. Para poder ejecutarlo con esta shell debería incluir el camino completo. No deseamos abusar de su escaso 'time' así que no lo comentaremos. Para buscar en el man el comando 'time' que hemos explicado o de cualquier otro comando interno tendría que mirar en la página del manual de bash.

Comando kill
Este comando se utiliza para matar procesos. En realidad envia señales a otros procesos, pero la acción por defecto asociada a la mayoria de las señales de unix es la de finalizar el proceso. La finalización de un proceso puede venir acompañada del volcado de la información del proceso en disco. Se genera un fichero 'core' en el directorio actual que solo sirve para que los programadores localicen el fallo que provocó esta prusca finalización del proceso. Por ejemplo si el proceso intenta acceder fuera del espacio de memoria concedido por el kernel, recibirá una señal que lo matará. Lo mismo ocurrirá si se produce una división por cero o algún otro tipo de error irrecuperable.

Un proceso unix puede capturar cualquier señal excepto la señal 9. Una vez capturada la señal se puede activar una rutina que puede programarse con toda libertad para realizar cualquier cosa.

'kill' por defecto es 'kill -15' envia un SIGTERM y generalmente provoca cierre ordenado de los recursos en uso. Esta señal puede ser ignorada, o puede ser utilizada como un aviso para terminar ordenadamente. Para matar un proceso resulta recomendable enviar primero un kill -15 y si no se consigue nada repetir con kill -9. Este último -9 envia SIGKILL que no puede ser ignorada, y termina inmediatamente. Solo fallara si no tenemos permisos para matar ese proceso, pero si es un proceso nuestro, kill -9 resulta una opción segura para finalizar un proceso.

Las señales actuan frecuentemente como avisos de que ha ocurrido algo. Existen muchos tipos de señales para poder distinguir entre distintas categorías de incidencias posibles.

Comando nice
El multiproceso esta implementado concediendo ciclicamente la CPU en rodajas de tiempo a cada proceso que esta en ejecución.

Existen dos numeros de prioridad. La prioridad NICE y la prioridad concedida por el Kernel mediante un algoritmo. Esta última no tiene porque coincidir con nice y puede valer mas de 39. En cambio el comando nice solo acepta valores comprendidos entre 0 y 39, siendo 20 el valor por defecto. Cuando nice sube el valor significa que el proceso tiene baja prioridad. El comando 'nice -10' incrementara el valor nice en 10 (es decir baja la prioridad). Para bajar el valor de nice (Es decir para subir la prioridad) hace falta permisos de superusuario.

En un sistema con poca carga de trabajo no se notará apenas diferencia al ejecutar un comando con baja prioridad o con alta prioridad. Pero en un sistema con la CPU sobrecargada los comandos ejecutados con prioridad más baja se veran retrasados. ya que el kernel concederá más tiempo de CPU a los procesos con prioridad más alta.

Hay otros comandos de interés. Por ejemplo 'top' muestra los procesos que mas CPU estén consumiendo. 'vmstat' saca información del consumo de memoria virtual.

Hay que tener en cuenta que el sistema gasta recursos en la gestión de los procesos. Por ejemplo si estamos compartiendo una máquina con otros usuarios y tenemos que realizar 15 compilaciones importantes terminaremos antes haciendolas en secuencia una detras de otra que lanzandolas todas a la vez. La avaricia de querer usar toda la CPU posible para nosotros puede conducir a una situación en la cual ni nosotros ni nadie sacará gran provecho de la CPU. El sistema realizará una cantidad enorme de trabajo improductivo destinado a mantener simultanemente funcionando una gran cantidad de procesos que gastan mucha CPU y mucha memoria. La máquina comienza a usar el disco duro para suplir la falta de RAM y comienza a gastar casi todo el tiempo en el intercambio de la RAM con el disco duro. A esta situación se la denomina swaping.

Comando renice
Sirve para cambiar la prioridad de un proceso. Sigue la misma filosofia que el comando nice pero hay que identificar el o los procesos que deseamos cambiar su prioridad. Se puede cambiar la prioridad de un proceso concreto dado su PID o los procesos de un usuario dando su UID o todos los procesos pertenecientes a un determinado grupo. dando su GID. Tanto el comando nice como el comando 'renice' tienen mayor interés para un administrador de sistemas que para un usuario normal. Consulte las páginas del manual para más detalles.

Test
Puede comprobar sus conocimientos respondiendo el siguiente test.
Para ello seleccione las opciones que se ajusten a la verdad y luego pulse el boton para ver el resultado de su test.

1 'init' es un proceso que muere despues de inicializar el sistema.
2 Un hijo puede comunicarse con su padre a traves de variables de entorno.
3 En el arranque de una sesión 'getty' es padre de 'login' y 'login' será padre de la 'shell'.
4 El comando 'sleep 60' gastará un minuto de tiempo de CPU.
5 Para matar un proceso conviene hacer 'kill -9' y si no es suficiente se hace luego un 'kill -15'
6 El comando 'nice -10' decrementa el valor de NICE.

Resultado del test

Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos
contacto

.
....
...

Volver a la página anterior
Volver a la página principal de la tienda