procesos unix ipcs

38
PROGRAMACIÓN DE SISTEMAS UNIX: COMUNICACIÓN ENTRE PROCESOS ÍNDICE. 1. PROCESOS. 1. Conceptos generales. 2. Ejecución de comandos. 1. Subrutina system . 2. Subrutinas exec . 3. Creación de procesos. 1. Subrutina fork . 2. Subrutinas wait y waitpid . 4. Terminación de un proceso. 1. Subrutina exit . 2. Subrutina atexit . 2. SEÑALES. 1. Conceptos generales. 2. Lista de las señales más importantes. 3. Capturar señales. 1. Subrutina signal : 2. Subrutina sigaction : 3. Subrutina kill : 4. Alarmas y temporizadores. 1. Subrutinas alarm y ualarm : 5. Tratamiento de errores. 1. Lista de errores más importantes. 2. Subrutina perror : 3. PIPES (TUBERÍAS). 1. Conceptos generales. 2. Redirección. 1. Subrutinas dup y dup2 : 2. Subrutina fcntl : 3. Comunicación entre procesos emparentados. 1. Subrutina pipe 4. Comunicación entre procesos no emparentados.

Upload: linguini-gusteau

Post on 02-Jan-2016

75 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Procesos Unix IPCs

PROGRAMACIÓN DE SISTEMAS UNIX:COMUNICACIÓN ENTRE PROCESOS

ÍNDICE.

1. PROCESOS. 1. Conceptos generales. 2. Ejecución de comandos.

1. Subrutina system . 2. Subrutinas exec .

3. Creación de procesos. 1. Subrutina fork . 2. Subrutinas wait y waitpid .

4. Terminación de un proceso. 1. Subrutina exit . 2. Subrutina atexit .

2. SEÑALES.

1. Conceptos generales. 2. Lista de las señales más importantes. 3. Capturar señales.

1. Subrutina signal : 2. Subrutina sigaction : 3. Subrutina kill :

4. Alarmas y temporizadores. 1. Subrutinas alarm y ualarm :

5. Tratamiento de errores. 1. Lista de errores más importantes. 2. Subrutina perror :

3. PIPES (TUBERÍAS).

1. Conceptos generales. 2. Redirección.

1. Subrutinas dup y dup2 : 2. Subrutina fcntl :

3. Comunicación entre procesos emparentados. 1. Subrutina pipe

4. Comunicación entre procesos no emparentados. 1. Subrutina mkfifo :

Lista de programas.

Page 2: Procesos Unix IPCs

system.c - Listar los procesos del usuario usando system. exec.c - Listar los procesos del usuario usando exec. fork.c - Ejecución conjunta de procesos padre e hijo. fork_huerf.c - Ejemplo de proceso huérfano. waitpid.c - Esperar la terminación de un proceso hijo. atexic.c - Ejecución de una rutina al salir de un programa. signal.c - Contar el número de CTRL-C en 15 segundos. kill.c - Ejecución con tiempo de espera usando kill. alarm.c - Esperar una alarma. dup2.c - Redirección usando dup2. pipe.c - Tubería sin nombre entre procesos padre e hijo. pipe_conec.c - Tubería entre 2 comandos usando pipe. lector_fifo.c - Tuberia con nombre usando mkfifo. escritor_fifo.c - Tuberia con nombre usando mkfifo. fifo - Carga los procesos lector y escritor en 2 o plano.

1. PROCESOS.

1,1. Conceptos generales.

Proceso: programa o comando en ejecución. Características:

Un proceso consta de código, datos y pila. Los procesos existen en una jerarquía de árbol (varios Hijos, un sólo padre). El sistema asigna un identificador de proceso (PID) único al iniciar el

proceso. El planificador de tareas asigna un tiempo compartido para el proceso según

su prioridad (sólo root puede cambiar prioridades).

Ejecución en 1er plano: proceso iniciado por el usuario o interactivo.

Ejecución en 2o plano: proceso no interactivo que no necesita ser iniciado por el usuario.

Demonio: proceso en 2o plano siempre disponible, que da servicio a varias tareas (debe ser propiedad del usuario root).

Proceso zombi: proceso parado que queda en la tabla de procesos hasta que termine su padre. Este hecho se produce cuando el proceso padre no recoge el código de salida del proceso hijo.

Proceso huérfano: proceso en ejecución cuyo padre ha finalizado. El nuevo identificador de proceso padre (PPID) coincide con el identificador del proceso init (1).

Page 3: Procesos Unix IPCs

1,2. Ejecución de comandos.

1,2,1. Subrutina system:

- Descripción: Llamada a un intérprete para ejecutar un comando. El proceso espera a que finalice la ejecución de la subrutina y devuelve la salida del programa ejecutado.

- Formato: #include <stdlib.h> int system (cadena) const char *cadena;

- Parámetro: cadena - Comando a ejecutar.

- Devuelve: Estado de salida del programa ejecutado. -1 o 127 en caso de error.

- Comentarios: a) Se crea un proceso hijo (fork) y se lanza (exec) /usr/bin/bsh, que interpreta el comando a ejecutar. b) Si la llamada se hace con camino seguro, la orden exec ejecuta el intérprete /usr/bin/tsh. c) Se ignoran las señales SIGINT y SIGQUIT y se bloquea la señal SIGCHLD. d) La salida de system no afecta a la salida de los procesos hijos del proceso ejecutor.

- Ejemplo:

/* system.c - Listar los procesos del usuario usando system. */#include <stdio.h>#include <stdlib.h>

int main () { int salida; /* Salida del comando */ char comando[100]; /* Comando a ejecutar */

printf ("Ejemplo de system.\n"); sprintf (comando, "/bin/ps -fu %s", getenv ("USER")); salida = system (comando); printf ("Salida del comando: %d\n", salida); exit (salida); }

