Ud  Procesos
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Ud Procesos

on

  • 903 views

 

Statistics

Views

Total Views
903
Views on SlideShare
903
Embed Views
0

Actions

Likes
1
Downloads
14
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Ud Procesos Document Transcript

  • 1. Procesos i
  • 2. Í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
  • 3. 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). 2. Sistema operativo y procesos Cuando un programa es leído del disco por el núcleo y se carga en la memoria para su ejecución, se convierte en un proceso. Esto no significa que el proceso sea una simple copia del programa, pues el núcleo le añade información adicional para poder manejarlo. En este sentido, un proceso es una entidad activa que cuenta con un contador ordinal que especifica la siguiente instrucción que debe ejecutar y con un conjunto de recursos asociados. Los procesos los podemos clasificar en: • Procesos de usuario: se crea a partir de una ejecución directa o mediante una aplicación originada por el usuario. • Procesos de sistema: forman parte del sistema operativo, es decir, realizan operaciones de acceso a recursos de entrada/salida o de apoyo a la ejecución de otros procesos. Estos pueden ser: o Permanentes: arrancan con el sistema y permanecen activos hasta que éste es apagado. o Transitorio: su duración viene determinada por la realización de una tarea específica, de manera que su ejecución finaliza cuando la tarea se ha completado. 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). • USER: el propietario del proceso. • PID: el identificador del proceso. • PPID: • % CPU: porcentaje de CPU consumida. • % MEM: porcentaje de memoria consumida. • SIZE: tamaño total del proceso (Kilobytes). • RSS: Kilobytes del programa en memoria. (El resto estará en disco (swap)). • TTY: identificador del terminal desde donde se lanzó el proceso. • STAT: estado del proceso. • START: hora en la se lanzó el proceso. • TIME: tiempo de CPU consumido. • COMMAND: nombre del proceso. MGB 1
  • 4. 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 Este comando es prácticamente equivalente a ps –ef | grep i5590. 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. Figura 4.1: Estados de un proceso 2 MGB
  • 5. 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. 6. Identificadores de usuarios y procesos Las funciones C que se utilizan para obtener el identificador de proceso (PID) o el identificador de usuario (UID) son getpid, getppid y getuid: #include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void); uid_t getuid(void); • 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(quot;ID de proceso: %ldnquot;, (long)getpid()); printf(quot;ID de proceso padre: %ldnquot;, (long)getppid()); printf(quot;ID de usuario propietario: %ldnquot;, (long)getuid()); return 0; } MGB 3
  • 6. Procesos 7. Ejecutando comandos de UNIX desde C Se pueden ejecutar comandos desde un programa de C como si se estuviera en la línea de comandos de UNIX usando la función system(). NOTA: se puede ahorrar bastante tiempo y confusión en vez de ejecutar otros programas, scripts, etc. para hacer las tareas. int system(char *mandato) -- donde mandato puede ser el nombre de una utilería de UNIX, un shell ejecutable o un programa del usuario. La función regresa el status de salida del shell. La función tiene su prototipo en <stdlib.h> Ejemplo: llamada del comando ls desde un programa main() { printf(quot;Archivos en el directorio son:nquot;); system(quot;ls -lquot;); } 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: En lugar de Es mejor utilizar system(“rm –f fichero”); unlink (“fichero”); system(“mkdir directorio”); mkdir(“directorio”); system(“mv nomantig nomnuevo”); rename (“nomantig”, “nomnuevo”); 8. Creación de procesos: fork() Los procesos de un sistema UNIX tienen una estructura jerárquica, de manera que un proceso (proceso padre) puede crear un nuevo proceso (proceso hijo) y así sucesivamente. Para la realización de aplicaciones con varios procesos, el sistema operativo UNIX proporciona la llamada al sistema1 fork(). 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
  • 7. 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() { int pid = 0, status = 0; pid = fork() ; switch( pid) { case -1: printf(“Error al crear proceso hijon”); exit(1); break ; case 0: /* Proceso Hijo */ printf(“El PID de mi proceso padre es %dn”, getppid()); exit(1); break ; default: /* Proceso Padre */ printf(“Mi PID es el %d y he creado un proceso hijo cuyo pid es %dn”, getpid(), pid); wait( &status); printf(“nEl proceso hijo finalizo con el estado %dn”, status); exit(0); } } 8.1. Herencia de descriptores Cuando fork crea un proceso hijo, éste hereda la mayor parte del entorno y contexto del padre, que incluye el estado de las señales, los parámetros de la planificación de procesos y la tabla de descriptores de archivo. Hay que tener cuidado ya que las implicaciones de la herencia de los descriptores de archivos no siempre resultan obvias, ya que el proceso padre e hijo comparten el mismo desplazamiento de archivo para los archivos que fueron abiertos por el padre antes del fork. 9. Ejecución de procesos: exec Una llamada al sistema que se utiliza normalmente en combinación con fork() es execl(), la cual permite ejecutar un código (previamente compilado) desde otro programa. Cabecera: #include <unistd.h> int execl (const char *path, const char *arg0, ..., const char *argn, char * /*NULL*/); int execv (const char *path, char *const argv[]); int execle (const char *path, const char *arg0, ..., const char *argn, char * /*NULL*/, char *const envp[]); int execve (const char *path, char *const argv[], char *const envp[]); int execlp (const char *file, const char *arg0, ..., const char *argn, char * /*NULL*/); MGB 5
  • 8. Procesos int execvp (const char *file, char *const argv[]); • 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 (quot;Lista de procesosnquot;); if (execl (quot;psquot;, quot;psquot;, quot;-fquot;, 0) < 0) { fprintf(stderr, quot;Error en exec %dnquot;, errno); exit(1); } printf (quot;Fin de la lista de procesosnquot;); 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, quot;Error en fork %dnquot;, errno); exit(1); } else if (childpid == 0) { /* código del proceso hijo */ if ( execl (quot;/bin/psquot;, quot;psquot;, quot;-fuquot;, getenv (quot;USERquot;), 0) < 0) { fprintf(stderr, quot;Error en exec %dnquot;, errno); exit(1); 6 MGB
  • 9. 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() { int pid = 0, status = 0; pid = fork() ; switch( pid) { case -1: printf(“Error al crear proceso hijon”); exit(1); break ; case 0: /* Proceso Hijo */ if (execl(``esclavo'', ``nombre'', ``-a'', NULL) == -1) { printf(``Error al ejecutar execln''); exit(1); } break ; default: /* Proceso Padre */ wait( &status); printf(“nEl proceso hijo finalizo con el estado %dn”, 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 main( int argc, char *argv[]) { int i = 0; for (i = 0; i < argc; i++) printf(``nArgumento [%d]: %s'', i, argv[i]); exit(0); } MGB 7
  • 10. Procesos 10. La llamada wait() La función int wait() (int *status) forzará a un proceso padre para que espere a un proceso hijo que se detenga o termine. La función regresa el PID del hijo o -1 en caso de errror. El estado de la salida del hijo es regresado en status. 11. La llamada exit() La función void exit(int status) termina el proceso que llama a esta función y regresa en la salida el valor de status. Tanto UNIX y los programas bifurcados de C pueden leer el valor de status. Por convención, un estado de 0 significa terminación normal y cualquier otro indica un error o un evento no usual. Muchas llamadas de la biblioteca estándar tienen errores definidos en la cabecera de archivo sys/stat.h. Se puede fácilmente derivar su propia convención. 12. La terminación de procesos Cuando termina un proceso (normal o anormalmente), el SO recupera los recursos asignados al proceso terminado, actualiza las estadísticas apropiadas y notifica a los demás procesos la terminación. Las actividades realizadas durante la terminación de un proceso incluyen la cancelación de temporizadores y señales pendientes, la liberación de los recursos de memoria virtual así como la de otros recursos del sistema ocupados por el proceso. Cuando un proceso termina, sus hijos huérfanos son adoptados por el proceso init, cuyo ID el 1. Si un proceso padre no espera a que sus hijos terminen la ejecución, entonces éstos se convierten en procesos zombies y tiene que ser el proceso init el que los libere (lo hace de manera periódica). 1 Las llamadas a exit y exit se utilizan para terminar de forma normal un proceso. La función exit lo que hace es llamar a los manejadores de terminación del usuario (si existen) y después llamar a exit. 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. 4. ¿Cuál es el efecto del siguiente programa? main() { int e, i; for (i=1;i<=3;i++) { if (fork()!=0) wait(&e); printf(quot;%dnquot;,i); } } Indicar las salidas a pantalla y el árbol de procesos creados. 5. Dado el siguiente trozo de programa: for(i=0;i<2;i++) if(fork()==getpid()) printf(quot;UCLMquot;); ¿Cuál es el resultado impreso en UNIX? a) Imprime “UCLM” dos veces. b) Imprime “UCLM” cuatro veces. 1 Fíjese que return no es una llamada al sistema sino que se trata de una instrucción del lenguaje C. 8 MGB
  • 11. Procesos c) No imprime nada. 6. Dado el siguiente código: void main() { if (fork() != 0) { if (fork() != 0) printf(quot;Proceso Padrenquot;); else printf(quot;Proceso Hijo 2nquot;); } else printf(quot;Proceso Hijo 1nquot;); } a) Dibujar el árbol de procesos. b) Indicar el orden en que se imprimen los mensajes. 7. Dado el siguiente código: void main() { int i; int j=0; for (i=1; i<4; i++) { if (fork() == 0) { j++; } else { exit(1); } j++; } } a) Dibujar el árbol de procesos que se obtiene. b) Indicar el valor final de la variable j en cada uno de ellos. 8. Dado el siguiente código: void main() { int j=1; for (i=1; i<3; i++) { fork(); printf(quot;%d, %dquot;, i, j); j++; } } a) Dibujar el árbol de procesos que se obtiene. b) ¿Qué mensaje imprime cada proceso. MGB 9