Se
le llama proceso en Unix a un programa en ejecución y al objeto
abstracto que crea el sistema operativo para manejar el acceso de ese programa
a los recursos del sistema (memoria, CPU, dispositivos de E/S). Pueden
coexistir varias instancias de un mismo programa ejecutando en forma
simultánea. Cada una de ellas es un proceso diferente.
Unix es un sistema multiproceso por tiempo compartido. A los ojos de un usuario
en un momento dado hay múltiples programas en ejecución, cada uno
de ellos avanzando en su tarea. Sin embargo en una máquina con un solo
procesador hay en cada instante solamente un proceso ejecutando. Es el sistema
operativo el que va rotando el uso del procesador a intervalos breves (alguna
decena de milisegundos) entre los procesos definidos en el sistema de forma que
se crea la ilusión que todos avanzan simultáneamente.
El administrador del sistema dispone de herramientas para supervisar el estado
de los procesos y eventualmente tomar acciones para suspender o detener la
ejecución de un proceso o simplemente modificar su comportamiento.
Para
cada proceso definido en el sistema, el kernel del sistema operativo almacena y
mantiene al día varios tipos de información sobre el proceso.
Esta información podemos ordenarla de la siguiente forma:
* Información general. Identificadores de proceso, usuario y grupo
* Ambiente (variables, directorio actual, etc.)
* Información de E/S
* Información de Estado
* Espacio de direcciones del proceso. Areas de trabajo (código ("text"),
datos, stack)
Al
crearse un nuevo proceso se le asigna un identificador de proceso único.
Este número debe utilizarse por el administrador para referirse a un
proceso dado al ejecutar un comando.
Los PID son asignados por el sistema a cada nuevo proceso en orden creciente
comenzando desde cero. Si antes de un reboot del sistema se llega al nro.
máximo, se vuelve a comenzar desde cero, salteando los procesos que
aún estén activos.
La
creación de nuevos procesos en Unix se realiza por la vía de
duplicar un proceso existente invocando al comando fork(). Al proceso original
se le llama "padre" y al nuevo proceso "hijo". El PPID de un proceso es el PID
de su proceso padre.
El mecanismo de creación de nuevos procesos en Unix con el comando
fork() se ve con más detalle en el apartado "Ciclo de vida de un proceso".
Normalmente
estos dos identificadores coinciden pero hay excepciones.
El User ID (UID) del proceso identifica al creador del proceso, esto es a la
persona que lo lanzó a correr. Este usuario y root son los únicos
que pueden modificar al proceso. El UID se utiliza con fines de
tarificación o accounting. El sistema de accounting carga a la cuenta
del usuario identificado por el UID del proceso por los recursos del sistema
que el proceso utilice (tiempo de CPU, impresoras, terminales, etc.).
El Effective User ID (EUID) en cambio se utiliza para determinar si el proceso
tiene permiso para acceder a archivos y otros recursos del sistema.
La forma más habitual de hacer que el EUID de un proceso sea el de un
usuario diferente del que lanza a correr el programa es activando el flag
setuid en el archivo del programa. Un ejemplo de esto son los comandos que
permiten a un usuario modificar su password, en que se debe modificar el
archivo passwd o equivalente del sistema sobre el cual el usuario obviamente no
tiene permiso de escritura. Habitualmente ese comando es un archivo de root con
setuid y el proceso corre con EUID de root.
Es
totalmente análogo a los identificadores de usuario pero para grupos de
usuarios. El GID se hereda del proceso padre. El EGID puede utilizarse igual
que el EUID para controlar el acceso del proceso a archivos.
En la mayoría de los sabores actuales de Unix el proceso puede estar en
varios grupos y se chequea contra toda la lista de grupos para definir si el
proceso puede acceder o no a un recurso.
El proceso mantiene actualizado cual es su directorio de trabajo.
Son heredadas por los procesos hijos.
Solamente existen en el proceso que las define.
En
general los procesos están asociados a una terminal de control. Esta
terminal determina el valor por defecto de los archivos stdin, stdout y stderr
del proceso.
Una excepción a esto son los procesos llamados daemons, que una
vez lanzados se desvinculan de su terminal de control y siguen ejecutando
inclusive después de cerrada la sesión de usuario desde la cual
se lanzaron a correr.
El
kernel mantiene descriptores de los archivos abiertos por el proceso.
Están siempre definidos los archivos de entrada, salida y error
estándar (stdin, stdout y stderr). Por defecto están asociados
con el teclado y la pantalla del terminal de control del proceso pero pueden
ser redireccionados a un archivo cualquiera. Todos los shells prevén un
mecanismo para hacer este redireccionamiento en el momento de lanzar a correr
un programa.
En la mayoría de los sistemas multiproceso como Unix, cada proceso tiene la ilusión de disponer para si el espacio de direcciones completo del procesador. En realidad el procesador ve un espacio de direcciones virtual. Este espacio está organizado en secciones para el código (text), datos, stack y otras y generalmente está dividido en páginas. En un instante dado una página puede estar residiendo en la memoria física del procesador o puede estar almacenada en disco en un procedimiento llamado "swapping". El sistema operativo, con el auxilio del hardware, mantiene al día una tabla con el estado de cada página de memoria del proceso.
Los
estados básicos en los que puede estar un proceso son los siguientes:
* Durmiendo (asleep). En general a la espera de algún recurso
compartido.
* Listo para ejecutar (runnable). A la espera que le toque el turno en
el uso de la CPU.
* Ejecutando (running). Puede estar ejecutando en modo kernel o en modo
usuario.
A su vez el proceso (o partes del espacio de memoria virtual del proceso) puede
estar cargado en memoria o "swapped" a disco.