Ejemplo de system. USER PID PPID TTY CMD ramon 3638 1 hft/0 -ksh ramon 10089 10600 hfp/0 /bin/ps -fu ramon

Page 4: Procesos Unix IPCs

ramon 10600 11623 hft/0 bsh bsh bsh ramon 11623 3638 hft/0 system.eSalida del comando: 0

Suponiendo que no existe el comando MUSHO y sustituyendo la ejecución de system por la siguiente línea, se obtiene la salida mostrada a continuación.

salida = system ("MUSHO BETI");

Ejemplo de system.bsh: MUSHO: no encontrado.Salida del comando: 256

1,2,2. Subrutinas exec>:

- Descripción: Ejecuta un nuevo programa en el mismo proceso. Se crea un proceso imagen sustituyendo el programa actual por el nuevo.

- Formatos: #include <unistd.h> int execl (camino, arg0 [, arg1, ...] , 0) const char *camino, *arg0, *arg1, ...; int execle (camino, arg0 [, arg1, ...] , 0, p_entorno) const char *camino, *arg0, *arg1, ...; char *const p_entorno[]; int execlp (fichero, arg0 [, arg1, ...] , 0) const char *fichero, *arg0, *arg1, ...; int execv (camino, val_args) const char *camino; char *const val_args[]; int execve (camino, val_arg, p_entorno) const char *camino; char *const val_args[], *p_entorno[]; int execvp (fichero, val_args) const char *fichero; char *const val_args[]; int exect (camino, val_arg, p_entorno) char *camino, *val_args, *p_entorno[];

- Sufijos: L - usa lista de parámetros, el último debe ser 0. V - usa matriz de parámetros generada previamente, el último debe ser 0. T - trazado del programa con ptrace (en desuso). E - usa la matriz de variables de entorno. P - búsqueda utilizando la variable PATH.

- Parámetros: camino - Camino completo del fichero ejecutable. fichero - Nombre del fichero ejecutable. argN - Argumento N-ésimo. val_args - Puntero a la matriz de argumentos. p_entorno - Puntero a la matriz del entorno.

Page 5: Procesos Unix IPCs

- Devuelve: -1, en caso de error.

- Comentarios: a) La rutina principal (main) de un programa C ejecutable recibe los siguientes parámetros: int main (cont_args, val_args, p_entorno) int cont_args; /* Contador de argumentos. */ char *val_args; /* Puntero a la matriz de argumentos. */ char *p_entorno; /* Puntero a la matriz del entorno. */

Las variables val_args y p_entorno son similares a las utilizadas en las subrutinas exec. b) Esta rutina principal llama a una subrutina de iniciación que construye la variable de entrono. Dicha variable global es accesible desde el programa declarándola de la siguiente manera: extern char **environ; Las subrutinas exec que no usan el parámetro p_entorno utilizan la variable environ. c) Los descriptores de ficheros abiertos se pasan al nuevo proceso imagen, excepto los que tengan activo el bit FD_CLOEXEC (ver fcntl). d) Las señales capturadas se reasignan a sus acciones por defecto; las ignoradas, continúan siendo ignoradas (ver sigaction). e) Si el nuevo proceso imagen tiene activo el bit SUID, la identificación efectiva de usuario (EUID) del nuevo proceso toma el valor del identificador del propietario. Idem, si tiene activo el bit SGID. f) Los identificadores reales de usuario y de grupo (RUID y RGID) mantienen el valor que tenían en el proceso llamador. g) El párrafo anterior puede aplicarse a ficheros remotos (previa traducción de los identificadores). h) El párrafo e) no afecta a las shell-scripts. i) El proceso nuevo mantiene las siguientes características del proceso llamador:

Identificadores de proceso (PID), de proceso padre (PPID) y de grupo de procesos (PGID).

Valores de prioridad (nice), de TTY y del bit de trazado. Directorio actual y directorio raíz. Máscara de ficheros, limites de longitud de ficheros, límites de recursos. Tiempos para activar alarmas y subrutinas times. Identificador de usuario de conexión.

- Ejemplo:

/* exec.c - Listar los procesos del usuario usando exec. */#include <stdio.h>#include <unistd.h>

int main () { int salida;/* Salida del comando */

Page 6: Procesos Unix IPCs

printf ("Ejemplo de exec.\n"); execl ("/bin/ps", "ps", "-fu", getenv ("USER"), 0); printf ("Salida del comando: %d\n", salida);

exit (salida);

}

Ejemplo de system. USER PID PPID TTY CMD ramon 3638 1 hft/0 -ksh ramon 10739 3638 hft/0 /bin/ps -fu ramon

Suponiendo que no existe el comando MUSHO y sustituyendo la ejecución de execl por la siguiente línea, se obtiene la salida mostrada a continuación.

salida = execl ("MUSHO", "BETI", 0);

Ejemplo de exec.Salida del comando: -1

1,3. Creación de procesos.

1,3,1. Subrutina fork:

- Descripción: Crea un nuevo proceso (hijo), copia casi exacta del proceso generador (padre).

- Formato: #include <unistd.h> pid_t fork ();

- Devuelve: 0 al proceso hijo y PID del hijo al proceso padre (-1, si error).

- Comentarios: a) La versión BSD (en la librería libbsd.a) es: int vfork (); b) Atributos que hereda el proceso hijo.

Entorno. Bit FD_CLOEXEC para cada descriptor de fichero. Señales capturadas. SUID y SGID. Estado de privilegios y prioridades. Librerías compartidas y segmentos de memoria compartida. PGID y TTYGID.

Page 7: Procesos Unix IPCs

Directorio actual y directorio raíz. Máscara y límites de medida para ficheros. Eventos y estado de auditoría. Estado de depuración.

