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). |
![]() |
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.
|
Quizas para un curso de programación avanzada si fuera necesario, pero lo que nosotros vamos a describir es unicamente los detalles más importantes desde un punto de vista didáctico y práctico para un usuario normal.
En un sistema multitarea todo funciona con procesos así que conocer unos cuantos principios básicos sobre procesos le resultará de la máxima utilidad. En un sistema monotarea se usa frecuentemente el término programa indistintamente para hablar de un programa en papel, en cdrom, en disco duro o en funcionamiento. En un sistema multitarea no se debe usar el término programa para hablar de la ejecución del mismo. En su lugar hablaremos de proceso indicando con ello que esta arrancado y funcionando. Un programa puede dar lugar a varios procesos. Por ejemplo en un mismo instante varios usuarios pueden estar usando un mismo editor. Un proceso puede estar detenido pero a diferencia de un programa existe una informacíon de estado asociada al proceso. Un programa es algo totalmente muerto. Un proceso detenido es más bien como si estuviera dormido.
Los procesos tienen organizada la memoria de una forma especial muy eficiente. Por ejemplo la párte de código del proceso es una parte de solo lectura y puede ser compartida por varios procesos a la vez. Imaginemos que hay varios usuarios en el sistema usando un mismo editor. En ese caso sería un desperdicio tenener la mísma información de código de ese programa repetida varias veces en memoria y ocupando un recurso tan valioso como es la memoria RAM.
En el caso de tener varios programas distintos en ejecución también se suele dar el caso de que contengan pártes comunes de código pertenecientes a librerías que continen gran cantidad de funciones de propósito general. Para hacer un uso eficiente de estas librerías existen librerías dinámicas de uso compartido. En Linux el uso de estas librerías no está organizado de una forma unificada para todas las distribuciones por lo cual los ejecutables binarios pueden ser incompatibles entre distintas distribuciones. Este problema se puede solucionar partiendo de los fuentes y recompilando la aplicación en nuestro sistema. Por esta razón un binario de RedHat, o Suse puede no funcionar en Slackware o en Debian.
PID y PPID
El proceso init
Es decir 'init' es un proceso que va a generar nuevos procesos pero esta no
es una cualidad especial. Es muy frecuente que un proceso cualquiera genere
nuevos procesos y cuando esto ocurre se dice que genera procesos hijos.
Este no es un curso de administración pero diremos que a init se le
puede indicar que arranque el sistema de diferentes formas, por ejemplo
en modo monousuario para mantenimiento. Este es un capítulo en el cual
pueden surgir muchas preguntas retorcidas tales como, que pasa si matamos
a init, o quien es el padre de init, pero no nos interesa responder a
esto ya que init es un proceso muy especial y su padre aún más. En cada
sistema de tipo Unix las respuestas a cosas como estas pueden variar
mucho porque a ese nivel la implementaciones varían mucho. Ningún programa
normal necesitará usar ese tipo de información. Quedan muchos detalles
interesantes relativos a temas de administración. Los curiosos siempre
tienen el recurso de mirar la página man de init(8) y de inittab(5) pero
nosotros no insistiremos más en este tema. (Que alivio verdad ?)
UID y EUID
El UID es uno de los atributos de un proceso que indica por
decirlo de alguna manera quien es el propietario actual de ese
proceso y en función
de ello podrá hacer ciertas cosas. Por ejemplo si un usuario normal
intentara eliminar un proceso del cual no es propietario el sistema
no lo permitirá mostrando un mensaje de error en el que advierta que
usted no es el propietario de ese proceso y por tanto no está autorizado
a hacer esa operación. Por el contrario el usuario root puede
hacer lo que quiera con cualquier proceso ya que el sistema no comprueba
jamás si root tiene permisos o no para hacer algo. root siempre tiene
permisos para todo. Esto es cierto a nivel de llamadas del sistema
pero nada impide implementar un comando que haga comprobaciones de
permisos incluso con root. Algunos comandos en Linux tienen opciones
que permiten hacer estas cosas y solicitar confirmación en caso de
detectar una operación peligrosa o infrecuente.
El UID también es un atributo
presente en otros elementos del sistema. Por ejemplo los ficheros y
directorios del sistema tienen este atributo. De esta forma cuando un
proceso intenta efectuar una operación sobre un fichero. El kernel
comprobará si el EUID del proceso coincide con el UID del fichero.
Por ejemplo si se establece que determinado fichero solo puede ser
leido por su propietario el kernel denegará todo intento de lectura
a un proceso que no tenga un EUID igual al UID del fichero salvo que
se trate del todo poderoso root.
Aunque estamos adelantando cosas sobre el sistema de ficheros vamos
a continuar con un ejemplo. Comprobaremos cual es el UID de su
directorio home.
Si su directorio home está configurado de forma lógica deberá pertenecerle
a usted. Si esto no es así reclame enérgicamente a su administrador, pero
si el administrador resulta ser usted sea más indulgente y limitese a
corregirlo y no confiese su error a nadie. En realidad casi todo lo que
que se encuentre dentro de su directorio home debería pertenecerle a usted.
Usted debe ser el propietario de su directorio home porque de otra
forma y dependiendo de los permisos asociados a este directorio los
procesos que usted arranque se verían o bien incapaces de trabajar con
él, o lo que
es peor cualquiera podría hacer cualquier cosa con él. Como es lógico
hemos mencionado de pasada el tema de permisos de directorios para ilustrar
un poco la utilidad del uid pero esto se verá en detalle en el capítulo
dedicado al sistema de ficheros de Linux.
Algunos ficheros ejecutables poseen un bit de permisos que hace que
cambie el EUID del proceso que lo ejecute convirtiendose en el UID del
propietario del fichero ejecutado. Suena complicado pero no lo es.
Es decir imaginemos que usted ejecuta un comando propiedad de root que tiene
este bit. Pues bien en ese momento el EUID de su proceso pasaría a ser el de
root. Gracias a esto
un proceso puede tomar temporalmente la identidad de otro usuario. Por ejemplo
puede tomar temporalmente la identidad de root para adquirir privilegios
de superusuario y así acceder por ejemplo a ficheros del sistema propiedad
de root.
El sistema recibe continuamente peticiones de los procesos y el EUID del
proceso determinará que el kernel le conceda permiso para efectuar la
operación deseada o no.
Muchas veces sorprende que en Linux apenas se conozcan unos pocos casos
de virus, mientras que en otros sistemas parecen estar a la orden del día.
Es perfectamente posible realizar un virus que infecte un sistema Linux
pero de una forma o de otra el administrador tendría que darle los
privilegios que le convierten en peligroso. Por eso no es facil que estos
virus lleguen muy lejos.
Como se crea un nuevo proceso
La llamada al sistema exec()
La llamada al sistema fork()
También normalmente aunque no siempre esta parte de la lección es necesario
releerla más de una vez.
Estamos dando pequeños detalles de programación porque en estas dos llamadas
del sistema son muy significativas. Su funcionamiento resulta chocante y
su comprensión permite explicar un montón de cosas.
Test
A cada proceso le corresponderá un número PID que le identifica totalmente.
Es decir en un mismo momento es imposible que existan dos procesos con
el mismo PID.
Lo mismo que todos los procesos tienen un atributo PID que es el número de
proceso que lo identifica en el sistema tambien existe un atributo llamado
PPID. Este número se corresponde con el número PID del proceso padre.
Todos los procesos deben de tener un proceso que figure como padre pero
entonces que ocurre si un padre muere antes que alguno de sus hijos ?
En estos casos el proceso 'init' del cual hablaremos en seguida adoptará
a estos procesos para que no queden huerfanos.
Cuando arranca el sistema se desencadena una secuencia de procesos que a
grandes rasgos es la siguiente. Primero se carga el nucleo de Linux
(Kernel) de una forma totalmente especial y distinta a otros procesos.
Dependiendo de los sitemas puede existir un proceso con PID=0 planificador,
o swapper. En Linux y en casi todos los sistemas tipo Unix seguirá un
proceso llamado 'init'. El proceso init tiene PID = 1. Lee un fichero
llamado inittab donde se
relacionan una serie de procesos que deben arrancarse para permanecer
activos todo el rato (demonios). Algunos de ellos están definidos para
que en caso de morir sean arrancados de nuevo inmediatamente garantizando
la existencia de ese servicio de forma permanente.
Los procesos tienen un EUID (Efectiv User Identif), y un UID normalmente
ambos coinciden. El UID es el identificador de usuario real que coincide
con el identificador del usuario que arrancó el proceso. El EUID es el
identificador de usuario efectivo y se llama así porque es el identinficador
que se tiene en cuenta a la hora de considerar los permisos que luego
explicaremos.
$ # Cambiamos el directorio actual a home
$ cd
$ # Comprobamos a quien pertenece 'uid' y 'guid'
$ ls -ld .
$ # Ahora obtenemos el 'uid' y el 'gid' con sus valores numéricos.
$ ls -lnd .
El núcleo del sistema llamado también kernel es el encargado de realizar la
mayoría de funciones básicas que gestiona entre otras cosas los procesos.
La gestíon de estas cosas se hacen por medio de un limitado número de
funciones que se denominan llamadas al sistema. Estas llamadas al sistema
están implementadas en lenguaje C y hablaremos ahora un poco sobre un par
de ellas llamadas fork() y exec(). Si logramos que tenga una vaga idea
de como funcionan estas dos importantísimas funciones facilitaremos la
compresión de muchas otras cosas más adelante.
Cuando hablamos de proceso debe usted pensar solamente en algo que se
está ejecutando y que está vivo. Un proceso puede evolucionar y cambiar
totalmente desde que arranca hasta que muere. Lo único que no cambia en
un proceso desde que nace hasta que se muere es su identificador de proceso
PID. Una de las cosas que puede hacer un proceso es cambiar por completo
su código. Por ejemplo un proceso encargado de procesar la entrada y
salida de un términal (getty) puede transformarse en un proceso de
autentificacion de usuario y password (login) y este a su vez puede
transformarse en un interprete de comandos (bash). Si la llamada exec()
falla retornará un -1. Esto no es curso de programación así que nos da
igual el valor retornado pero lo que si nos interesa es saber que cuando
esta llamada tiene éxito no se produce jamás un retorno. En realidad no
tendría sentido retornar a ningún lado. Cuando un proceso termina simplemente
deja de existir. En una palabra muere y ya está. La llamada exec() mantiene
el mismo PID y PPID pero transforma totalmente el código de un proceso en
otro que cargará desde un archivo ejecutable.
La forma en que un proceso arranca a otro es mediante una llamada al sistema
con la función fork(). Lo normal es que el proceso hijo ejecute luego una
llamada al sistema exec().
fork() duplica un proceso generando dos procesos casi idénticos. En realidad
solo se diferenciaran en los valores PID y PPID.
Un proceso puede pasar al proceso hijo una serie de variables pero
un hijo no puede pasar nada a su padre a través de variables.
Además fork() retorna un
valor numérico que será -1 en caso de fallo, pero si tiene éxito se habrá
producido la duplicación de procesos y retornará un valor distinto para
el proceso hijo que para el proceso padre. Al proceso hijo le retornará
el valor 0 y al proceso padre le retornará el PID del proceso hijo.
Después de hacer fork() se pueden hacer varias cosas pero lo primero que
se utiliza despúes del fork es una pregunta sobre el valor retornado por
fork() para así saber si ese proceso es el padre o el hijo ya que cada uno
de ellos normalmente deberá hacer cosas distintas. Es decir la pregunta
sería del tipo si soy el padre hago esto y si soy el hijo hago esto otro.
Con frecuencia aunque no siempre el hijo hace un exec() para trasformarse
completamente y con frecuencia aunque no siempre el padre decide esperar
a que termine el hijo.
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.
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos