Herramientas de usuario

Herramientas del sitio


procesos

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
procesos [2014/10/14 13:14] – [Pipes] lmateuprocesos [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.  De ahí en adelante las modificaciones que haga el hijo en su memoria no se verán reflejadas en la memoria del padre, y viceversa.  Son espacios de direcciones independientes.   * Para el hijo se crea un nuevo espacio de direcciones de memoria que parte inicialmente con una copia de toda la memoria del padre.  De ahí en adelante las modificaciones que haga el hijo en su memoria no se verán reflejadas en la memoria del padre, y viceversa.  Son espacios de direcciones independientes.
-  * El hijo hereda una copia de todos los archivos abiertos por el padre.+  * 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 <unistd.h>   #include <unistd.h>
      
-  pid_t child; +  pid_t child= fork()
-  if ((child= fork()) != 0) { +  if (child==0) { 
-    /aquí se ejecuta el padre */ +    /aquí se ejecuta el hijo 
-    /* la variable "child" contiene el pid del hijo */ +    ... cuerpo del hijo ... 
-    ...+    exit(...); // termina el hijo entregando un código de retorno
   }   }
   else {   else {
-    /aquí se ejecuta el hijo *+    /aquí se ejecuta el padre 
-    ...+    // la variable "child" contiene el pid del hijo 
 +    ... código que se ejecuta en paralelo con el cuerpo del hijo ... 
 +    int status; 
 +    waitpid(child, &status, 0);  // espera hasta que el hijo termine 
 +    int rc= WEXITSTATUS(status); // código de retorno del hijo
   }   }
 +</code>
 +
 +==== 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); | Esta función no retorna |
 +| abort(); | El efecto de esta última llamada es indefinido, pero puede generar un core |
 +
 +Para matar a otro proceso se usa:
 +
 +<code>
 +  #include <sys/types.h>
 +  #include <signal.h>
 +
 +  pid_t pid;
 +  kill(pid, SIGKILL);
 </code> </code>
  
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: enterrarlos cuando mueren. Para esto, basta con que invoque la función wait en cualquier momento, pero es indispensable que sea informado que su hijo murió para que Linux pueda deshacerse por completo de él. Un hijo muerto pero no enterrado (cuyo padre nunca ha sido informado de su muerte vía wait) es un Zombie: un proceso que ya no existe pero no puede reciclarse por completo. En Unix, un padre es completamente independiente de sus hijos, pero debe cumplir con una responsabilidad fundamental: enterrarlos cuando mueren. Para esto, basta con que invoque la función wait en cualquier momento, pero es indispensable que sea informado que su hijo murió para que Linux pueda deshacerse por completo de él. Un hijo muerto pero no enterrado (cuyo padre nunca ha sido informado de su muerte vía wait) es un Zombie: un proceso que ya no existe pero no puede reciclarse por completo.
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#deberes_de_un_padre|este ejemplo]]. Para observar lo que sucede mientras el padre no ha invocado wait compile y ejecute [[start#deberes_de_un_padre|este ejemplo]].
 +
 +==== 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 ''ls | more''.
 +
 +Para crear un pipe se invoca:
 +
 +<code>
 +  #include <unistd.h>
 +  
 +  int fds[2];
 +  pipe(fds);
 +</code>
 +
 +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.  Este pipe sirve para comunicar unidireccionalmente ambos procesos.
 +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.  Después de llamar a particionar se llama a fork.  La parte inferior del arreglo se ordena en el proceso hijo
 +y la parte superior en el padre.  El ejemplo completo se encuentra
 +[[http://users.dcc.uchile.cl/~lmateu/CC3301/download/pquicksort.zip|acá]].
 +
 +<code>
 +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/pipe/socket */
 +    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, i, j);
 +    int fd[2];
 +    pipe(fd);
 +    pid_t pid= fork();
 +    if (pid==0) {
 +      close(fd[0]);
 +      quicksort(a, i, h-1);
 +      write(fd[1], &a[i], (h-i)*sizeof(int));
 +      exit(0);
 +    }
 +    else {
 +      close(fd[1]);
 +      quicksort(a, h+1, j);
 +      leer(fd[0], (char*)&a[i], (h-i)*sizeof(int));
 +      close(fd[0]);
 +      waitpid(pid, NULL, 0);
 +    }
 +  }
 +}
 +</code>
 +
 +Observe que si se reemplazara la llamada de la función leer por read, podría funcionar en algunas plataformas, pero sería incorrecto.  La llamada al sistema read no siempre entregará la cantidad de bytes pedidos.  Puede entregar menos bytes cuando se piden 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.  Si se escribe menos es porque ocurrió un error.  Yo hice el experimento en Linux y read siempre leyó la cantidad de bytes pedidos.
 +
 +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.  Use un pipe para que el hijo entregue sus resultados al padre.
 +
 +==== Ejercicio ====
 +
 +Resuelva la pregunta 1 partes a y b del [[http://users.dcc.uchile.cl/~lmateu/CC3301/controles/c3-142.pdf|control 3 de 2014/2]].  Pruebe su solución con el archivo [[http://users.dcc.uchile.cl/~lmateu/CC3301/download/mult.zip|mult.zip]].
  
 ==== 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 2: invocar ls ===
  
 <code> <code>
Línea 144: Línea 242:
   }   }
 </code> </code>