c) Atributos diferenciadores entre padre e hijo: PID único. PPID distintos (el PPID del hijo coincide con el PID del padre). El proceso hijo tiene su propia copia de los descriptores de fichero del padre,

pero comparte con éste un puntero a fichero para cada descriptor del proceso padre.

Bloqueos de proceso, texto y datos no se heredan. Las subrutinas times se ponen a 0. Las alarmas pendientes toman su valor inicial. Se eliminan las señales pendientes para el proceso hijo.

- Ejemplos:

/* fork.c - Ejecución conjunta de procesos padre e hijo */#include <stdio.h>#include <unistd.h>main () { printf ("Ejemplo de fork.\n");

printf ("Inicio del proceso padre. PID=%d\n", getpid ());

if (fork() == 0)

{ /* Proceso hijo */

printf ("Inicio proceso hijo. PID=%d, PPID=%d\n",

getpid (), getppid ());

sleep (1);

}

else

{ /* Proceso padre */

Page 8: Procesos Unix IPCs

printf ("Continuación del padre. PID=%d\n", getpid ());

sleep (1);

}

printf ("Fin del proceso %d\n", getpid ());

exit (0);

}

Ejemplo de fork.Inicio proceso padre. PID=8153Inicio proceso hijo. PID=6618, PPID=8153Continuación proceso padre. PID=8153Fin del proceso 6618Fin del proceso 8153

- Ejemplo:

/* fork_huerf.c - Ejemplo de proceso huérfano *#include <stdio.h>#include <unistd.h>main () { printf ("Ejemplo de proceso huérfano.\n"); printf ("Inicio del proceso padre. PID=%d\n", getpid ()); if (fork () == 0)

{printf ("Inicio proceso hijo. PID=%d, PPID=%d\n",getpid (), getppid ());sleep (1);printf ("El proceso queda huérfano. PID=%d PPID=%d\n",getpid (), getppid ());}

elseprintf ("Concinuación del padre. PID=%d\n", getpid ());

printf ("Fin del proceso %d\n", getpid ()); exit (0); }

Ejemplo de proceso huérfano.Inicio proceso padre. PID=11330Inicio proceso hijo. PID=6467, PPID=11330Continuación proceso padre. PID=11330Fin del proceso 11330$punto indicativo> El proceso queda huérfano. PID=6467, PPID=1Fin del proceso 6467

Page 9: Procesos Unix IPCs

Notas: En el ejemplo, el proceso padre no espera la finalización del proceso hijo y termina

antes que éste. Cuando un proceso queda huérfano, el proceso de iniciación (init) se convierte en

su padre. Una vez que finaliza el proceso padre, se devuelve el control al intérprete de

comandos, de ahí que aparezca el mensaje del "punto indicativo". El proceso hijo no deberá mandar mensajes a la consola, como ocurre en este ejemplo.

1,3,2. Subrutinas wait y waitpid:

- Descripción: Espera a que pare o termine un proceso hijo, permitiendo obtener sus estados de salida. Una señal no bloqueada o no ignorada puede reactivar el proceso padre.

- Formato: #include <sys/wait.h> pid_t wait (estados) int *estados; pid_t wait ((void *) 0); pid_t waitpid (PID, estados, opciones) pid_t PID; int *estados, opciones;

- Parámetros:< PID - PID del proceso o grupo de proceso. Sus valores son:

-1: waitpid actúa igual que wait, esperando cualquier hijo. >0: PID de un proceso hijo determinado. 0: para cualquier hijo con el mismo grupo de procesos que el padre. <-1: para cualquier hijo cuyo grupo de proceso sea igaul al valor absoluto de

PID.

opciones - Máscara de opciones. Sus bits son: WNOHANG: evita la suspensión del padre mientras esté esperando a algún

hijo. WUNTRACED: el padre obtiene información adicional si el hijo recibe

alguna de las señales SIGTTIN, SIGTTOU, SIGSSTP o SIGTSTOP .

estados - Puntero a una tabla con los estados de salida de los procesos. - Devuelve:

0, si no ha terminado ningún proceso. - Macros:

WIFSTOPPED (estado) /* !=0, si estado es de un hijo parado */ pid_t estado; int WSTOPSIG (estado) /* Nú de señal que ha causado la parada */ pid_t estado; WIFEXITED (estado) /* !=0, si estado es de salida normal */ pid_t estado;

Page 10: Procesos Unix IPCs

int WEXITSTATUS (estado) /* 8 bits bajos del estado de salida */ pid_t estado; WIFSIGNALED (estado) /* !=0, si estado es de salida anormal */ pid_t estado; int WTERMSIG (estado) /* Nú de sañal que ha causado la slida */ pid_t estado;

- Cometarios: a) Estas subrutinas pueden verse afectadas por la señal SIGCHLD (ver sigaction). b) La subrutina wait espera la terminación de cualquier proceso hijo.

- Ejemplos:

/* waitpid.c - Esperar la terminación de un proceso hijo */#include <stdio.h>#include <signal.h>#include <sys/wait.h>