Además de estos estados básicos un proceso puede estar detenido
(stopped). En este caso se le prohibe ejecutar al proceso. Hay
mecanismos para detener y rearrancar un proceso a través de las
señales STOP y CONT que veremos más adelante.
El estado stopped es el estado en que queda un proceso lanzado a correr desde
un interprete de comandos (shell) cuando se presiona <Control-Z> o la
tecla configurada como "suspend" en el terminal que estemos utilizando.
Finalmente el otro estado en que a menudo un administrador encuentra a un
proceso es el estado zombie o exiting. Un proceso en este estado
está en proceso de terminación. Este caso se discute más
en detalle en el apartado "Ciclo de vida de un proceso".
El
mecanismo de creación de un proceso en Unix es un poco peculiar. Un
proceso se crea invocando a una función del sistema operativo llamada
fork(). La función fork() crea una copia idéntica del
proceso que la invoca con excepción de:
* El nuevo proceso tiene un PID diferente
* El PPID del nuevo proceso es el PID del proceso original
* Se reinicia la información de tarificación del proceso (uso de
CPU, etc.)
Al retorno de fork() se siguen ejecutando las siguientes sentencias del
programa en forma concurrente. Para distinguir entre los dos procesos la
función fork() devuelve un cero al proceso hijo y el PID del nuevo
proceso al proceso padre. Normalmente el proceso hijo lanza luego un nuevo
programa ejecutando alguna variante de comando exec(). En el recuadro puede
verse un ejemplo del uso de fork.
kidpid = fork()
if (kidpid==0) {
/* soy el hijo, p. ej. lanzo un nuevo prog. */
exec(...)
}
else{
/* soy el padre */
...
wait() /* espero exit() del hijo */
}
|
Cambio
de contexto entre procesos:
* código ("text")
* datos
* stack
* tablas varias
Varias secuencias de ejecución (threads) pueden agruparse en un proceso
y compartir algunos segmentos
Ventajas:
* simplifica cambio de contexto y comunicación
* facilita el uso de múltiples procesadores
Soportado por la mayoría de los SO actuales (Digital Unix, DEC OSF/1,
Solaris 2, AIX, HP-UX, SGI Irix, Linux)
Posix standard
Bibliotecas provistas por terceros
Las
señales de Unix son un mecanismo para anunciar a un proceso que ha
sucedido cierto evento que debe ser atendido. La lista de posibles
señales a comunicar a los procesos está fija, con algunas
variaciones de un sabor a otro de Unix.
La recepción de una señal en particular por parte de un proceso
provoca que se ejecute una subrutina encargada de atenderla. A esa rutina se le
llama el "manejador" de la señal (signal handler). Un proceso puede
definir un manejador diferente para sus señales o dejar que el kernel
tome las acciones predeterminadas para cada señal.
Cuando un proceso define un manejador para cierta señal se dice que
"captura" (catch) esa señal.
Si se desea evitar que determinada señal sea recibida por un proceso se
puede solicitar que dicha señal sea ignorada o bloqueada.
Una señal ignorada simplemente se descarta sin ningún efecto
posterior. Cuando alguien envía a cierto proceso una señal que
está bloqueada la solicitud se mantiene encolada hasta que esa
señal es explícitamente desbloqueada para ese proceso. Cuando la
señal es desbloqueada la rutina de manejo de la señal es invocada
una sola vez aunque la señal haya sido recibida más de una vez
mientras estaba bloqueada.
Si bien un proceso tiene ciertas libertades para configurar como reacciona
frente a una señal (capturando, bloqueando o ignorando la señal),
el kernel se reserva ciertos derechos sobre algunas señales. Así,
las señales llamadas KILL y STOP no pueden ser capturadas, ni
bloqueadas, ni ignoradas y la señal CONT no puede ser bloqueada.
La señal KILL provoca la terminación de un proceso. La
señal STOP provoca la detención del proceso que queda en el
estado "stopped" hasta que alguien le envíe la señal CONT.
Una señal puede enviarse desde un programa utilizando llamadas al
sistema operativo, o desde la línea de comandos de un shell utilizando
el comando kill. Al comando kill se le pasa como parámetro
el número o nombre de la señal y el PID del proceso. El uso
más habitual del comando es para enviar una señal TERM o KILL
para terminar un proceso, de ahí su nombre.
Para muchas de las señales la acción predeterminada consiste en
terminar el proceso. En algunos casos se genera además un core
dump. Un core dump es un archivo con una imagen del estado del sistema que
permite al desarrollador de un programa diagnosticar problemas con la ayuda de
un debugger. Al usuario final de un programa esta imagen rara vez le sirve de
ayuda.
La lista de posibles señales puede obtenerse para cada sistema a
través del man del comando kill o de la función kill() del
sistema operativo. En la tabla se listan las utilizadas más a menudo por
un administrador.
ID |
Nombre |
Uso habitual |
1 |
SIGHUP |
Usualmente para releer configuración |
9 |
SIGKILL |
El kernel destruye el proceso |
15 |
SIGTERM |
Terminación "elegante", en general termina enviándose un KILLL a sí mismo |
SIGSTOP |
El kernel pasa el proceso a stopped | |
SIGCONT |
||
SIGUSR |
Definidas por el usuario o más bien por quien programó el proceso |
Cuando
hay más de un proceso en el estado "listo para ejecutar", el kernel le
asigna el uso de la CPU al de mayor prioridad en ese momento. En el caso de
Unix esta prioridad varía dinámicamente. Las diferentes versiones
y sabores de Unix utilizan diferentes algoritmos de planificación del
uso de la CPU (algoritmos de scheduling), pero en todos los casos tienen
características similares:
- procuran ser justos con los diferentes procesos
- procuran dar buena respuesta a programas interactivos
Para eso los algoritmos consideran parámetros como cuanto uso de CPU ha
hecho el proceso recientemente, si pasa mucho tiempo dormido a la espera de un
evento de teclado (sería un proceso interactivo), etc..
El administrador del sistema o el usuario dueño de un proceso pueden
influir en el algoritmo de scheduling a través del llamado valor
nice. Este es un número que se asigna a cada proceso e indica que
tan "nice" es el proceso para con los demás.
Este valor es considerado por el algoritmo de scheduling de manera que un
proceso con valor nice alto estará en desventaja frente a otro con valor
nice menor a la hora de decidir a quien asignar la CPU. Como ejemplo veamos el
algoritmo utilizado por alguna versión de AIX:
P = min + nice + (0.5 x recent)
Donde P indica la prioridad dinámica (a menor P mayor prioridad) y
recent es una medida de cuanto ha recibido la CPU el proceso
recientemente. Recent se calcula de la siguiente forma:
* Inicialmente vale 0
* Al final de cada time slice (aprox. 10 milisegundos) recent se incrementa en
1 para el proceso que está usando la CPU
* Una vez por segundo se divide por dos el valor recent para todos los
procesos
Normalmente el valor nice se hereda del proceso padre. El dueño del
proceso o el propio proceso pueden elevar su valor nice (menor prioridad). El
superusuario puede modificar el valor nice de todos los procesos a gusto.
En los sistemas "a la BSD" el valor del número nice puede variar
entre -20 y +20, siendo por defecto 0.
En System V en cambio los valores posibles van de 0 a 39, siendo 20 el
valor por defecto.
Se puede modificar el valor nice por defecto en el momento de lanzar un
programa lanzándolo a correr con el comando nice, o
posteriormente utilizando el comando renice.
La
herramienta básica para diagnosticar problemas relacionados con procesos
es el comando ps. Este comando genera un reporte de un renglón
por cada proceso, brindando abundante información sobre cada uno.
El comando ps en los dos sabores básicos de Unix (BSD y System V)
difiere en el nombre y función de los parámetros, en la
información que brindan sobre cada proceso y en los criterios de
ordenación de los procesos.
En ambos casos si ejecuto ps sin parámetros solamente se listará
información básica sobre los procesos que pertenecen a mi
usuario. A través del uso de parámetros adecuados puedo agregar
más columnas (más información sobre cada proceso) o
más filas (más procesos) al reporte. Un conjunto de
parámetros que permite ver todos los procesos con un grado de detalle
que en general es adecuado es:
ps -uax (BSD)
ps -elf (System V)
Los campos de información más importantes desplegados por ps para
cada proceso son:
* Usuario (USER)
* Identificadores de proceso (PID, PPID)
* Uso de recursos reciente y acumulado (%CPU, %MEM, TIME)
* Estado del proceso (STAT, S)
* comando invocado (COMMAND)
El comando top actualiza un reporte similar al generado por ps a intervalos periódicos. Normalmente top ordena los procesos por uso de CPU en forma decreciente. Permite además de manera amigable invocar los comandos kill y renice sobre el proceso.
En algunos sistemas está disponible el comando pstree, que lista los procesos y sus descendientes en forma de árbol. Esto permite visualizar rápidamente los procesos que están corriendo en el sistema.
Los comandos w (who) y uptime reportan tres números que indican la carga promedio del sistema en un intervalo de 1, 5 y 15 minutos respectivamente. Más precisamente el parámetro reportado es el promedio de la cantidad de procesos listos para correr. Este valor es un indicador rápido de la actividad del sistema y suele vigilarse para detectar sobrecargas en el mismo, o registrarse periódicamente para un análisis posterior en caso de problemas. La relación entre este número y que tan "pesado" se vuelve el sistema para los usuarios varía fuertemente dependiendo de las características del sistema (de la cantidad de procesadores p. ej.) y de los procesos que se están ejecutando.
El
comando vmstat reporta varias estadísticas que mantiene el kernel
sobre los procesos, la memoria y otros recursos del sistema. Se puede tomar una
instantánea de esas estadísticas o repetirlo cierta cantidad de
veces a intervalos de algunos segundos.
Alguna de la información reportada por vmstat es la siguiente:
* Cantidad de procesos en diferentes estados (listo para correr, bloqueado,
"swapeado" a disco.
* Valores totales de memoria asignada a procesos y libre.
* Estadísticas sobre paginado de memoria (page faults, paginas llevadas
o traídas a disco, páginas liberadas)
* Operaciones de disco
* Uso de CPU reciente clasificado en inactivo, ejecutando en modo usuario y
ejecutando en modo kernel
A
menudo, por algún bug en el programa o por algún error de
operación, los procesos no terminan correctamente y es necesario
terminarlos por algún método más violento. El
procedimiento usual en estos casos es obtener el PID del proceso con la ayuda
de ps y luego terminarlo con el comando kill. Enseñarle a
los usuarios a hacer estas operaciones con sus procesos colgados es una muy
buena inversión de tiempo para un administrador de sistema.
También suele suceder que algún proceso quede fuera de control y
comience a acaparar algún recurso del sistema (memoria, disco o CPU).
Esto puede suceder por un error de programación, por un error de
configuración, por intentar correr algún proceso que necesita
más recursos que los disponibles o directamente por mala
intención.
Sea cual sea el caso se hace necesario que el dueño del proceso o el
administrador del sistema tomen medidas para frenar o terminar a ese
proceso.
En general el primer síntoma es "el sistema está muy pesado" (o
el teléfono sonando con reclamos de los usuarios). El primer paso
será identificar a el o los procesos problemáticos utilizando las
herramientas ya vistas. Si la situación es tan grave que dificulta la
operación del administrador, puede ser recomendable lanzar un shell de
root con prioridad alta utilizando el comando nice.
Si no está claro por qué el proceso puede estar acaparando
recursos se puede intentar detenerlo con la señal STOP hasta ubicar al
usuario dueño del proceso. Una vez ubicado al usuario si se considera
necesario que el proceso continúe se le puede bajar la prioridad de
ejecución con el comando renice.
Las siguientes opciones son o bien pedirle al proceso que tenga a bien terminar
con la señal TERM (kill -15 PID) o bien terminarlo por la fuerza
con la señal KILL (kill -9 PID)
*
Use el comando ps y analice la información obtenida.
Experimente las diferentes opciones de ps en su sistema.
* Experimente el comando kill. Ejecute comandos en background
para tener "materia prima" con qué jugar. Utilice ps junto
con grep para averiguar el PID
* Consulte el man para saber como reacciona algún proceso daemon
(p. Ej. named) frente a diferentes signals
*
man page de:
* ps, top, kill, nice, renice, vmstat
* Nemeth, "UNIX System Administration Handbook", cap 5. El libro
rojo/violeta
* Frisch, "Essential Unix Administration", cap. 7. El libro "de la mulita"