-==== Variables de ambiente ==== 
  
-El puntero environ apunta a un arreglo de strings de la forma: +=== Ejemplo 3: more ===
- +
-<code> +
-  name=value +
-</code> +
- +
-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: +
- +
-<code> +
-  #include <stdlib.h> +
-  char *p; +
-   +
-  p= getenv(name); +
-</code> +
- +
-El parámetro //name// apunta al nombre de la variable y //p// recibe el puntero a la línea //name=value// respectiva, o bien NULL si no existe. +
- +
-==== 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); | Esta función no retorna | +
-| abort(); | El efecto de esta última llamada es indefinido, pero puede generar un core | +
- +
-Para matar a otro proceso se usa: +
- +
-<code> +
-  #include <sys/types.h> +
-  #include <signal.h> +
- +
-  pid_t pid; +
-  kill(pid, SIGKILL); +
-</code> +
- +
-==== 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 ''ls | more''+
- +
-Para crear un pipe se invoca: +
- +
-<code> +
-  #include <unistd.h> +
-   +
-  int fds[2]; +
-  pipe(fds); +
-</code> +
- +
-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.  Este pipe sirve para comunicar unidireccionalmente ambos procesos. +
-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.  Después de llamar a particionar se llama a fork.  La parte inferior del arreglo se ordena en el proceso hijo +
-y la parte superior en el padre.  El ejemplo completo se encuentra +
-[[http://users.dcc.uchile.cl/~lmateu/CC3301/download/pquicksort.zip|acá]]. +
- +
-<code> +
-int leer(int fd, char *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/pipe/socket */ +
-    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, i, j); +
-    int fd[2]; +
-    pipe(fd); +
-    pid_t pid= fork(); +
-    if (pid==0) { +
-      close(fd[0]); +
-      quicksort(a, i, h-1); +
-      write(fd[1], &a[i], (h-i)*sizeof(int)); +
-      exit(0); +
-    } +
-    else { +
-      close(fd[1]); +
-      quicksort(a, h+1, j); +
-      leer(fd[0], (char*)&a[i], (h-i)*sizeof(int)); +
-      close(fd[0]); +
-      waitpid(pid, NULL, 0); +
-    } +
-  } +
-+
-</code> +
- +
-Observe que si se reemplazara la llamada de la función leer por read, probablemente funcionaría en algunos casos, pero sería incorrecto.  La llamada al sistema read no +
-siempre entregará la cantidad de bytes pedidos.  Puede entregar menos bytes cuando se piden +
-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.  Si se escribe menos es porque ocurrió +
-un error. +
- +
-=== Ejemplo 2: more ===+
  
 El siguiente programa filtra la salida por medio de more: El siguiente programa filtra la salida por medio de more:
Línea 347: Línea 328:
 Con la función system es trivial implementar ls_pipe_more.  La desventaja es que en Con la función system es trivial implementar ls_pipe_more.  La desventaja es que en
 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:
 +
 +<code>
 +  name=value
 +</code>
 +
 +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:
 +
 +<code>
 +  #include <stdlib.h>
 +  char *p;
 +  
 +  p= getenv(name);
 +</code>
 +
 +El parámetro //name// apunta al nombre de la variable y //p// recibe el puntero a la línea //name=value// respectiva, o bien NULL si no existe.
 +
 +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 413: 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 471: Línea 483:
 </code> </code>
  
-=== Ejemplo ===+=== Ejemplo 4: mostrar la hora ===
  
 hora.c hora.c
Línea 490: Línea 502:
           t->tm_mon+1, t->tm_mday, t->tm_year+1900);           t->tm_mon+1, t->tm_mday, t->tm_year+1900);
                  
-    printf("Da de la semana: %d\n", t->tm_wday+1); +    printf("Dia de la semana: %d\n", t->tm_wday+1); 
-    printf("Da del ao: %d\n", t->tm_yday+1);+    printf("Dia del anno: %d\n", t->tm_yday+1);
     printf(t->tm_isdst>0?"Horario de Verano\n":"Horario Normal\n");     printf(t->tm_isdst>0?"Horario de Verano\n":"Horario Normal\n");
     return 0;     return 0;
   }   }
 </code> </code>
procesos.1413292495.txt.gz · Última modificación: 2014/10/14 13:14 por lmateu