main () { pid_t id_padre; /* PID del proceso padre */ pid_t id_hijo; /* PID del proceso hijo */ int estado; /* Estado de salida */

printf ("Ejemplo de waitpid.\n"); printf ("Inicio proceso padre. PID=%d\n", getpid ()); id_padre = getpid (); if ((id_hijo = fork ()) == 0) { /* Proceso hijo */ printf ("Inicio proceso hijo. PID=%d, PPID=%d\n", getpid (), id_padre); sleep (3); printf ("Salida proceso hijo. PID=%d\n", getpid ()); exit (getpid () > id_padre); /* 1, si PID > PPID */ } else { signal (SIGINT, SIG_IGN); /* Ignorar CTRL-C */ while (waitpid (id_hijo, &estado, 0) != id_hijo); if (WIFSIGNALED (estado)) printf ("El proceso hijo ha recibido la señal %d\n", WTERMSIG (estado)); if (WIFEXITED (estado)) { printf ("Estado de salida del proceso hijo: %d\n", WEXITSTATUS (estado)); if (WEXITSTATUS (estado) == 1) printf ("PID hijo > PID padre.\n"); else printf ("PID padre > PID hijo.\n"); } printf ("Fin del proceso %d\n", getpid ()); exit (0); }

Page 11: Procesos Unix IPCs

Ejemplo de waitpid.Inicio proceso padre. PID=24213Inicio proceso hijo. PID=31638, PPID=24213Fin proceso hijo. PID=31638Estado de salida del proceso hijo: 1PID hijo > PID padreFin del proceso 24213

La salida siguiente muestra el efecto de generar una señal de interrupción pulsando [CTRL][C]. Dicha señal provoca la terminación automática del proceso hijo, mientras que el proceso padre la ignora (ver signal). Ejemplo de waitpid.Inicio proceso padre. PID=7240Inicio proceso hijo. PID=5705, PPID=7240^CEl proceso hijo ha recibido la señal: 2Fin del proceso 7240

1,4. Terminación de un proceso.

1,4,1. Subrutina exit:

- Descripción: Termina la ejecución de un proceso.

- Formato: #include <stdlib.h> void exit (estado) int estado;

- Parámetro: Estado de salida del proceso.

- Comentarios: a) El proceso de salida de un proceso es el siguiente:

Llamada a la función _cleanup para limpiar las áreas de E/S. Llamada a la subrutina especificada en la subrutina atexit. Llamada a la subrutina _exit para finalizar el proceso.

b) Si _cleanup no puede cancelar las peticiones de E/S asíncrona, la aplicación se bloquea hasta que se completen dichas peticiones. c) Se cierran todos los descriptores de fichero. d) Si el proceso padre está en espera (ver wait), se devuelve el valor de los 8 bits menos significativos del estado de salida. e) Se envía una señal SIGCHLD al proceso padre. La acción por defecto es ignorar esta señal. Si no se ignora, el proceso hijo puede quedar como proceso zombi. f) La salida de un proceso no provoca la terminación de sus hijos. El PPID de los hijos será el PPID del proceso init (1). g) Se eliminan los bloqueos de ficheros (ver fcntl). h) Si para un proceso perteneciente a un grupo huérfano, se envían las señales SIGHUP y SIGCONT a cada proceso del grupo de procesos huérfanos.

Page 12: Procesos Unix IPCs

1,4,2. Subrutina atexit:

- Descripción: Ejecuta una determinada función antes de la terminación del proceso.

- Formato: #include <sys/limits.h> int atexit (función) void (*función) (void);

- Parámetro: Puntero a la función llamada.

- Devuelve: 0: si no hay errores.

- Comentarios: a) La función se ejecuta si se ha completado con éxito la subrutina _cleanup.

- Ejemplo:

/* atexic.c - Ejecución de una rutina al salir de un programa */#include <stdio.h>#include <sys/limits.h>

int bucle=0; /* Contador de vueltas del bucle */

void salida (); /* Prototipo de la función de salida */

int main () { int n;

atexit (salida); printf ("Ejemplo de atexit.\n"); for (bucle=1; bucle<255; bucle++)

{n=rand ();printf ("%d-%d\t", bucle, n);if (n > 30000) exit (1);}

exit (0); }

void salida () { printf ("El bucle ha dado %d vueltas.\n"); printf ("Hasta luega Lucas.\n"); }

Ejemplo de atexit.1-16838 2-5758 3-10113 4-17515 5-31051El bucle ha dado 5 vueltas.Hasta luego Lucas.

Page 13: Procesos Unix IPCs

2. SEÑALES.

2,1. Conceptos generales.

Señal: Evento que debe ser procesado y que puede interrumpir el flujo normal de un programa.

Capturar una señal: Una señal puede asociarse con una función que procesa el evento que ha ocurrido.

Ignorar una señal: El evento no interrumpe el flujo del programa. Las señales SIGINT y SIGSTOP no pueden ser ignoradas (ver tabla de señales).

Acción por defecto: Proceso suministrado por el sistema para capturar la señal (ver tabla de señales).

Alarma: Señal que es activada por los temporizadores del sistema.

Error: Fallo o acción equivocada que puede provocar la terminación del proceso.

Error crítico: Error que provoca la salida inmediata del programa.

2,2. Lista de las señales más importantes.

Núm. Nombre Comentarios

1 SIGHUP Colgar. Generada al desconectar el terminar.

2 SIGINT Interrupción. Generada por teclado.

3 SIGQUIT1 Salir. Generada por teclado.

4 SIGILL1 Instrucción ilegal. No se puede recapturar.

5 SIGTRAP1 Trazado. No se puede recapturar.

6 SIGABRT1 Abortar proceso.

8 SIGFPE1 Excepción aritmética, de coma flotante o división por cero.

9 SIGKILL1 Matar proceso. No puede capturarse, ni ignorarse.

10 SIGBUS1 Error en el bus.

11 SIGSEGV1 Violación de segmentación.

12 SIGSYS1 Argumento erróneo en llamada al sistema.

13 SIGPIPE Escritura en una tubería que otro proceso no lee.

14 SIGALRM Alarma de reloj.

15 SIGTERM Terminación del programa.

Page 14: Procesos Unix IPCs

16 SIGURG2 Urgencia en canal de E/S.

17 SIGSTOP3 Parada de proceso. No puede capturarse, ni ignorarse.

18 SIGTSTP3 Parada interactiva. Generada por teclado.

19 SIGCONT4 Continuación. Generada por teclado.

20 SIGCHLD2 Parada o salida de proceso hijo.

21 SIGTTIN3 Un proceso en 2o plano intenta leer del terminal.

