Proceso
Proceso
Proceso
i
Índice
1. INTRODUCCIÓN ....................................................................................................................... 1
2. SISTEMA OPERATIVO Y PROCESOS.................................................................................. 1
3. IDENTIFICACIÓN DE PROCESOS........................................................................................ 1
4. ESTADOS DE UN PROCESO ................................................................................................... 2
5. MULTIPROGRAMACIÓN ....................................................................................................... 3
6. IDENTIFICADORES DE USUARIOS Y PROCESOS ........................................................... 3
7. EJECUTANDO COMANDOS DE UNIX DESDE C ............................................................... 4
8. CREACIÓN DE PROCESOS: FORK().................................................................................... 4
8.1. HERENCIA DE DESCRIPTORES ................................................................................................ 5
9. EJECUCIÓN DE PROCESOS: EXEC....................................................................................... 5
10. LA LLAMADA WAIT() ...................................................................................................... 8
11. LA LLAMADA EXIT() ...................................................................................................... 8
12. LA TERMINACIÓN DE PROCESOS ................................................................................. 8
13. EJERCICIOS.......................................................................................................................... 8
ii MGB
Procesos
1. Introducción
Un programa se compone de un conjunto de instrucciones (operaciones aritméticas, bucles de control y órdenes
de entrada y salida) que se ejecutan siguiendo una secuencia. Una vez que hemos conocido estas operaciones en un
curso de programación básica, aprenderemos a programar la ejecución de varios programas o procesos al mismo
tiempo.
Se verá como un programa (proceso padre) puede crear, terminar y controlar otros procesos (procesos hijos).
3. Identificación de procesos
El sistema asigna a cada proceso, en el momento de su nacimiento, un número entero (mayor que cero) que lo
identifica de forma unívoca y que recibe el nombre de pid. El sistema operativo UNIX proporciona la orden ps que
nos da información relativa a cada uno de los procesos que existen en el sistema:
ps [-aA] [-G grouplist] [-o format] ... [-p proclist][-t termlist] [-U userlist]
Muchas de las implementaciones que hacen los distintos vendedores del comando ps no cumplen con el estándar
POSIX, un ejemplo claro es Sun Solaris (SO del servidor de prácticas de la escuela) que emplea la opción -e en lugar
de -A para mostrar la información de todos los procesos. La versión larga del comando ps de Solaris muestra mucha
información interesante sobre los procesos asociados a un terminal (la figura 1 muestra algunos).
MGB 1
Procesos
Ejemplo 1 El siguiente comando muestra, en formato largo, todos los procesos cuyo usuario propietario es i5599:
murillo:/export/home/cursos/so> ps -fu i5590
UID PID PPID C STIME TTY TIME CMD
i5590 12246 12195 0 20:53:16 pts/15 0:02 clips
i5590 12164 12150 0 20:30:56 pts/15 0:00 -ksh
i5590 12194 12164 0 20:35:23 pts/15 0:00 -ksh
i5590 12175 12152 0 20:31:12 pts/14 0:00 -ksh
i5590 12195 12194 0 20:35:23 pts/15 0:00 /export/home/cursos/CCIA/bin/tcsh
i5590 12176 12175 0 20:31:12 pts/14 0:00 /export/home/cursos/CCIA/bin/tcsh
i5590 12205 12150 0 20:37:29 pts/16 0:00 -ksh
i5590 12152 12150 0 20:30:39 pts/14 0:00 -ksh
i5590 12192 12176 0 20:34:47 pts/14 0:01 /opt/sfw/bin/emacs oo.clp
4. Estados de un proceso
La idea principal de un sistema multiproceso, tal y como es UNIX, es que el sistema operativo gestione los
recursos disponibles (memoria, CPU, etc) entre los procesos que en ese momento trabajan en el sistema, de tal forma
que, para ellos, el sistema se comporte como si fuera monousuario. Así que, en un sistema monoprocesador, la CPU
se reparte entre los procesos que se tengan en ese momento. Como es lógico, sólo un proceso puede estar
ejecutándose, los demás estarán esperando para poder ocupar la CPU, esta forma de operar recibe el nombre de
ejecución entrelazada.
A continuación, se enumeran los distintos estados en los que se puede encontrar un proceso en este tipo de
sistemas:
• Preparado (R).- Proceso que está listo para ejecutarse. Simplemente está esperando a que el sistema
operativo le asigne un tiempo de CPU.
• Ejecutando (O).- Sólo uno de los procesos preparados se está ejecutando en cada momento
(monoprocesador).
• Suspendido (S).- Un proceso se encuentra suspendido si no entra en el reparto de CPU, ya que se encuentra
esperando algún tipo de evento (por ejemplo, la recepción de una señal software o hardware). En cuanto
dicho evento se produce, el proceso pasa a formar parte del conjunto de procesos preparados.
• Parado (T).- Un proceso parado tampoco entra en el reparto de CPU, pero no porque se encuentre
suspendido esperando algún evento. En este caso, sólo pasarán a estar preparados cuando reciban una señal
determinada que les permita continuar.
• Zombie (Z).- Todo proceso al finalizar avisa a su proceso padre, para que éste elimine su entrada de la tabla
de procesos. En el caso de que el padre, por algún motivo, no reciba esta comunicación no lo elimina de la
tabla de procesos. En este caso, el proceso hijo queda en estado zombie, no está consumiendo CPU, pero sí
continua consumiendo recursos del sistema.
2 MGB
Procesos
Un ejemplo que permite aclarar la diferencia entre procesos suspendidos y procesos preparados es el siguiente: se
tiene un proceso que debe esperar a que el usuario introduzca un valor por teclado. En principio, pueden proponerse
dos soluciones diferentes:
Espera activa.
El proceso está codificado utilizando un bucle, en el cual sólo se comprueba el valor de una variable para saber si
el usuario ha introducido el valor o no. En este caso, el proceso es un proceso preparado que está consumiendo CPU
para realizar la comprobación de la variable continuamente.
Interrupción.
El proceso está codificado de forma que el proceso se suspende. De esta forma, ya no consume CPU porque no tiene
que comprobar en cada ejecución del bucle si el usuario ha introducido un valor. Sólo espera a que sea el propio
usuario el que le “avise” y lo despierte para, así, pasar a estar preparado.
5. Multiprogramación
En un ordenador con un único procesador, en un instante de tiempo dado, hay un solo proceso en ejecución;
dicho proceso se conoce como proceso actual.
A cada proceso le corresponde un espacio de tiempo. Linux elige un proceso y deja que se ejecute durante un
periodo establecido. A continuación, el sistema pasa el proceso actual al estado de a punto, y elige otro proceso para
que se ejecute durante otro espacio de tiempo. Este tiempo es tan corto que el usuario tiene la sensación de que los
distintos procesos se ejecutan simultáneamente.
• pid t es un entero largo con el ID del proceso llamante (getpid) o del padre del proceso llamante
(getppid)
• uid t es un entero con el ID del usuario propietario del proceso llamante.
• En caso se error se devuelve -1.
Ejemplo 2 El siguiente programa imprime el identificadores del proceso llamante, del proceso padre y del
propietario:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
printf("ID de proceso: %ld\n", (long)getpid());
printf("ID de proceso padre: %ld\n", (long)getppid());
printf("ID de usuario propietario: %ld\n", (long)getuid());
return 0;
}
MGB 3
Procesos
La función system es una llamada que esta construida de otras 3 llamadas del sistema: execl(), wait() y
fork() (las cuales tienen su prototipo en <unistd.h>).
Desventajas de utilizar la función system:
• Resulta poco eficiente, pues cada vez que se invoca no sólo comienza a ejecutarse la orden deseada, sino
que también se ejecuta una copia del shell. En consecuencia, si el programa tuviera que ejecutar muchos
comandos, sería más oportuno buscar otra forma de hacerlo.
• Las llamadas al sistema y las rutinas de biblioteca son siempre más eficientes que system. Ejemplo:
Cabecera:
#include <unistd.h>
int fork(void);
Comportamiento de la llamada:
fork() crea un nuevo proceso exactamente igual (mismo código) al proceso que invoca la función. Ambos
procesos continúan su ejecución tras la llamada fork(). En caso de error, la función devuelve el valor -1 y no se
crea el proceso hijo. Si no hubo ningún error, el proceso padre (que realizó la llamada) obtiene el pid del proceso
hijo que acaba de nacer, y el proceso hijo recibe el valor 0.
Ejemplo:
4 MGB
Procesos
En el ejemplo que se muestra a continuación, se crea un proceso hijo que imprime en pantalla el pid de su
proceso padre, mientras que el proceso padre imprime en pantalla su propio pid y el del proceso hijo que ha creado.
Para ello, se utilizan las llamadas al sistema getpid() y getppid(). El proceso padre, antes de finalizar se
suspende hasta que el hijo acaba, para evitar que éste se quede zombie. Para ello, utiliza la llamada al sistema
wait(), que recibe en la variable status el estado en que el proceso hijo finalizó.
Ejemplo3:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
/* Proceso Hijo */
printf(“El PID de mi proceso padre es %d\n”, getppid());
exit(1);
break ;
default:
/* Proceso Padre */
printf(“Mi PID es el %d y he creado un proceso hijo cuyo pid es %d\n”, getpid(), pid);
wait( &status);
printf(“\nEl proceso hijo finalizo con el estado %d\n”, status);
exit(0);
}
}
Cabecera:
#include <unistd.h>
MGB 5
Procesos
• Las seis variaciones de la llamada exec se distinguen por la forma en que son pasados los argumentos de
la línea de comando y el entorno, y por si es necesario proporcionar la ruta de acceso y el nombre del
archivo ejecutable.
• Las llamadas execl (execl, execle y execlp) pasan la lista de argumentos de la línea de comando como
una lista y son útiles sólo si se conoce a priori el número de éstos.
• Las llamadas execv (execvl, execvp y execve) pasan la lista de argumentos en una cadena (un array de
punteros a char) y son útiles cuando no se sabe el número de argumentos en tiempo de compilación.
• path es la ruta (completa o relativa al directorio de trabajo) de acceso al archivo ejecutable.
• argi es el argumento i–´esimo (sólo en llamadas execl).
• argv es una cadena con todos los argumentos (sólo en llamadas execv).
• envp es una cadena con el entorno que se le quiere pasar al nuevo proceso.
Ejemplo 5 El siguiente programa utiliza la función execl para listar los procesos activos en el momento de la
ejecución. Ejecútelo y vea los errores del mismo.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main()
{
int status;
printf ("Lista de procesos\n");
if (execl ("ps", "ps", "-f", 0) < 0)
{
fprintf(stderr, "Error en exec %d\n", errno);
exit(1);
}
printf ("Fin de la lista de procesos\n");
exit(0);
}
Ejemplo 6 El siguiente programa utiliza un esquema fork–exec para listar los procesos del usuario propietario
del proceso.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main()
{
pid_t childpid, waitreturn;
int status;
if ((childpid = fork()) == -1)
{
fprintf(stderr, "Error en fork %d\n", errno);
exit(1);
}
else if (childpid == 0)
6 MGB
Procesos
}
}
else /* c´odigo del proceso padre */
while(childpid != (waitreturn = wait(&status)))
if ((waitreturn == -1) && (errno != EINTR))
break;
exit(0);
Ejemplo 7 En el siguiente ejemplo, el proceso hijo ejecutará el código del ejecutable esclavo. Este programa recibe
dos parámetros de entrada, primero, el nombre bajo el que se ejecutará el proceso hijo (``nombre'') y el segundo es
el parámetro ``-a''. Para que la lista se de por terminada habrá que especificar el parámetro NULL.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
/* Proceso Hijo */
if (execl(``esclavo'', ``nombre'', ``-a'', NULL) == -1)
{
printf(``Error al ejecutar execl\n'');
exit(1);
}
break ;
default:
/* Proceso Padre */
wait( &status);
printf(“\nEl proceso hijo finalizo con el estado %d\n”, status);
exit(0);
}
}
A continuación se muestra un posible código del proceso esclavo, que simplemente imprime en pantalla la lista
de argumentos recibidos:
#include <stdio.h>
int i = 0;
exit(0);
}
MGB 7
Procesos
13. Ejercicios
1. Realice un programa que cree cuatro procesos, A, B, C Y D, de forma que A sea padre de B, B sea padre de C, y C
sea padre de D.
2. Realice un programa copiaConc al que se le pase una lista de nombres de archivos y para cada archivo f cree un
nuevo proceso que se encargue de copiar dicho archivo a f.bak.
3. Realice un programa ejecuta que lea de la entrada estándar el nombre de un programa y cree un proceso hijo para
ejecutar dicho programa.
for(i=0;i<2;i++)
if(fork()==getpid()) printf("UCLM");
1
Fíjese que return no es una llamada al sistema sino que se trata de una instrucción del lenguaje C.
8 MGB
Procesos
c) No imprime nada.
MGB 9