Ing. Ignacio Javier Bonelli
O como comunicar programas o procesos
dentro de una misma máquina
Nota: Esto lo veremos desde un punto de vista Unix clásico.
Hay librerías más modernas como D-Bus.
Links: D-Bus y otros IPCs // D-Bus Tutorial
Missing man package?
apt-get install glibc-doc
¿Mas info?
Macros definidas en el archivo header <signal.h>
para las señales mas comunes:
man 7 signal
Signal Value Action Comment
----------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating-point exception
SIGKILL 9 Term Kill signal
...
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
...
SIGALRM 14 /* alarm clock */
SIGCONT 19 /* continue a stopped process */
SIGCHLD 20 /* to parent on child stop or exit */
POSIX.1-1990, POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD, ...
Signals can be numbered from 0 to 31.
#include <signal.h>
#include <sys/types.h>
sighandler_t signal(int signum, sighandler_t handler);
int kill(pid_t pid, int sig);
Código:
Más información de "GNU C Library" sección "Signal Handling"
Ver ejemplo TCP/IP con fork : Servidor y Cliente
¿Que pasa si corremos 3 hijos y el padre
cuando terminan los hijos?
Ver con:
watch -n 1 "ps elf | grep tcp | grep -v grep"
tcp/ip + fork (sin wait) => ¡hijos zombies!
Proceso Zombie : Muere el hijo y el padre no se entera
man 2 signal
Que opciones tenemos:
No nos ocupamos y nos queda un proceso Zombie: zombie1.c
Formas de evitarlo:
Bonus: Usamos un signal handler haciendo algo zombie5.c y cerramos al padre zombie6.c
typedef void (*sighandler_t)(int);
Más información de "GNU C Library" sección "Signal Handling"
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
Es como crear un archivo. Solo que un proceso lo escribe y el segundo proceso lo lee. Siempre en orden FIFO.
Luego para usarlo es igual que con archivos/sockets, usaremos: open(), read() y write().
Una sola dirección, lectura "destructiva".
Ejemplo 1:
Ejemplo 2:
Datos curiosos...
Para leer mas...
Implemente una función que lea un archivo de texto desde el final hacia el principio y lo escriba en una FIFO, que debe ser creada por la función.
El prototipo de la función es el siguiente:
int enviarInvertido (char *fifoName, char *fileName);
Donde:
Y devuelve un número positivo indicando la cantidad de caracteres escritos en la fifo y un número negativo en caso de error.
Dos opciones:
map or unmap files or devices into memory
shared_memory2.cmmap: Todos pueden leer y escribir al mismo tiempo...
¡Falta coordinación!
From https://www.softprayog.in/programming/interprocess-communication-using-posix-shared-memory-in-linux
Corriendo procesos en paralelo que son mas livianos y flexibles que los creados por fork.
Nos da un nuevo proceso que es una copia del actual. Tiene una "copia" de la memoria original. A medida que los nuevos procesos avanzan los valores de cada uno en memoria cambian, pero el ejecutable es el mismo. Los procesos/tareas no comparten memoria, la única forma de compartir algo es usar IPCs.
Un proceso puede tener multiples threads, cada una ejecutandose en paralelo dentro del mismo contexto (memoria). Todos los recursos son compartidos, usan la misma memoria. Se deben usar mecanismos de lock y sincronización para evitar problemas.
Conclusión: Se comparte la memoria de datos...
¡Pero no comparten la pila!
(ejemplo sencillo)
Formas de sincronizar threads:
/* Creando un thread */
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
/* Volviendo a unirse */
int pthread_join(pthread_t thread, void **retval);
/* Retornar el pthread ID actual */
pthread_t pthread_self(void);
/* ¿Son tid1 y tid2 el mismo thread? */
/* pthread_t no tiene forma portable de ser comparado */
int pthread_equal(pthread_t tid1, pthread_t tid2);
/* Salir del thread/función en el que estamos */
void pthread_exit(void *retval);
pthread_create():
Los mutual exclusion locks (mutexes) son uno de los métodos usados para serializar la ejecución de threads. Nos permiten asegurar que en un momento solo un thread se estará ejecutando.
En ciertas situaciones esto es importante y permite preservar la parte del programa que no puede trabajar en paralelo.
Su función principal es evitar el acceso a recursos compartidos.
pthread_mutex_t : Puntero a extructura mutex
/* Inicia y destruye un registro de mutex */
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/* Bloquea y desbloquea un/los threads */
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
(primer ejemplo)
Salida:
Job 1 started
Job 2 started
Job 2 finished
Job 2 finished
"Job 2 finished" se repite dos veces... ¿Que pasó con count?
Se "serializan" los procesos en la etapa Mutex.
Solo uno ejecuta el bloque mutex al mismo tiempo.
(evitando problemas...)
Salida:
Job 1 started
Job 1 finished
Job 2 started
Job 2 finished
Ahora si funciona sin que se sobre-escriban variables.
En threads_mutex_v3.c tenemos algo más que el mutex en el proceso. Hay cosas que si corren en paralelo.
Mutex se podría ver como un hermano menor de los semáforos.
FORK & PID
ps elf | head -n1
ps elf | grep tcp | grep -v grep
watch -n 1 "ps elf | grep tcp | grep -v grep"
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 19398 17451 20 0 84348 1032 - Sl+ pts/7 0:00 ./threads_m2
Threads & LWP
ps -eLf | head -n1
ps -eLf | grep threads_m2 | grep -v grep
watch -n 1 "ps -eLf | grep threads | grep -v grep"
UID PID PPID LWP C NLWP STIME TTY TIME CMD
ignacio 19926 17451 19926 0 3 09:30 pts/7 00:00:00 ./threads_m2
ignacio 19926 17451 19927 1 3 09:30 pts/7 00:00:00 ./threads_m2
ignacio 19926 17451 19928 0 3 09:30 pts/7 00:00:00 ./threads_m2
Nota: Este ejemplo usa pthread_detach(), explicado en el siguiente slide.
int pthread_detach(pthread_t thread);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
NOTA: Estas funciones también nos permiten utilizar threads y que se cierren adecuandamente sin el uso del join() que es bloqueante.
int pthread_tryjoin_np(pthread_t thread, void **retval);
int pthread_timedjoin_np(pthread_t thread, void **retval,
const struct timespec *abstime);
NOTA - These functions are nonstandard GNU extensions; hence the suffix "_np" (nonportable) in the names.
Sumando y restando una matriz
man 3 exit
┌──────────┬───────────────┬─────────────────────┐
│Interface │ Attribute │ Value │
├──────────┼───────────────┼─────────────────────┤
│exit() │ Thread safety │ MT-Unsafe race:exit │
└──────────┴───────────────┴─────────────────────┘
The exit() function uses a global variable that is not protected,
so it is not thread-safe.
Para leer mas...
sem_t : Estructura del tipo semaforo
/* Inicializa un semaforo */
int sem_init(sem_t *sem, int pshared, unsigned int value);
/* Bloquea un semaforo */
int sem_wait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
/* Desbloquea un semaforo */
int sem_post(sem_t *sem);
/* Destruye/libera un semaforo */
int sem_destroy(sem_t *sem);
Semáforos: Productor / Consumidor
Ejemplo usando semaforos y threads: sem_thread.c
Librerias a usar:
<sys/types.h> , <sys/ipc.h> y <sys/msg.h>
/* Convierte un nombre/ruta a un ID manejable en System V */
key_t ftok(const char *pathname, int proj_id);
/* Obtiene el ID de una message queue System V */
int msgget(key_t key, int msgflg);
/* Envia un mensaje de una cola de mensajes identificada por key */
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/* Obtiene un mensaje de una cola de mensajes identificada por key */
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/* Permite controlar una cola de mensajes System V */
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Ejemplo sencillo IPC:
Mensajes por Beej: kirk.c y spock.c
El comando "ipcs" lista los IPC abiertos, si lo corremos mientras los procesos kirk/spock corren reporta sobre la actividad.
Más ejemplos de la catedra en:
./files/clase26/ejemplos_catedra/*
En recuperatorios y finales se tomó Named Pipes (o FIFOs).
Librería Sockets Info1: sock-lib.h y sock-lib.c.
Más ejemplos de la catedra en: ./files/clases_integradoras/*