22 SIGTTOU3 Un proceso en 2o plano intenta escribir en el terminal.

23 SIGIO2 Operación de E/S posible o completada.

24 SIGXCPU Tiempo de UCP excedido.

25 SIGXFSZ Excedido el límite de tamaño de fichero.

30 SIGUSR1 Definida por el usuario número 1.

31 SIGUSR2 Definida por el usuario número 2.

34 SIGVTALRM Alarma de tiempo virtual.

36 SIGPRE Excepción programada. Definida por el usuario.

Notas sobre la acción por defecto para la señal.

1. Generar un fichero core. 2. Ignorar la señal. 3. Parar el proceso que recibe la señal. 4. Reiniciar o continuar el proceso que recibe la señal.

Las señales comprendidas entre la 37 y la 58 (ambas inclusive) están reservadas por el sistema.

El rango de señales en el UNIX de Berkeley (BSD) es de 1 a 31.

2,3. Capturar señales.

2,3,1. Subrutina signal:

- Descripción: Asocia una acción determinada con una señal.

- Formato: #include <signal.h> void (*signal (señal, acción)) () int señal; void (*accón) ();

- Parámetros: señal: Número de señal, excepto SIGKILL.

Page 15: Procesos Unix IPCs

acción: Puntero a la rutina asociada con la señal o uno de los valores: SIG_DFL: acción por defecto para dicha señal. SIG_IGN: ignorar la señal,

- Devuelve: Valor de la acción anteriormente asociada; -1, en caso de error.

- Comentarios: a) Existe una versión de la subrutina signal compatible con el UNIX de Berkeley (BSD). b) No se permiten máscaras de bloqueo de señales y se activa el bit SA_OLDSTYLE (ver > sigaction ).

- Ejemplo:

/* signal.c - Contar el número de CTRL-C en 15 segundos */#include <stdlib.h>#include <signal.h>

int numcortes=0; /* Contador de CTRL-C */int enbucle=1; /* Controlador de salida del bucle de espera */

void alarma (); /* Captura la señal de alarma SIGALRM */void cortar (); /* Captura la señal de interrupción SIGINT */

int main () { signal (SIGINT, cortar); signal (SIGALRM, alarma); printf ("Ejemplo de signal.\n"); printf ("Pulsa varias veces CTRL-C durante 15 segundos.\n"); alarm (15); while (bucle); signal (SIGINT, SIG_IGN); printf ("Has intentado cortar %d veces.\n", numcortes); printf ("Hasta luego Lucas.\n"); exit (0); }

void alarma () { signal (SIGALRM, SIG_IGN); bucle=0; /* Salir del bucle */ printf ("¡Alarma!\n"); }

void cortar () { signal (SIGINT, SIG_IGN); printf ("Has pulsado CTRL-C\n"); numcortes++; signal (SIGINT, cortar); }

Page 16: Procesos Unix IPCs

Ejemplo de signal.Pulsa CTRL-C varias veces durante 15 segundo.^CHas pulsado CTRL-C^CHas pulsado CTRL-C^CHas pulsado CTRL-C^CHas pulsado CTRL-C^CHas pulsado CTRL-C¡Alarma!Has intentado cortar 5 veces.Hasta luego Lucas.

2,3,2. Subrutina sigaction:

- Descripción: Especifica la acción a realizar cuando un proceso recibe una señal.

- Formato: #include <signal.h> int sigaction (señal, acción, acción_salida) () int señal; struct sigaction *accón, *acción_salida;

- Parámetros: señal: Número de señal, excepto SIGKILL. acción: Acción especificada cuando se recibe la señal. acción_salida: Acción a realizar cuando termine la función sigaction.

- Campos de la estructura sigaction: void (*sa_handler) (); Puntero a la rutina asociada con la señal o uno de los valores:

SIG_DFL: acción por defecto para dicha señal. SIG_IGN: ignorar la señal.

sigset_t sa_mask; Especifica la máscara de las señales que serán bloqueadas durante la captura de la señal especificada. int sa_flags;

SA_ONSTACK: La captura de la señal se realiza en una pila de señales en vez de en la pila del proceso.

SA_OLDSTYLE: El parámetro señal se asocia con la ación por defecto (SIG_DFL) antes de llamar a la rutina de captura (no recomendable, la señal puede recurrir).

SA_NOCLDSTOP: Evita que el proceso padre reciba una señal SIGCHLD cuando para el proceso hijo.

- Devuelve: 0, si es correcta; -1, en caso de error.

- Comentarios: a) Las siguientes funciones pueden ser llamadas sin problemas desde una rutina de captura de señales: _exit access alarm chdir chmod chown close creat dup dup2 exec fcntl

Page 17: Procesos Unix IPCs

fork fstat getegid geteuid getgid getgroups getpgrp getpid getppid getuid kill link lseek mkdir mkfifo open pause pipe readx rename rmdir setgid setpgrp setuid sigaction sigaddset sigdelset sigfillset sigismember signal sigpending sigprocmask sigsuspend sleep statx tcdrain tcflow tcflush tcgetattr tcgetpgrp tcsendbreak tcsetattr tcsetpgrp time times umask uname unlink ustat utime write b) Una vez que una acción está instalada para una señal, continúa hasta que haya otra llamada a sigaction o se llame a la subrutina exec, excepto si se ha activado el bit SA_OLDSTYLE. c) Las señales SIGKILL y SIGSTOP no pueden ser ignoradas.

2,3,3. Subrutina kill:

- Descripción: Envía una señal a un proceso.

- Formato: #include <signal.h> int kill (proceso, señal) pid_t proceso; int señal;

- Parámetros: proceso: Identificador del proceso o del grupo de procesos que recibirá la señal. Puede tomar los siguientes valores:

