procesos
Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
procesos [2014/10/14 02:18] – [Pipes] lmateu | procesos [2018/07/12 14:00] (actual) – [Procesos] lmateu | ||
---|---|---|---|
Línea 8: | Línea 8: | ||
* En el padre, fork retorna el pid del hijo. | * En el padre, fork retorna el pid del hijo. | ||
* Para el hijo se crea un nuevo espacio de direcciones de memoria que parte inicialmente con una copia de toda la memoria del padre. | * Para el hijo se crea un nuevo espacio de direcciones de memoria que parte inicialmente con una copia de toda la memoria del padre. | ||
- | * El hijo hereda | + | * El hijo hereda todos los archivos abiertos por el padre. Los fds (file descriptors) del padre siguen siendo válidos en el hijo (y también en el padre). |
* El tiempo de ejecución del proceso hijo parte de 0. | * El tiempo de ejecución del proceso hijo parte de 0. | ||
* El hijo parte sin alarmas ni señales pendientes. | * El hijo parte sin alarmas ni señales pendientes. | ||
Línea 17: | Línea 17: | ||
#include < | #include < | ||
| | ||
- | pid_t child; | + | pid_t child= fork(); |
- | if ((child= | + | if (child==0) { |
- | /* aquí se ejecuta el padre */ | + | // aquí se ejecuta el hijo |
- | | + | |
- | ... | + | |
} | } | ||
else { | else { | ||
- | /* aquí se ejecuta el hijo */ | + | // aquí se ejecuta el padre |
- | ... | + | |
+ | ... código que se ejecuta en paralelo con el cuerpo del hijo ... | ||
+ | int status; | ||
+ | waitpid(child, | ||
+ | int rc= WEXITSTATUS(status); | ||
} | } | ||
+ | </ | ||
+ | |||
+ | ==== Formas de terminar un proceso ==== | ||
+ | |||
+ | ^ mecanismo ^ notas ^ | ||
+ | | return del main | Si el valor retornado de 0 se considera que el fin fue exitoso | | ||
+ | | exit(value); | ||
+ | | abort(); | El efecto de esta última llamada es indefinido, pero puede generar un core | | ||
+ | |||
+ | Para matar a otro proceso se usa: | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | pid_t pid; | ||
+ | kill(pid, SIGKILL); | ||
</ | </ | ||
Línea 63: | Línea 84: | ||
Los flags incluyen como valor posible WNOHANG que hace que la llamada no se bloquee. | Los flags incluyen como valor posible WNOHANG que hace que la llamada no se bloquee. | ||
- | === Deberes del padre === | + | ==== Deberes del padre ==== |
En Unix, un padre es completamente independiente de sus hijos, pero debe cumplir con una responsabilidad fundamental: | En Unix, un padre es completamente independiente de sus hijos, pero debe cumplir con una responsabilidad fundamental: | ||
Línea 69: | Línea 90: | ||
Siempre deben evitar generar Zombies en el sistema y es su responsabilidad hacer wait de todos sus hijos muertos. | Siempre deben evitar generar Zombies en el sistema y es su responsabilidad hacer wait de todos sus hijos muertos. | ||
Para observar lo que sucede mientras el padre no ha invocado wait compile y ejecute [[start# | Para observar lo que sucede mientras el padre no ha invocado wait compile y ejecute [[start# | ||
+ | |||
+ | ==== Pipes ==== | ||
+ | |||
+ | Un pipe es un canal de comunicación entre dos procesos. Se comporta como una cola (FIFO) en donde lo que se escribe por un extremo se lee por el otro. Fue el primer mecanismo que tuvo Unix para comunicar procesos y todavía se | ||
+ | usa cuando desde el shell Ud. invoca por ejemplo '' | ||
+ | |||
+ | Para crear un pipe se invoca: | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | | ||
+ | int fds[2]; | ||
+ | pipe(fds); | ||
+ | </ | ||
+ | |||
+ | en donde fds es un arreglo de tamaño 2. Después de ejecutar esta llamada, se obtienen 2 fds: | ||
+ | |||
+ | fds[0] permite leer | ||
+ | fds[1] permite escribir | ||
+ | |||
+ | Después de invocar pipe, se duplica el proceso mediante fork. Uno de los procesos cierra el fd de lectura y | ||
+ | el otro cierra el de escritura. | ||
+ | El sistema garantiza que un write de tamaño menor o igual a PIPE_BUF es indivisible. | ||
+ | |||
+ | === Ejemplo 1: quicksort paralelo === | ||
+ | |||
+ | El siguiente programa ordena un arreglo por medio de 2 procesos. | ||
+ | y la parte superior en el padre. | ||
+ | [[http:// | ||
+ | |||
+ | < | ||
+ | int leer(int fd, void *buf, int n) { | ||
+ | if (n==0) | ||
+ | return 0; | ||
+ | do { | ||
+ | int rc= read(fd, buf, n); | ||
+ | if (rc<=0) | ||
+ | return 1; /* fracaso: error o fin del archivo/ | ||
+ | n-= rc; /* descontamos los bytes leídos */ | ||
+ | buf= (char*)buf + rc; /* avanzamos el buffer */ | ||
+ | } while (n>0); /* mientras no leamos todo lo que esperamos */ | ||
+ | return 0; /* exito */ | ||
+ | } | ||
+ | |||
+ | void pquicksort(int a[], int i, int j) { | ||
+ | if (i<j) { | ||
+ | int h= particionar(a, | ||
+ | int fd[2]; | ||
+ | pipe(fd); | ||
+ | pid_t pid= fork(); | ||
+ | if (pid==0) { | ||
+ | close(fd[0]); | ||
+ | quicksort(a, | ||
+ | write(fd[1], | ||
+ | exit(0); | ||
+ | } | ||
+ | else { | ||
+ | close(fd[1]); | ||
+ | quicksort(a, | ||
+ | leer(fd[0], (char*)& | ||
+ | close(fd[0]); | ||
+ | waitpid(pid, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Observe que si se reemplazara la llamada de la función leer por read, podría funcionar en algunas plataformas, | ||
+ | |||
+ | Cuando user fork nunca olvide: | ||
+ | - Invocar exit para terminar el hijo | ||
+ | - Invocar waitpid en el padre para enterrar al hijo | ||
+ | - Padre e hijo usan espacios de direcciones independientes así es que el padre no verá los cambios que el hijo haya hecho en la memoria. | ||
+ | |||
+ | ==== Ejercicio ==== | ||
+ | |||
+ | Resuelva la pregunta 1 partes a y b del [[http:// | ||
==== Cambiar el archivo ejecutable ==== | ==== Cambiar el archivo ejecutable ==== | ||
Línea 103: | Línea 201: | ||
las variables de ambiente (ver más abajo). | las variables de ambiente (ver más abajo). | ||
- | === Ejemplo: invocar ls === | + | === Ejemplo |
< | < | ||
Línea 144: | Línea 242: | ||
} | } | ||
</ | </ | ||
- | ==== Variables de ambiente ==== | ||
- | El puntero environ apunta a un arreglo de strings de la forma: | + | === Ejemplo |
- | + | ||
- | < | + | |
- | name=value | + | |
- | </ | + | |
- | + | ||
- | terminando con un puntero NULL. | + | |
- | + | ||
- | Ejemplo de variables de ambiente: | + | |
- | + | ||
- | HOME | + | |
- | PATH | + | |
- | TERM | + | |
- | TZ | + | |
- | LOGNAME | + | |
- | + | ||
- | Para buscar un nombre de variable en el environment se usa: | + | |
- | + | ||
- | < | + | |
- | #include < | + | |
- | char *p; | + | |
- | + | ||
- | p= getenv(name); | + | |
- | </ | + | |
- | + | ||
- | El parámetro //name// apunta al nombre de la variable y //p// recibe el puntero a la línea // | + | |
- | + | ||
- | ==== Formas de terminar un proceso ==== | + | |
- | + | ||
- | ^ mecanismo ^ notas ^ | + | |
- | | return del main | Si el valor retornado de 0 se considera que el fin fue exitoso | | + | |
- | | exit(value); | + | |
- | | abort(); | El efecto de esta última llamada es indefinido, pero puede generar un core | | + | |
- | + | ||
- | Para matar a otro proceso se usa: | + | |
- | + | ||
- | < | + | |
- | #include < | + | |
- | #include < | + | |
- | + | ||
- | pid_t pid; | + | |
- | kill(pid, SIGKILL); | + | |
- | </ | + | |
- | + | ||
- | ==== Pipes ==== | + | |
- | + | ||
- | Un pipe es un canal de comunicación entre dos procesos. Se comporta como una cola (FIFO) en donde lo que se escribe por un extremo se lee por el otro. Fue el primer mecanismo que tuvo Unix para comunicar procesos y todavía se | + | |
- | usa cuando desde el shell Ud. invoca por ejemplo '' | + | |
- | + | ||
- | Para crear un pipe se invoca: | + | |
- | + | ||
- | < | + | |
- | #include < | + | |
- | + | ||
- | int fds[2]; | + | |
- | pipe(fds); | + | |
- | </ | + | |
- | + | ||
- | en donde fds es un arreglo de tamaño 2. Después de ejecutar esta llamada, se obtienen 2 fds: | + | |
- | + | ||
- | + | ||
- | fds[0] permite leer | + | |
- | fds[1] permite escribir | + | |
- | + | ||
- | Después de invocar pipe, se duplica el proceso mediante fork. Uno de los procesos cierra el fd de lectura y | + | |
- | el otro cierra el de escritura. | + | |
- | El sistema garantiza que un write de tamaño menor o igual a PIPE_BUF es indivisible. | + | |
- | + | ||
- | === Ejemplo 1: quicksort paralelo === | + | |
- | + | ||
- | El siguiente programa ordena un arreglo por medio de 2 procesos. | + | |
- | y la parte superior en el padre. | + | |
- | [[http:// | + | |
- | + | ||
- | < | + | |
- | int leer(int fd, char *buf, int n) { | + | |
- | if (n==0) | + | |
- | return 0; | + | |
- | do { | + | |
- | int rc= read(fd, buf, n); | + | |
- | if (rc< | + | |
- | return 1; /* fracaso: error o fin del archivo/ | + | |
- | n-= rc; /* descontamos los bytes leídos */ | + | |
- | buf+= rc; /* avanzamos el buffer para no reescribir lo leido previamente */ | + | |
- | } while (n>0); /* mientras no leamos todo lo que esperamos */ | + | |
- | return 0; /* exito */ | + | |
- | } | + | |
- | + | ||
- | void pquicksort(int a[], int i, int j) { | + | |
- | if (i<j) { | + | |
- | int h= particionar(a, | + | |
- | int fd[2]; | + | |
- | pipe(fd); | + | |
- | pid_t pid= fork(); | + | |
- | if (pid==0) { | + | |
- | quicksort(a, | + | |
- | write(fd[1], | + | |
- | exit(0); | + | |
- | } | + | |
- | else { | + | |
- | quicksort(a, | + | |
- | leer(fd[0], (char*)& | + | |
- | waitpid(pid, | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | Observe que si se reemplazara la llamada de la función leer por read, probablemente funcionaría en algunos caso, pero sería incorrecto. | + | |
- | siempre entregará la cantidad de bytes pedidos. | + | |
- | más bytes que la capacidad del buffer que utiliza un pipe. Por otra parte, se garantiza que write siempre escribirá la cantidad de bytes pedidos. | + | |
- | un error. | + | |
- | + | ||
- | === Ejemplo | + | |
El siguiente programa filtra la salida por medio de more: | El siguiente programa filtra la salida por medio de more: | ||
Línea 332: | Línea 316: | ||
=== Ejercicio: ls | more === | === Ejercicio: ls | more === | ||
- | Escriba la función ls_pipe_more() que lanza el comando ls de manera que su salida estándar alimente | + | * Modifique la función pquicksort de modo que reciba el parámetro adicional p, correspondiente al número de procesos que se usarán para el ordenamiento. |
- | la entrada estándar del comando more. Esto es equivalente a invocar desde el shell de comandos '' | + | * Escriba la función ls_pipe_more() que lanza el comando ls de manera que su salida estándar alimente la entrada estándar del comando more. Esto es equivalente a invocar desde el shell de comandos '' |
- | + | ||
- | Nota: La solución de este ejercicio aparece en [[start# | + | |
==== La función system ==== | ==== La función system ==== | ||
Línea 346: | Línea 328: | ||
Con la función system es trivial implementar ls_pipe_more. | Con la función system es trivial implementar ls_pipe_more. | ||
ese caso se crea un proceso adicional para sh, el cual no es un proceso barato. | ese caso se crea un proceso adicional para sh, el cual no es un proceso barato. | ||
+ | |||
+ | ==== Variables de ambiente ==== | ||
+ | |||
+ | El puntero environ en las funciones execle, execve y execvpe apunta a un arreglo de strings de la forma: | ||
+ | |||
+ | < | ||
+ | name=value | ||
+ | </ | ||
+ | |||
+ | terminando con un puntero NULL. | ||
+ | |||
+ | Ejemplo de variables de ambiente: | ||
+ | |||
+ | HOME | ||
+ | PATH | ||
+ | TERM | ||
+ | TZ | ||
+ | LOGNAME | ||
+ | |||
+ | Para buscar un nombre de variable en el environment se usa: | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | char *p; | ||
+ | | ||
+ | p= getenv(name); | ||
+ | </ | ||
+ | |||
+ | El parámetro //name// apunta al nombre de la variable y //p// recibe el puntero a la línea // | ||
+ | |||
+ | Para el resto de las funciones de la familia exec se preservan las mismas variables de ambiente que regían antes de la llamada a exec. En una llamada a fork, el proceso hijo hereda las mismas variables de ambiente del proceso hijo. Por esta razón al invocar un comando desde el shell, el proceso que se crea heredará las variables de ambiente del shell. | ||
+ | |||
==== Información disponible durante la ejecución ==== | ==== Información disponible durante la ejecución ==== | ||
Línea 412: | Línea 426: | ||
Otros ejemplos de comandos internos del shell son setenv (en csh) y set. | Otros ejemplos de comandos internos del shell son setenv (en csh) y set. | ||
- | |||
==== Hora y Fecha ==== | ==== Hora y Fecha ==== | ||
Línea 470: | Línea 483: | ||
</ | </ | ||
- | === Ejemplo === | + | === Ejemplo |
hora.c | hora.c | ||
Línea 489: | Línea 502: | ||
t-> | t-> | ||
| | ||
- | printf(" | + | printf(" |
- | printf(" | + | printf(" |
printf(t-> | printf(t-> | ||
return 0; | return 0; | ||
} | } | ||
</ | </ |
procesos.1413253081.txt.gz · Última modificación: 2014/10/14 02:18 por lmateu