PROCESOS Sistemas Distribuidos Alvaro Ospina Sanjuan Universidad Pontificia Bolivariana Medellín 2010 Proceso Abstracción de un programa en ejecución Consiste en los siguientes elementos: Contexto del programa (status del programa) Directorio de trabajo Archivos y directorios derecho el programa Credenciales o derechos de acceso del programa Cantidad de memoria y otros recursos del sistema asignados sobre los cuales tiene Proceso Abstracción de un programa en ejecución Entidad activa Actividad de la CPU Sistemas procesamiento por lotes Sistemas tiempo compartido Sistemas monousuario Conformado por: Código del programa Pila Datos jobs tasks program Estados de un Proceso 1 EN EJECUCIÓN 4 3 BLOQUEADO 2 LISTO 1. Se bloquea en espera de datos 2. Los datos están disponibles 3. Se asigna a la CPU por el fijador del kernel 4. Se le quita la CPU En ejecución, Listo o preparado, Bloqueado, Nuevo, Espera Implantación Tabla de procesos: Observarla en linux con “??” Demonios: Procesos creados por el SO para ejecución permanente. En linux terminan en “d” Atributos de un proceso: • Todos los procesos tiene un PID o Process ID, porque? • • El PID es único Todo los procesos tiene un proceso padre que es el proceso que lo inició – Excepto el proceso 0 el cual es iniciado por el kernel Llamadas para Procesos en C (linux) pid_t fork(void): Crea un proceso, donde el hijo es copia exacta del padre. La función retorna al proceso hijo “0” y al proceso padre el pid del hijo. int execv(const char *path, char * argv[]): Reemplaza la imagen de un proceso. pid_t wait( int * stat_loc): Suspende al padre hasta que llegue el hijo o una señal. void _exit( int status): Termina el proceso y lo envía donde el padre. Ejercicios de procesos Ejercicio 1 Ejercicio 2 Ejercicio 3 Ejercicio 4 Ejercicio 5 Ejercicio 1 #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void){ printf("\nSoy el proceso: %d\n", getpid()); printf("Mi proceso padre es: %d\n", getppid()); printf("Y mi propietario es: %d\n\n", getuid()); _exit(0); } Ejercicio 2 #include #include #include #include <sys/types.h> <sys/wait.h> <unistd.h> <stdio.h> int main (void){ pid_t childpid; int status; childpid = fork(); if (childpid == -1) { perror("Ha fallado el fork"); _exit(1); } else if (childpid == 0) fprintf(stderr,"Soy el hijo con pid = %d y mi papa es %d\n", getpid(),getppid()); else if (wait(&status) != childpid) fprintf(stderr, "Algo sacó al padre del wait\n"); else fprintf(stderr, "Soy el padre con pid = %d y el pid del hijo es = %d\n",getpid(), childpid); _exit(0); } Ejercicio 3 #include #include #include #include #include <sys/types.h> <sys/wait.h> <unistd.h> <stdio.h> <stdlib.h> int main(void) { pid_t childpid; int status; if ((childpid = fork()) == -1) { perror("Error en el fork"); _exit(1); } else if (childpid == 0) { /* código del hijo */ if (execl("/bin/ls", "ls", NULL) < 0) { perror("Ha fallado la ejecución de ls"); _exit(1); } } else wait(&status); /* código del padre */ _exit(0); } Ejercicio 4 #include #include #include #include <sys/types.h> <sys/wait.h> <unistd.h> <stdio.h> int main (void){ pid_t childpid; int status; if ((childpid = fork()) == -1) { perror("Ha fallado fork"); _exit(1); } else if (childpid == 0){ fprintf(stderr, "Soy el hijo con pid = %d\n", getpid()); sleep(5); } else{ sleep(3); fprintf(stderr, "Soy el padre con pid = %d y mi hijo tiene el pid = %d\n",getpid(), childpid); wait(&status); } _exit(0); } Estados de un Proceso El proceso original sigue por la parte del else y el nuevo proceso por el then. then De esta manera, el proceso original, por ejemplo, podría seguir atendiendo nuevos clientes que quieran conectarse a nuestro programa por un socket, mientras que el proceso hijo podría atender a un cliente que acaba de conectarse y que es el que ha provocado que lancemos el fork(). Proceso Hay que tener en cuenta que se ha duplicado todo el espacio de memoria. Por ello, ambos procesos tienen todas las variables repetidas, pero distintas. Si el proceso original toca la variable "contador", el proceso hijo no verá reflejado el cambio en su versión de "contador". si antes del fork() tenemos, por ejemplo, un flujo abierto (un flujo a archivo, un socket o cualquier otra cosa), después del fork() ambos procesos tendrán abierto el mismo flujo y ambos podrán escribir en él. Es más, uno de los procesos puede cerrar el flujo mientras que el otro lo puede seguir teniendo abierto. Proceso Proceso tiene su propia memoria, es imposible en Linux que un proceso se “meta” en la memoria de otro. Proceso Una vez lanzado el proceso hijo, el padre puede "esperar" que el hijo termine. Para ello tenemos la función wait(). wait() Una vez que salimos del wait(), tenemos unas macros que nos permiten evaluar el contenido de estadoHijo – WIFEXITED(estadoHijo) es 0 si el hijo ha terminado de una manera anormal (caida, matado con un kill, etc). Distinto de 0 si ha terminado porque ha hecho una llamada a la función exit() – WEXITSTATUS(estadoHijo) devuelve el valor que ha pasado el hijo a la función exit(), siempre y cuando la macro anterior indique que la salida ha sido por una llamada a exit(). Respuesta del Ejercicio6 Hijo : Mi pid es 4303. El pid de mi padre es 4302 Hijo : variable = 1. La cambio al valor 2 Padre : Mi pid es 4302. El pid de mi hijo es 4303 Hijo : variable = 2 y salgo Padre : Mi hijo ha salido. Devuelve 33 Padre : variable = 1 ----------------- (program exited with code: 0) Press return to continue Agradecimientos A la profesora Ana Oviedo por su colaboración en este material.