>0: Identificador de un único proceso. 0: Procesos cuyo identificador del grupo de procesos sea igual al PID del

proceso actual. <-1: Procesos cuyo identificador del grupo de procesos sea igaul al valor

absoluto de proceso.

señal: Número de señal enviada. - Devuelve:

0, si se ha completado correctamente; -1, en caso de error. - Comentarios:

a) La subrutina raise envía una señal al proceso actual. #include <sys/signal.h> int raise (señal) int señal; Este código es equivalente al mostrado a continuación: error = kill (getpid (), señal); b) La subrutina killpg envía una señal a un grupo de procesos. Esta subrutina es compatible con el UNIX de Berkeley (librería libbsd.a). #include <signal.h> int killpg (int grupo_procesos, int señal);

Page 18: Procesos Unix IPCs

El código anterior equivale al mostrado a continuación: if (grupo_procesos < 0) { errno = ESRCH; return (-1); }return (kill(-grupo_procesos, señal));

c) Para enviar una señal a otro proceso deben coincidir el identificador de usuario (UID) real o efactivo de ambos procesos, o que el proceso emisor tenga prioridad de usuario root.

- Ejemplo:

/* kill.c - Ejecución con tiempo de espera usando kill */#include <stdlib.h>#include <signal.h>

int espera; /* Tiempo de espera */

void hijo (); /* Controlador de fin de proceso hijo */

int main (int contargs, char *args[]); { pid_t pid;

if (contargs < 3) { printf ("Formato: %s segundos comando [opciones].\n", args[0]); exit (1); } printf ("Ejemplo de kill.\n"); printf ("Ejecución con tiempo de espera.\n"); signal (SIGCHLD, hijo); pid = fork (); if (pid == 0) { execvp (args[2]; &args[2]); perror (args[0]); } else { espera = atoi (args[1]); sleep (espera); printf ("El hijo %d ha excedido el tiempo de %d s.\n",

pid, espera); signal (SIGCHLD, SIG_IGN); kill (pid, SIGINT); } exit (1); }

void espera () { int id_hijo, est_hijo;

Page 19: Procesos Unix IPCs

id_hijo = wait (&est_hijo); printf ("El hijo %d ha terminado antes de %d s.\n",

id_hijo, espera); exit (0); }

$ kill.e 3 wc kill.cEjemplo de kill.Ejecución de un comando con tiempo de espera.45 132 1065 kill.cEl hijo 10489 ha terminado antes de 3 s.

$ kill.e 3 sleep 5Ejemplo de kill.Ejecución de un comando con tiempo de espera.El hijo 10851 ha excedido el tiempo de espera de 3 s.

2,4. Alarmas y temporizadores.

2,4,1. Subrutinas alarm y ualarm:

- Descripción: Genera alarmas de reloj (señal SIGALRM) para el proceso actual.

- Formato #include <unistd.h> unsigned int alarm (segundos) unsigned int segundos; unsigned int ualarm (valor, intervalo) unsigned int valor, intervalo;

- Parámetros: segundos: Número de segundos para enviar al proceso la señal SIGALRM. valor: Número de señales generadas. intervalo: Intervalo (en ms.) entre las señales.

- Devuelve: alarm devuelve el número de segundos que restan para generar la señal. ualarm devuelve el número de microsegundos que restan hasta la próxima señal.

- Comentarios: a) Sólo puede generarse una única alarma (no son aplilables). b) El parámetro intervalo no puede ser menor que 10 para un usuario sin privilegios. c) Estas 2 subrutinas son compatibles con las primeras versiones del AIX, con UNIX System V y con UNIX de Berkeley (BSD). En AIX, se han programado como llamadas a la subrutina incinterval.

- Ejemplo:

/* alarm.c - Esperar una alarma */#include <stdlib.h>#include <unistd.h>

Page 20: Procesos Unix IPCs

int main () { printf ("Una alarma en 3 segundos.\n"); alarm (3); printf ("Esperando...\n"); while (1); printf ("Esta línea no se ejecutará nunca.\n"); exit (0); }

Una alarma en 3 segundos.Esperando...Alarm clock

2,5. Tratamiento de errores.

2,5,1. Lista de errores más importantes.

Núm. Nombre Descripción

1 EPERM Operación no permitida.

2 ENOENT El archivo o directorio no existe.

3 ESRCH El proceso no existe.

4 EINTR Llamada al sistema interrumpida.

5 EIO Error de E/S.

6 ENXIO No existe dispositivo o dirección.

7 E2BIG Lista de argumentos demasiado larga.

8 ENOEXEC Error en formato de ejecución.

9 EBADF Descriptor de fichero erróneo.

10 ECHILD No existe el proceso hijo.

11 EGAIN Recurso no disponible temporalmente.

12 ENOMEM No hay suficiente espacio de memoria.

13 EACCES Permiso denegado.

14 EFAULT Dirección de memoria errónea.

15 ENOTBLK Se necesita un fichero de bloques.

16 EBUSY Recurso ocupado.

17 EEXIST Fichero existente.

18 EXDEV Enlace impropio.

19 ENODEV Dispositivo inexistente.

20 ENOTDIR No es un directorio.

21 EISDIR Es un directorio.

Page 21: Procesos Unix IPCs

22 EINVAL Argumento no válido.

23 ENFILE Demasiados ficheros abiertos en el sistema.

24 EMFILE Demasiados ficheros abiertos.

26 ETXBUSY Fichero de texto ocupado.

27 EFBIG Fichero demasiado largo.

28 ENOSPC No queda espacio en el dispositivo.

29 ESPIPE Búsqueda no válida.

30 EROFS Fichero sólo de lectura.

32 EPIPE Tubería rota.

33 EDOM Error de dominio matemático.

34 ERANGE Resultado fuera de rango.

78 ETIMEDOUT Excedido tiempo de conexión (NFS).

88 EDQUOT Cuota de disco excedida.

Nota: No se tratan aquí los errores relativos a comunicaciones, ni a sockets.

2,5,2. Subrutina perror:

- Descripción: Escribe un mensaje explicando un error.

- Formato: #include <errno.h> void perror (cadne) char *cadena;

- Parámetro: Cadena de caracteres que explica el error.

- Variables globales de errno.h: extern int errno; Número de error. extern char *sys_errlist[]; Tabla con la descripción de los errores del sistema.

- Comentarios: a) Se imprime en la salida normal con un formato equivalente a la siguiente orden: printf ("%s: %s\n", cadena, sys_errlist[errno]);

3. PIPES (TUBERÍAS).

3,1. Conceptos generales.

Descriptor de fichero:

Page 22: Procesos Unix IPCs

Número entero positivo usado por un proceso para identificar un fichero abierto. Esta traducción se realiza mediante una tabla de descriptores de fichero, ubicado en la zona de datos del proceso.

Descriptores reservados: 0: entrada normal (stdin). 1: salida normal (stdout). 2: salida de error (stderr).

Redirección: Establecer copias del descriptor de ficheros de un archivo para encauzar las operaciones de E/S hacia otro fichero.

Tubería: Mecanismo de intercomunicación entre procesos que permite que 2 o más procesos envíen información a cualquier otro.

Tubería sin nombre: Enlace de comunicación unidireccional, capaz de almacenar su entrada (hasta 4 KB en BSD o hasta 40 KB en System V).

Tuberías nombradas (FIFO): Permiten una comunicación menos restringida, ya que las colas FIFO existen en el sistema de archivos hasta que son borradas. Características:

Permite comunicar procesos no emparentados. Tiene una entrada en el sistema de archivos. Usa una política de colas "primero en llegar, primero en servirse". Sólo disponible en UNIX System V.

3,2. Redirección.

3,2,1. Subrutinas dup y dup2:

- Descripción: Duplica un descriptor de fichero.

- Formatos: #include <unistd.h> #include <fcntl.h> #include <sys/types> int dup (desc_abierto) int desc_abierto; int dup2 (desc_abierto, desc_nuevo) int desc_abierto, desc_nuevo;

- Parámetros: desc_abierto: Descriptor de fichero abierto. desc_nuevo: Nuevo descriptor de fichero devuelto por dup2.

- Devuelve: dup devuelve el menor descriptor de fichero que esté libre. dup2 devuelve el valor de desc_nuevo.

Page 23: Procesos Unix IPCs

Ambas subrutinas devuelven el valor -1 en caso de error. - Comentarios:

a) Las subrutinas dup y dup2 son equivalentes a la subrutina fcntl de la siguiente forma: dup: fcntl (desc_abierto, F_DUPFD, 0);

dup2: close (desc_nuevo); fcntl (desc_abierto, F_DUPFD, desc_nuevo); b) Puede redirigirse hacia un fichero cualquier descriptor especial.

- Ejemplo:

/* dup2.c - Redirección usando dup2 */#include <stdlib.h>#include <unistd.h>#include <fcntl.h>

int main (int contargs, char *args[]) { int desc_fich;

if contargs < 3) { printf ("Formato: %s fichero comando [opciones].\n", args[0]); exit (1); } printf ("Ejemplo de redirección.\n"); desc_fich = open (args[1], O_CREAT|O_TRUNC|O_WRONLY, 0); dup2 (desc_fich, 1); /* Redirige la salida normal */ close (desc_fich); execvp (args[2], &args[2]; /* Ejecuta comando */ exit (1); }

$ dup2.e dup2.sal ls *.cEjemplo de redirección.

$ chmod 600 dup2.sal; cat dup2.salalarm.c atexit.c dup2.c escritor_fifo.cexec.c fork.c fork_huerf.c kill.clector_fifo.c pipe.c pipe_conec.c signal.csystem.c waitpid.c

3,2,2. Subrutina fcntl:

- Descripción: Realiza operaciones de control sobre ficheros abiertos, tales como:

duplicar el descriptor, poner o leer características del descriptor, poner o leer estado del fichero, gestionar bloqueos de registros, gestionar la propiedad de la E/S asíncrona,

Page 24: Procesos Unix IPCs

cerrar varios ficheros.

- Formato: #include <unistd.h> #include <fcntl.h> #include <sys/types> int fcntl (descriptor, comando, argumento) int descriptor, comando, argumento);

- Parámetros: descriptor: Descriptor del fichero. comando: Operación ha realizar. argumento: Parámetro del comando.

- Devuelve: Valor devuelto por el comando; -1, en caso de error.

- Operaciones: F_DUPFD: Obtener el menor descriptor de fichero disponible que sea mayor que

el parámetro descriptor. Mantiene el mismo puntero y las mismas características del fichero original.

F_GETFD: Obtener características del descriptor. F_SETFD: Poner características del descriptor. F_GETFL: Obtener estado del fichero. F_SETFL: Poner estado del fichero. F_GETLK: Obtener información de bloqueo. F_SETLK: Poner bloqueo. F_SETLKW: Poner bloqueo en una zona bloqueada. F_GETOWN: Obtener PID (>0) o PGID (<0) del proceso que recibe las señales

SIGIO o SIGURG. F_SETOWN: Poner PID (>0) o PGID (<0) del proceso gestor de la E/S asíncrona. F_CLOSEM: Cierra todos los descriptores desde descriptor hasta el valor máximo

(OPEN_MAX). - Características del descriptor de ficheros:

FD_CLOEXEC: Indica si el descriptor se cerrará ante una función exec. - Estados del modo de acceso al fichero:

O_RDONLY: Abierto sólo para lectura. O_RDWR: Abierto para lectura y escritura. O_WRONLY: Abierto sólo para escritura.

- Bloqueos: F_RDLCK: Bloqueo de lectura (compartido). F_WRLCK: Bloqueo de escritura (exclusivo). F_UNLCK: Sin bloqueo.

- Comentarios: a) Un bloqueo de lectura evita que otros procesos activen bloqueos de lectura en cualquier zona del área protegida. Sí se permiten otros bloqueos de lectura en toda el área o en partes de ella. b) Un bloqueo de escritura evita que otros procesos bloqueen dicha zona.

Page 25: Procesos Unix IPCs

c) Los "abrazos mortales" en un sistema distribuido no siempre son detectables. El programa deberá usar temporizadores para poder liberar sus bloqueos.

3,3. Comunicación entre procesos emparentados.

3,3,1. Subrutina pipe

- Descripción: Crea un canal de comunicación entre procesos emparentados.

- Formato: #include <unistd.h> int pipe (descriptores) int descriptores[2];

- Parámetros: Tabla que recibirá los descriptores de entrada y de salida de la tubería.

- Devuelve: 0, si se ha completado correctamente; -1, en caso de error.

- Comentarios: a) descriptores[0] se abre para lectura y descriptores[1], para escritura. b) La operación de lectura en descriptores[0] accede a los datos escritos en descriptores[1] como en una cola FIFO (primero en llegar, primero en servirse),

- Ejemplos:

/* pipe.c - Tubería sin nombre entre procesos padre e hijo */#include <stdlib.h>#include <unistd.h>

#define LEER 0#define ESCRIBIR 1

int main () { int descr[2]; /* Descriptores de E y S de la turbería */ int bytesleidos; char mensaje[100],

*frase="Veremos si la transferecia es buena.";

printf ("Ejemplo de tuberÍa entre padre e hijo.\n"); pipe (descr); if (fork () == 0) { close (descr[LEER]); write (descr[ESCRIBIR], frase, strlen(frase)); close (descr[ESCRIBIR]); } else { close (descr[ESCRIBIR]); bytesleidos = read (descr[LEER], mensaje, 100);

Page 26: Procesos Unix IPCs

printf ("Bytes leidos: %d\n"); printf ("Mensaje: %s\n", bytesleidos, mensaje); close (descr[LEER]); } }

Ejemplo de tubería entre padre e hijo.Bytes leídos: 36Mensaje: Veremos si la transferencia es buena.

/* pipe_conec.c - Tubería entre 2 comandos usando pipe. */#include <stdlib.h>#include <unistd.h>

#define LEER 0#define ESCRIBIR 1

int main (int contargs, char *args[]) { int descr[2]; /* Descriptores de E y S de la turbería */

if (contargs != 3) { printf ("Formato: %s comando_ent comando_sal.\n", args[0]); exit (1); } pipe (descr); if (fork () == 0) { close (descr[LEER]); dup2 (descr[ESCRIBIR], 1); close (descr[ESCRIBIR]); execlp (args[1], args[1], NULL); perror (args[0]); } else { close (descr[ESCRIBIR]); dup2 (descr[LEER], 0); close (descr[LEER]); execlp (args[2], args[2], NULL); perror (args[0]); } }

$ pipe_conec.e ls wc37 37 354

3,4. Comunicación entre procesos no emparentados.

3,4,1. Subrutina mkfifo:

- Descripción:

Page 27: Procesos Unix IPCs

Crea un canal FIFO de comunicaciones entre procesos que no necesitan estar emparentados.

- Formato: #include <sys/mode.h> int mkfifo (camino, modo) const char *camino; int modo;

- Parámetros: camino: Camino completo del fichero FIFO. modo: Tipo de fichero y permisos de acceso.

- Devuelve: 0, si se ha completado correctamente; -1, en caso de error.

- Comentarios: a) La subrutina mkfifo es un interfaz de la rutina mknod para crear colas FIFO, las cuales no necesitan privilegios especiales del sistema. b) El comando ls -al identifica una tubería nombrada con el carácter descriptor p

- Ejemplos:

/* lector_fifo.c - Tuberia con nombre usando mkfifo */#include <stdlib.h>#include <fcntl.h>#include <sys/mode.h>

int linea (int df, char *cad);

int main () { int descr; char cadena[100];

unlink ("tuberia"); mkfifo ("tuberia", 0); chmod ("tuberia", 460); descr = open ("tuberia", O_RDONLY); while (linea (descr, cadena)) printf ("%s\n", cadena); close (descr); pritnf ("Fin del lector.\n"); }

int linea (int df, char *cad) { int n;

do { n = read (df, cad, 1); } while (n > 0 && *cad++ != NULL); return (n > 0); }

Page 28: Procesos Unix IPCs

#include <stdlib.h>

#include <fcntl.h>

#include <sys/mode.h>

int main ()

{

int descr, longmens, i;

char mensaje[100];

sprintf (mensaje, "Un saludo desde el proceso %d", getpid ());

longmens = strlen (mensaje) + 1;

do

{ /* intentar la conexion */

descr = open ("tuberia", O_WRONLY);

if (descr == -1) sleep (1);

}

while (descr == -1);

for (i=0; i<3; i++)

{

write (descr, mensaje, longmens);

sleep (3);

}

Page 29: Procesos Unix IPCs

close (descr);

printf "Fin del escritor %d\n", getpid ());

}

#!/bin/ksh

# fifo - Carga los procesos lector y escritor en 2o plano.

lector_fifo.e &

escritor_fifo.e &

escritor_fifo.e &

$ fifo$Un saludo desde el proceso 11996Un saludo desde el proceso 10971Un saludo desde el proceso 11996Un saludo desde el proceso 10971Un saludo desde el proceso 11996Un saludo desde el proceso 10971Fin del escritor 10971Fin del escritor 11996Fin del lector