Sebastián Sánchez Prieto

Slides:



Advertisements
Presentaciones similares
Laboratorio Lenguaje de Programación ING2101
Advertisements

Unidad 15 Características de C++ no relacionadas con el DOO.
DATSI, FI, UPM José M. Peña Programación en C DATSI, FI, UPM José M. Peña Programación en C.
Tabla de Contenido Concurrencia.
Memoria Compartida Llave de acceso Segmento Compartido 1234 estructura
LENGUAJES PARALELOS Chang y Smith (1990) clasificación:
Universidad Tecnológica Nacional Facultad Regional Buenos Aires Ingeniería en Sistemas de Información Funciones Útiles fork() Sistemas Operativos.
UNIX COMP 240.
Sistema operativo Componentes de un sistema operativo
I.T.E.S.R.C. Romina Tamez Andrea Martínez Ma. De Lourdes Solís
T5-multithreading SO-Grado Q1.
Informática II Clase 12: Flujos Diego Fernando Serna Restrepo
Subsistemas De un Sistema Operativo Celeste Domínguez Romo
Programación, Algoritmos y Estructuras de Datos
Funciones. Programación, Algoritmos y Estructuras de Datos.
Sistemas Operativos Unidad II Procesos.
Modelo de procesos de dos estados
Informática II Prof. Dr. Gustavo Patiño MJ
Informática II 1 Diego Fernando Serna RestrepoSemestre 2011/2.
Comunicación entre procesos en Linux
Tareas en Linux · TASK_RUNNING (0): Indica que el proceso en cuestión se está ejecutando o listo para ejecutarse. En este segundo caso, el proceso dispone.
Comunicación y sincronización entre procesos
SISTEMAS OPERATIVOS UNIDAD 1..
IMPLEMENTACIÓN DEL ALGORITMO DE PRIORIDADES DUALES EN RT-LINUX
JULIO BERNA. Los procesos de usuario emiten peticiones de entrada/salida al sistema operativo. Las peticiones se procesan de forma estructurada en las.
Funciones en lenguaje C
Funciones y procedimientos
Programación I Teoría VI: Recursividad
Multiprogramación Procesos Cecilia Hernández
Unidad I Java y C++ : Similitudes y diferencias
Introducción a los SSOO Sebastián Sánchez Prieto.
Archivos Programación.
Signal, kill, sigaction y máscara de señales
Semana 5 Subprogramas..
Ing Florencia Ferrigno Tecnicas Digitales 3
Unidad III Administración de procesos
Hilos - Lightweight process - Procesos ligeros
Archivos.
Introducción al Minikernel
Bases de datos en la Web n Las bases de datos permiten almacenar de una forma estructurada y eficiente toda la información de un sitio web n Ventajas –Proporcionar.
Estructura del sistema operativo
Programación de Memoria Compartida
Sistemas en tiempo real (STR)
SISTEMAS OPERATIVOS.
Date | Place Señales Práctica I. Name | Company Objetivos Captura de una señal y tratamiento de la misma Checkpointing.
Concepto de proceso Sebastián Sánchez Prieto. Procesos 2  S2P, OGP & IGT Definición de proceso Un proceso es un programa en ejecución Componentes:
Introducción a los Sistemas Operativos
Archivos Programación I MC Beatriz Beltrán Martínez.
Introducción al tiempo real en sistemas empotrados
Gestión de procesos Sistemas Operativos Edwin Morales
Unidad II Introducción a la programación en C++
Bibliotecas Nacen a partir de la necesidad de la compilación por módulos. Lo cual consiste en separar el programa principal de aquellas funciones que se.
Elementos básicos del lenguaje
Materia: Técnicas Digitales 3
Introducción a los SOs.
Integrante: Yohandry Cueto Carnet: # Contenido Kernel Linux Limitaciones del Kernel Linux Kernel Mach Que es Hurd Arquitectura Ventajas Distribuciones.
CONCEPTOS FUNDAMENTALES DEL NIVEL DEL SISTEMA OPERATIVO
El núcleo del sistema operativo
- 1 - Sistema Embebidos: Propiedades de los Lenguajes y SDL Agustín J. González 1s07 Se ha tomado como base el material generado por Peter Marwedel de.
CARACTERÍSTICAS Es un lenguaje de programación estructurado de propósito general. Está estrechamente asociado al sistema operativo UNIX, ya que el propio.
LIA. SUEI CHONG SOL, MCE..  1.- SOFTWARE BÁSICO O DE SISTEMA. Conjunto de programas imprescindibles para el funcionamiento del sistema.  2.- SOTWARE.
UTFSM - Sistemas Operativos
Definición, Funciones, Características, Categorias
Programación de Clientes Especialidad en Base de Datos.
Elementos y tipos de sistemas operativos
ELEMENTO DE COMPETENCIA 3
Lenguaje de Programación II PLAN DE EVALUACIÓN CONTENIDO MODALIDAD DE EVAL. PONDERACIÓN INTRODUCCIÓN BÁSICA AL LENGUAJE DE PROGRAMACIÓN.
Estructura del sistema operativo
SISTEMAS ELECTRÓNICOS 3ºGIERM1 1. Introducción 2. Tipos de datos 3. Estructuras típicas de programación 4. Manejo de bits Tema 7. Programación de microcontroladores.
El núcleo del sistema operativo
Transcripción de la presentación:

Sebastián Sánchez Prieto POSIX IEEE 1003 Sebastián Sánchez Prieto

Introducción POSIX son un conjunto de normas IEEE/ISO que definen la interfaz entre las aplicaciones y el SSOO POSIX: Portable Operating System Interface + UniX Su objetivo es conseguir la portabilidad de las aplicaciones a nivel de código fuente La aplicación puede desarrollarse en C, Ada, Fortran y otros lenguajes Las normas definen los servicios que cada sistema operativo particular puede incluirlos o no La denominación oficial es IEEE Std. 1003, e ISO/IEC-9945

POSIX: estándares base (C) POSIX 1, 1a Unix básico sin tiempo real POSIX 1b, 1d, 1i, 1j Extensiones de tiempo real POSIX 1c Extensiones de threads POSIX 1e Seguridad POSIX 1f Network File System POSIX 1g Servicios de red (sockets) POSIX 1h Tolerancia a fallos POSIX 21 Comunicaciones de TR

POSIX: interfaz otros lenguajes POSIX 5, 5a, 5b Interfaces con Ada POSIX 9 Interfaces con Fortran 77

POSIX: perfiles de entornos POSIX 10 Supercomputadores POSIX 13 Tiempo real POSIX 14 Multiprocesadores POSIX 18 Estación de trabajo POSIX

POSIX de tiempo real ¿para qué? Existe gran diversidad de sistemas de TR: Núcleos de TR (LynxOS, VxWorks, QNX, etc.) Ejecutivos Ada En sistemas grandes: VMS y otros Era necesario definir un estándar que asegurase la portabilidad de aplicaciones a nivel de código fuente entre diferentes entornos de tiempo real

Perfiles de entornos de aplicación PSE50: sistema de tiempo real mínimo Sólo procesos ligeros, sin gestión de memoria ni archivos ni terminal PSE51: controlador de tiempo real Añade el terminal y sistema de archivos PSE52: sistema de tiempo real dedicado Soporta procesos pesados y gestión de memoria PSE53: sistema de tiempo real generalizado Sistema completo con todos los servicios

Características de los perfiles Sistema de archivos Múltiples procesos Threads Sistema mínimo NO NO SÍ Controlador SÍ NO SÍ Sistema dedicado NO SÍ SÍ Sistema multipropósito SÍ SÍ SÍ

POSIX: Unix básico POSIX 1 define los servicios ofrecidos por Unix: Gestión de procesos: Creación y destrucción Sincronización Temporización Gestión de archivos Creación y borrado de archivos y directorios Trabajo con archivos especiales Protección de la información Entrada-salida y control

POSIX: Extensiones de TR Para obtener determinismo en el comportamiento Planificación Gestión de memoria Señales Relojes y temporizadores Para facilitar la concurrencia Sincronización Memoria compartida Colas de mensajes Entrada-salida síncrona y asíncrona

POSIX: Extensiones de threads POSIX 1c incorpora funciones para trabajar con hilos. Incluye: Gestión de hilos Sincronización de hilos Planificación de hilos Creación y destrucción de hilos Añade reentrada a algunas funciones de POSIX1 POSIX 1c puede hacer uso de funciones incluidas en POSIX 1 y POSIX 1b

POSIX: Procesos y planificación

Definiciones Programa Archivo ejecutable residente en un dispositivo de almacenamiento permanente Se ejecuta por medio de la llamada exec() Proceso Es un programa en ejecución Los procesos se crean con la llamada fork() Servicios del sistema operativo Invocados por medio de funciones POSIX no diferencia entre llamadas al sistema y procedimientos de biblioteca

Estructura de un proceso en C Proceso de usuario Funciones main() Rutina de inicio exit() _exit() exec() Llamada al sistema Núcleo

Ejemplo /************************************** * Programa que imprime todos los * * argumentos de línea de órdenes * **************************************/ int main (int argc, char *argv[]) { int i; for (i=0; i<argc; i++) printf (“%s\n”, argv[i]); exit(0); } /* Fin de main */

Características de un proceso Cada proceso se caracteriza por una estructura de datos conocida como tabla de control de tarea que contiene: Identificador de proceso o PID Identificador de proceso padre o PPID Identificador de usuario o UID Identificador de grupo o GID Puntero a la memoria asignada Puntero a los recursos ... Cada proceso dispone de un espacio de direccionamiento virtual independiente

Creación de procesos: fork() La llamada fork() crea una copia (hijo) del proceso que la invoca El hijo hereda del padre: Estado Semáforos Objetos de memoria Política de planificación, etc. El hijo no hereda: El PID Alarmas y temporizadores Operaciones de E/S asíncronas

Interfaz de fork() Definida en: Valores de retorno: #include <sys/types.h> pid_t fork(void); Valores de retorno: Al padre: el PID del hijo Al hijo: cero En caso de error: devuelve -1 y la variable errno contiene el valor asociado al error

Ejemplo con fork() #include <sys/types.h> int main(void) { pid_t id; id = fork(); if (id == -1) { perror (“Error en el fork”); exit (1); } if (id == 0) { while (1) printf (“Hola: soy el hijo\n”); } else { while (1) printf (“Hola: soy el padre\n”); } /* Fin de main */

Ejecución de programas: exec() La familia de llamadas exec() se emplea para cargar la imagen de un proceso desde un archivo a memoria La nueva imagen se carga encima de la del proceso que invoca la llamada, machacándolo El nuevo proceso hereda: El PID y el PPID del proceso original La política de planificación Las alarmas y señales pendientes

Prototipos de la familia exec() Definidos en: #include <unistd.h> int execl (const char *path, const char *arg, ...); int execlp (const char *file, int execle (const char *path, const char *arg, ..., char * const envp[]); int execv (const char *path, char const *argv[]); int execvp (const char *file, char const *argv[]); Valores de retorno: En caso de error exec() devuelve -1

Ejemplo con exec() #include <unistd.h> int main(void) { int ret; char *arg[3]; arg[0] = “ls”; arg[1] = “-l”; arg[2] = (char *)0; printf (“Allá va!\n”); ret = execv (“/bin/ls”, arg); if (ret == -1) { perror (“Error en el exec”); exit (1); } } /* Fin de main */

Finalización de procesos: exit() La llamada exit() provoca la finalización del proceso que la invoca Esta llamada nunca retorna Cuando un proceso termina, sus hijos no mueren y suelen ser adoptados por el proceso init Prototipo definido en: #include <unistd.h> void _exit (int status); El valor status es retornado al padre si es que existe

Espera por procesos: wait() La llamada wait() permite que un proceso quede esperando a que sus hijos terminen El padre puede conocer el valor de retorno de cada hijo Prototipos definido en: #include <sys/types.h> #include <sys/wait.h> pid_t wait (int *status); pid_t waitpid(pid_t pid, int *status, int options);

Ejemplo con wait() #include <sys/types.h> #include <sys/wait.h> int main(void) { pid_t id; int estado; id = fork(); if (id == -1) { perror (“Error en el fork”); exit (1); }

Continuación del ejemplo if (id == 0) { printf (“Soy el hijo\n”); sleep(3); printf (“Hijo: despierta y finaliza\n”); exit(0); } else { printf (“Soy el padre y ahora espero ...\n”); wait (&estado); printf (“Padre: el hijo terminó con estado = %d\n”, estado); exit (0); } } /* Fin de main */

Planificación Los mecanismos clásicos de planificación no son válidos Es necesario evitar el indeterminismo POSIX 1b utiliza una planificación expulsora con prioridades fijas (32 como mínimo) con tres políticas diferentes: SCHED_FIFO FIFO para tareas de igual prioridad SCHED_RR Round-Robin en tareas con la misma prioridad. El quanto es fijo SCHED_OTHER Definido por la realización concreta

Planificación (continuación) Los parámetros de planificación y los prototipos de las funciones se encuentran en: #include <sched.h> Parámetros de planificación: struct sched_param { int sched_priority; } Definir política y parámetros (hay que ser root): int sched_setscheduler (pid_t pid,int policy, const struct sched_param *param);

Planificación (continuación) Leer la política y los parámetros: int sched_getscheduler (pid_t pid); int sched_getparam (pid_t pid, struct sched_param *param); Ceder el procesador: int sched_yield (void); Leer los límites de los parámetros: int sched_get_priority_max (int policy); int sched_get_priority_min (int policy); int sched_rr_get_interval (pid_t pid, struct timespec *interval);

Ejemplo #include <sched.h> #include <sys/types.h> int main(void) { pid_t pid; struct sched_param parametros; int i, max_prio; pid = getpid(); max_prio = sched_get_priority_max(SCHED_FIFO); parametros.sched_priority = max_prio; sched_setscheduler(pid, SCHED_FIFO, &parametros); for (i=0; i<100000000; i++); } /* Fin de main */

POSIX: Gestión de memoria

Introducción La memoria virtual introduce NO determinismo POSIX proporciona la posibilidad de bloquear memoria para evitar la aleatoriedad Los procesos a pesar de tener un espacio de direccionamiento disjunto pueden compartir objetos de memoria La compartición se realiza mapeando la zona de memoria que deseamos compartir en los espacios de direccionamiento virtuales de cada proceso La compartición se realiza a través de páginas de modo que el tamaño de una zona proyectada es un múltiplo del tamaño de la página

Bloqueo de memoria Los procesos pueden bloquear todas sus páginas en memoria para evitar el intercambio con el disco #include <sys/mman.h> int mlockall (int flags); El valor de flags puede ser: MCL_CURRENT: afecta a las páginas actuales MCL_FUTURE: afecta a las páginas futuras Para liberar todas las páginas del proceso: int munlockall (void);

Bloqueo de memoria Un proceso pueden bloquear también un rango de su espacio de direccionamiento int mlock (const void *addr, size_t len); Para desbloquear un rango de direcciones: int munlock (const void *addr, size_t len);

Memoria compartida Proceso 1 Proceso 2 Memoria física M. Compartida

Proyección de objetos en memoria La llamada mmap() permite proyectar objetos en memoria Estos objetos pueden ser compartidos Función mmap(): void *mmap (void *addr, size_t len, int prot, int flags, int fildes, off_t off); El objeto queda identificado por fildes len y off son la longitud y el offset del objeto en bytes respectivamente addr es la dirección donde deseamos proyectar el objeto preferiblemente. Es sólo una indicación

Proyección de objetos en memoria La dirección real donde se proyecta el objeto es devuelta por mmap()y depende del valor de flags: MAP_FIXED: addr se interpreta de forma exacta, sin este flag si addr vale NULL, el sistema elige la dirección MAP_SHARED: los cambios son compartidos MAP_PRIVATE: no se comparten los cambios prot especifica el tipo de acceso: PROT_READ: derecho de lectura PROT_WRITE: derecho de escritura PROT_EXEC: derecho de ejecución PROT_NONE: sin derechos

Proyección de objetos en memoria Para eliminar la proyección de un objeto en memoria emplearemos la función munmap() int munmap (void *addr, size_t len);

Objetos de memoria compartida Para abrir un objeto de memoria compartida: #include <sys/mman.h> int shm_open (const char *path, int oflag, mode_t mode ); Se establece una conexión entre el path que identifica al objeto y el descriptor devuelto oflag determina el modo de acceso mode determina los derechos de acceso si creamos un nuevo objeto Es recomendable por razones de portabilidad que el path comience con el carácter /

Objetos de memoria compartida Fijar el tamaño de un objeto de memoria compartida int ftruncate (int fildes, off_t length); Para borrar un objeto de memoria compartida: int shm_unlink (const char *path);

POSIX: Relojes y temporizadores

Introducción El reloj sirve para medir el paso del tiempo Tick: Unidad del tiempo El número de ticks por segundo se puede conocer con sysconf (_SC_CLK_TCK) Resolución: Mínimo intervalo de tiempo que un reloj puede medir La Época: CUT (Coordinated Universal Time) 0 h 0 m 0s del 1 de enero de 1970

Introducción Temporizador Es un objeto que puede avisar a los procesos si ha transcurrido cierta cantidad de tiempo o se ha alcanzado cierta hora Cada temporizador está asociado a un reloj Reloj del sistema Mide los segundos desde La Época Mantiene la hora Reloj de tiempo real Se usa para timeouts y temporizadores

Reloj del sistema Leer la hora: #include <time.h> time_t time (time_t *t); time() devuelve los segundos transcurridos desde La Época Si t es distinto de NULL en él se devuelve la hora también Alarma: #include <unistd.h> unsigned int alarm (unsigned int seconds); Cuando transcurren los segundos especificados se envía la señal SIGALRM

Ejemplo #include <signal.h> #include <stdio.h> #include <unistd.h> #include <time.h> void manejador (int senal) { time_t seg; printf (“Recibida la señal de alarma ... \n”); seg = time (NULL); printf (“Segundos desde La Época: %d\n”, seg); exit (0); }

Ejemplo (continuación) main() { struct sigaction accion; time_t seg; accion.sa_flags = 0; accion.sa_handler = manejador; sigemptyset (&accion.sa_mask); sigaction (SIGALRM, &accion, NULL); seg = time (NULL); printf (“Segundos desde La Época: %d\n”, seg); alarm (3); while (1); } /* Fin de main */

Reloj de tiempo real La estructura timespec: typedef struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ } timespec_t; Tiempo = tv_sec * 109 + tv_nsec

Manejo de relojes Cambio de hora: Obtención de la hora: #include <time.h> int clock_settime(clockid_t clock_id, const struct timespec *tp); Obtención de la hora: int clock_gettime(clockid_t clock_id, struct timespec *tp); Resolución del reloj: int clock_getres(clockid_t clock_id, struct timespec *res); clockid_t debe valer CLOCK_REALTIME para TR

Ejemplo #include <time.h> main() { struct timespec stime; clock_getres (CLOCK_REALTIME, &stime); printf (“Segundos: %d\n”, stime.tv_sec); printf (“Nanosegundos: %ld\n”, stime.tv_nsec); } /* Fin de main */

Temporizadores Se utilizan para generar señales en ciertos momentos o para ejecutar acciones periódicas ¿Cómo crear un temporizador? #include <signal.h> #include <time.h> timer_create(clockid_t clock_id,struct sigevent *evp, timer_t *timerid); sigevent indica el modo de aviso: ninguno, señal o crear y ejecutar un hilo timerid es el identificador devuelto El temporizador inicialmente no está activo

Temporizadores ¿Cómo borrar un temporizador? timer_delete (timer_t timerid); ¿Cómo activar un temporizador? int timer_settime (timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); Estructura itimerspec: it_interval: periodo it_value: tiempo de expiración Si it_value vale 0 el temporizador es desactivado Si it_value > 0 el temporizador se activa en el instante especificado

Temporizadores Si el temporizador ya estaba activado, se reactiva flag indica si el temporizador es absoluto o relativo Si flag = TIMER_ABSTIME, la primera expiración será cuando el reloj valga it_value Si no se especifica nada, se espera hasta empezar el tiempo definido en it_value ¿Cómo leer el valor de un temporizador? int timer_gettime (timer_t timerid, struct itimerspec *value);

Ejemplo #include <time.h> #include <signal.h> void manejador(int senal) { struct timespec stime; clock_gettime (CLOCK_REALTIME, &stime); printf (“Repeticion -> Segundos: %d\t”, stime.tv_sec); printf (“Nanosegundos: %ld\n”, stime.tv_nsec); } /* Fin de manejador */

Ejemplo main() { struct sigaction accion; struct sigevent evento; timer_t idtemp; struct itimerspec itspec; struct timespec stime; accion.sa_flags = 0; accion.sa_handler = manejador; sigemptyset (&accion.sa_mask); sigaction (SIGUSR1, &accion, NULL); evento.sigev_signo = SIGUSR1; evento.sigev_notify = SIGEV_SIGNAL;

Ejemplo timer_create(CLOCK_REALTIME, &evento,&idtemp); itspec.it_value.tv_sec=5;/*Activación en 5 s*/ itspec.it_value.tv_nsec = 0; itspec.it_interval.tv_sec = 1; itspec.it_interval.tv_nsec = 0; timer_settime (idtemp, 0, &itspec, NULL); clock_gettime (CLOCK_REALTIME, &stime); printf (“Comienzo -> Segundos: %d\t”, stime.tv_sec); printf (“Nanosegundos: %ld\n”, stime.tv_nsec); while(1); } /* Fin de main */

Ejecución Comienzo -> Segundos: 921550145 Nanosegundos: 828803000 Repetición -> Segundos: 921550150 Nanosegundos: 828918000 Repetición -> Segundos: 921550151 Nanosegundos: 828883000 Repetición -> Segundos: 921550152 Nanosegundos: 828877000 Repetición -> Segundos: 921550153 Nanosegundos: 828891000 Repetición -> Segundos: 921550154 Nanosegundos: 828876000 Repetición -> Segundos: 921550155 Nanosegundos: 828876000 Repetición -> Segundos: 921550156 Nanosegundos: 828877000 Repetición -> Segundos: 921550157 Nanosegundos: 828878000 Repetición -> Segundos: 921550158 Nanosegundos: 828894000 Repetición -> Segundos: 921550159 Nanosegundos: 828880000 Repetición -> Segundos: 921550160 Nanosegundos: 828885000

sleep() Se emplea para dormir a un proceso o a un hilo hasta que transcurran lo segundos especificados unsigned int sleep (unsigned int seconds); Existe un sleep() de alta resolución int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); Con nanosleep() se duerme hasta que transcurre el intervalo especificado o hasta que se recibe una señal rqtp es el tiempo que vamos a dormir rmtp es el tiempo que falta por dormir si se retorna por la activación de una señal

POSIX: Sincronización

Semáforos Es un tipo de dato protegido que se utiliza para acceso exclusivo a recursos compartidos y para sincronización Introducido por Dijkstra a mediados de los 60 Si su valor es cero, el semáforo no está disponible Si su valor es positivo, está disponible Operaciones sobre semáforos: P (semáforo); V (semáforo); Existen semáforos con y sin nombre

Funciones asociadas Iniciar un semáforo sin nombre: #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); sem identifica al semáforo pshared determina si sólo puede ser utilizado por hilos del mismo proceso (=0) o se puede compartir entre procesos (!=0) value es el valor inicial del semáforo Destruir un semáforo sin nombre int sem_destroy(sem_t *sem);

Funciones asociadas Iniciar un semáforo con nombre: sem_t *sem_open (const char *name, int oflag, ... /* mode_t mode, int value */); La función devuelve un puntero al semáforo name debe tener el formato “/name” oflag indica las opciones O_CREAT: si el semáforo no existe, se crea. En este caso se requieren los permisos de acceso (mode) y el valor inicial (value) O_EXCL: si el semáforo existe se produce un error Cerrar un semáforo con nombre int sem_close(sem_t *sem);

Funciones asociadas Borrar un semáforo con nombre: Operación P: int sem_unlink (const char *name); Operación P: int sem_wait(sem_t *sem); Si el semáforo está libre, lo toma, en caso contrario, el proceso se bloquea Operación P (con consulta): int sem_trywait(sem_t *sem); Toma el semáforo si está libre, en caso contrario devuelve error

Funciones asociadas Operación V (liberar un semáforo): int sem_post (sem_t *sem); Leer el valor de un semáforo: int sem_getvalue(sem_t *sem, int *sval);

Ejemplo #include <semaphore.h> #include <sys/types.h> #define SEM “/usr/people/chan/src/semaforo” main() { sem_t *sem; pid_t id; int i; sem = sem_open (SEM, O_CREAT, 666, 0); if ((int)sem == -1) { perror (“Error en sem_open”); } id = fork(); if (id == -1) { perror (“Error en el fork”);

Ejemplo exit (1); } if (id == 0) { for (i=1; i<20; i++) { printf (“Hijo %d\n”, i); sem_post (sem); sleep (1); } else { sem_wait (sem); printf (“Padre %d\n”, i); } /* Fin de main */

POSIX: Mensajes

Definición Es una información que se transfiere entre diferentes procesos o hilos mediante su inserción o extracción de una cola de mensajes Cada mensaje lleva asociada una prioridad Los mensajes se extraen de la cola de mensajes por orden de prioridad (no FIFO) Las colas de mensajes se identifican por medio de un nombre o un descriptor de cola de mensajes El envío de los mensajes puede ser bloqueante o no bloqueante en función de si el buffer está lleno o vacío

Atributos de las colas Estructura mq_attr Está declarada en <mqueue.h> typedef struct mq_attr { long mq_flags; /*O_NONBLOCK -> no bloqueante*/ long mq_maxmsg; /*número máximo de mensajes*/ long mq_msgsize; /*tamaño máximo del mensaje*/ long mq_curmsgs; /*actual número de mensajes*/ } mq_attr_t;

Funciones asociadas Abrir una cola de mensajes: mqd_t mq_open (const char *mq_name, int oflag, ... /* mode_t mode, struct mq_attr *mq_attr */); La función devuelve un descriptor de cola mq_name debe ser del tipo “/name” oflag indica el modo de apertura Con O_CREAT mode especifica los derechos y mq_attr los atributos de creación

Funciones asociadas Cerrar una cola de mensajes: mqd_t mq_close (mqd_t mqd); Borrar una cola de mensajes: int mq_unlink (const char *mq_name); Definir los atributos de una cola: int mq_setattr (mqd_t mqd, struct mq_attr *mqstat, struct mq_attr *omqstat); Obtener los atributos de una cola: int mq_getattr (mqd_t mqd, struct mq_attr *mqstat);

Funciones asociadas Enviar un mensaje: int mq_send (mqd_t mqd, const char *msgptr, size_t msglen, unsigned int msg_prio); *msgptr es el puntero al mensaje msglen es su longitud msg_prio es la prioridad del mensaje Recibir un mensaje: int mq_receive (mqd_t mqd, char *msgptr, size_t msglen, unsigned int *msgprio);

Funciones asociadas Avisar de la llegada de un mensaje: int mq_notify (mqd_t mqd, const struct sigevent *notification); cuando llega el mensaje se avisa al proceso del evento sólo se puede avisar a un proceso si el proceso está esperando con mq_receive, la notificación no se produce

Ejemplo #include <mqueue.h> #include <fcntl.h> #include <sys/types.h> #define MQ “/usr/people/chan/src/mesgq” main() { struct mq_attr qattr; mqd_t qfd; pid_t id; int i; unsigned int Prio; char Mensaje[20];

Ejemplo qattr.mq_maxmsg = 32; qattr.mq_msgsize = 20; qfd = mq_open (MQ, O_CREAT|O_RDWR, 666, &qattr); if (qfd == -1) { perror (“Error en mq_open”); } id = fork(); if (id == -1) { perror ("Error en el fork"); exit (1);

Ejemplo if (id == 0) { for (i=1; i<20; i++) { printf (“Hijo %d\n”, i); mq_send (qfd, “Hola”, 5, 1); sleep (1); } } else { mq_receive (qfd, Mensaje, sizeof (Mensaje), &Prio); printf (“Padre %d - Mensaje %s\n”, i, Mensaje); } /* Fin de main */

POSIX 1c: Threads

Introducción Un thread es un flujo de control perteneciente a un proceso (a veces se habla de tareas con threads) Se les suele denominar también procesos ligeros, hebras, hilos, etc. La sobrecarga debida a su creación y comunicación es menor que en los procesos pesados Cada hilo pertenece a un proceso pesado Todos los hilos comparten su espacio de direccionamiento Cada hilo dispone de su propia política de planificación, pila y contador de programa

Introducción Sistema Operativo Hardware Cada hilo tiene su propio identificador tid que sólo es válido para hilos del mismo proceso Una aplicación con hilos puede beneficiarse de un procesamiento paralelo real en sistemas multiprocesador Hilos Procesos pesados Sistema Operativo Hardware

Operaciones con hilos Creación y destrucción Sincronización Gestión de prioridades Gestión de señales Gestión de memoria Se pueden utilizar todas las funciones incluidas en POSIX.1 y POSIX.1b La interfaz de hilos POSIX es pthreads, aunque existen otras bibliotecas de hilos

Operaciones asociadas Crear un nuevo hilo: int pthread_create (pthread_t *thread, pthread_attr_t *attr, void *(*start)(void *), void *arg); En thread devuelve el identificador de hilo attr es un puntero a los atributos de la tarea El tercer argumento en un puntero a la función que ejecutará el hilo arg es un puntero a los argumentos del hilo Finalizar un hilo: void pthread_exit(void *retval);

Ejemplo #include <pthread.h> void *Hilo (void *arg) { printf (“%s\n”, (char *)arg); pthread_exit (0); } /* Fin de Hilo */ main() { pthread_t th1, th2; pthread_create (&th1, NULL, Hilo, “Hilo 1”); pthread_create (&th2, NULL, Hilo, “Hilo 2”); sleep(5); puts (“Adios: Hilo principal”); } /* Fin de main */

Atributos de los threads Los atributos definibles son: Tamaño de la pila Dirección de la pila Control de devolución de recursos Los atributos se crean con: int pthread_attr_init(pthread_attr_t *attr); Los atributos se destruyen con: int pthread_attr_destroy(pthread_attr_t *attr);

Definición y obtención de attr int pthread_attr_setstacksize(pthread_attr_t *attr, size_t size); int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *size); int pthread_attr_setstackaddr(pthread_attr_t *attr, void *addr); int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **addr); int thread_attr_setdetachstate(pthread_attr_t *attr, int detach); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detach);

Devolución de recursos Los hilos pueden operar en dos modos diferentes para controlar la devolución de recursos: Detached: opera de modo autónomo, cuando termina devuelve sus recursos (identificador, pila, etc.) Joinable: en su terminación mantiene sus recursos hasta que otro hilo invoca a pthread_join()

Devolución de recursos Los recursos de una tarea joinable se liberan cuando esperamos con pthread_join() int pthread_join(pthread_t thread, void **retval); Si la tarea opera en modo detached, el propio hilo al terminar libera sus recursos. Para convertir a un hilo en detached, si no se hizo en su inicio: int pthread_detach(pthread_t thread);

Ejemplo #include <pthread.h> void *Hilo (void *arg) { printf (“%s\n”, (char *)arg); sleep(3); pthread_exit (NULL); } /* Fin de Hilo */ main() { pthread_t th1, th2; void *st1; pthread_create (&th1, NULL, Hilo, "Hilo 1"); pthread_join (th1, (void **) &st1); printf (“Retorno del hilo: %d\n”, st1); } /* Fin de main */

Identificación ¿Cómo obtener el identificador del hilo? pthread_t pthread_self(void); ¿Cómo comparar identificadores de hilo? int pthread_equal(pthread_t thread1, pthread_t thread2); Si los identificadores coinciden retorna TRUE, en caso contrario, FALSE

Cancelación En cualquier momento se puede solicitar la cancelación de un hilo Cuando se solicita la cancelación de un hilo se puede: Finalizar automáticamente el hilo Ignorar la petición y continuar Retrasar la finalización hasta llegar a un punto seguro

Funciones asociadas Solicitar la cancelación de un hilo: int pthread_cancel(pthread_t thread); Habilitar o inhabilitar la cancelación: int pthread_setcancelstate(int new_state, int *old_state); PTHREAD_CANCEL_ENABLE o PTHREAD_CANCEL_DISABLE Establecer el tipo de cancelación: int pthread_setcanceltype(int new_type, int *old_type); PTHREAD_CANCEL_ASYNCHRONOUS o PTHREAD_CANCEL_DEFERRED Verificar y terminar si se ha solicitado la cancelación: void pthread_testcancel(void);

Ejemplo #include <pthread.h> void *Hilo (void *arg) { while(1) { printf (“%s\n”, (char *)arg); sleep(1); } } /* Fin de Hilo */ main() { pthread_t th1; pthread_create (&th1, NULL, Hilo, “Hilo 1”); sleep(5); pthread_cancel(th1); printf(“Envío cancel al Hilo 1 ...\n”); } /* Fin de main */

Terminación Se puede instalar manejadores de terminación de hilos Cuando el hilo termina voluntariamente o por una cancelación, automáticamente se invoca a la función de terminación especificada Esta posibilidad evita el problema de dejar al sistema en un estado inconsistente

Funciones asociadas Para instalar un manejador de terminación: void pthread_cleanup_push(void *(*cleanup)(void *), void *arg); Para eliminar el último manejador instalado: void pthread_cleanup_pop(int exec); Si exec!=0, la función se elimina y se ejecuta

Planificación

Planificación de threads Existen las mismas políticas que para los procesos Los atributos de planificación se pueden especificar al crear el hilo en el objeto de atributos Se puede seleccionar entre dos ámbitos de planificación: Ámbito de proceso: un planificador de segundo nivel planifica los hilos de cada proceso PTHREAD_SCOPE_PROCESS Ámbito de sistema: los hilos se planifican como los procesos pesados PTHREAD_SCOPE_SYSTEM

Ámbitos (contentionscope) Hilos Planificador Sistema Operativo Planificador

Herencia Si existe herencia no se hace caso del resto de los atributos de planificación Valores: PTHREAD_INHERIT_SCHED: hereda los atributos del padre PTHREAD_EXPLICIT_SCHED: utiliza los del objeto attr

Funciones asociadas Definición y obtención del ámbito de contención: int pthread_attr_setscope(pthread_attr_t *attr, int scope); int pthread_attr_getscope(const pthread_attr_t *attr, int *scope); Definición y obtención de valores de herencia: int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit); int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);

Funciones asociadas Definición y obtención de los valores de la política de planificación: int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); Definición y obtención de la prioridad: int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);

Cambio dinámico de parámetros Se puede modificar dinámicamente la política de planificación y la prioridad: int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param); int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);

Señales

Introducción Una señal es un mecanismo para avisar a los proceso de la llegada de un evento División por cero Desbordamiento Expiración de alarmas Llegada de un mensaje etc. Cada señal es identificada por un número Cuando llega una señal, el proceso es interrumpido y se invoca a un manejador de señal

Señales ordinarias Las señales no son fiables, se pueden perder si existen señales del mismo número pendientes SIGILL Instrucción ilegal SIGFPE Operación aritmética errónea SIGSEGV Acceso a memoria erróneo SIGINT Señal de atención interactiva (Ctrl-c) SIGPIPE Escritura en una tubería sin lectores SIGSTOP Detiene al proceso SIGCONT Reanuda el proceso SIGUSR1 Reservada para el usuario SIGUSR2 Reservada para el usuario

Señales de tiempo real Las señales de tiempo real, si son soportadas, existen con números comprendidos entre SIGRTMIN y SIGRTMAX Las señales de tiempo real son fiables y: No se pierden nunca (se encolan) Se aceptan en orden de prioridad (número) Pueden transmitir un poco de información, un entero o un puntero Existen como mínimo 8

Generación, entrega y aceptación Un señal se genera: Cuando se produce el evento asociado Una señal se entrega Cuando la señal causa al proceso la acción asociada La acción se determina en recepción Una señal se acepta: Al seleccionarla Para aceptar una señal esta debe estar enmascarada

Generación, entrega y aceptación Señal pendiente: Estado entre su generación y su entrega o aceptación Observable si la señal está enmascarada Si se produce el mismo evento asociado a una señal pendiente: Si es una señal no fiable, el evento se puede perder Si es una señal fiable, y se ha especificado la acción SA_SIGINFO, la señal se encola Cada hilo puede tener su máscara La máscara es heredada y se puede modificar

Acciones asociadas a señales Antes de que llegue una señal el proceso puede prepararse para recibirla y actuar El tratamiento de la señal puede implicar: Ignorar la señal (SIG_IGN) Dejar que el sistema la trate por defecto (SIG_DFL) Manejarla con una función específica: cuando se entrega la señal se ejecuta el manejador indicado y se continúa la ejecución en el punto donde fuimos interrumpidos

Manejadores de señal POSIX define dos tipos de manejador: Si se especifica SA_SIGINFO void manejador (int signo); Si no se especifica SA_SIGINFO void manejador (int signo, siginfo_t *info, void *context); La estructura siginfo_t mantiene, entre otras cosas, el número de señal (si_signo) y la causa (si_code)

Señales en procesos multihilo Las señales pueden enviarse a procesos a a hilos Si el evento está asociado a un hilo, la señal es enviada al hilo correspondiente Si el evento es asíncrono, se envía al proceso Las señales asociadas a un PID se envían al proceso y las asociadas a un TID se envían a un hilo

Señales en procesos multihilo Una señal asociada a un hilo: Si no está bloqueada, se entrega Si está bloqueada y la acción no es ignorar se queda pendiente hasta que es desbloqueada o aceptada con sigwait() Si está bloqueada y la acción es ignorar: no está especificado

Señales en procesos multihilo Una señal asociada a un proceso: Si la acción no es ignorar, se envía a un único hilo que esté esperando con sigwait() o a un hilo que no tenga la señal bloqueada Si no es entregada, la señal queda pendiente hasta que un hilo llama a sigwait(), un hilo desbloquea la señal o la acción asociada se pone en ignorar

Funciones de configuración Prototipos definidos en: #include <signal.h> Funciones: int sigaddset(sigset_t *set, int sig); int sigdelset(sigset_t *set, int sig); int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigismember(sigset_t *set, int sig); Todas ellas permiten manipular la máscara de señales apuntada por set

Funciones de tratamiento Para examinar y cambiar la acción asociada a una señal: int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact); Si act=NULL, la acción no se modifica Si oldact!=NULL la acción actual se devuelve en *oldact

Estructura sigaction struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); } El primer campo es el puntero al manejador o también SIG_DFL o SIG_IGN El segundo campo es la máscara de señales para cuando estamos tratando la actual

Estructura sigaction El tercer campo puede valer: SA_SIGINFO: La señal se encola y lleva información adicional (el manejador de señal es de tres argumentos) SA_NOCLDSTOP: no generar SIGCHLD cuando un hijo se detiene El cuarto campo es el puntero a la función que se ejecutará si esta definido el flag SA_SIGINFO

Bloqueo de señales Para examinar y cambiar las señales bloqueadas: int sigprocmask (int operation, cont sigset_t *set, sigset_t *oldset); /* un único hilo */ int pthread_sigmask(int operation, const sigset_t *set, sigset_t *old_set); operation indica el modo de operar: SIG_BLOCK: añade señales bloqueadas SIG_UBLOCK: elimina señales bloqueadas SIG_SETMASK: modifica toda la mascara de señales set: señales afectadas. Si vale NULL, no se cambia ninguna oldset: si !=NULL, devuelve las señales bloqueadas anteriormente

Envío de señales Para enviar señales a procesos: int kill (pid_t pit, int sig); int sigqueue (pid_t victim_id, int this_sig, union sigval extra_info); Para enviar señales a hilos: int pthread_kill(pthread_t thread, int sig);

Aceptación de señales Para examinar señales pendientes: int sigpending (sigset_t *set); Para esperar a que se ejecute un manejador de señal: int pause(void); int sigsuspend (const sigset_t *mask); sigsuspend() sustituye la máscara de señales con la apuntada por mask y espera la recepción de una de ellas. Al retornar se restaura la máscara original

Aceptación de señales Esperar a una señal bloqueada: int sigwait(const sigset_t *set, int *sig); int sigwaitinfo(const sigset_t *set, siginfo_t *value); int sigtimedwait(const sigset_t *set, siginfo_t *value, timespec_t *timeout);

Ejemplo #include <signal.h> #include <stdio.h> void manejador (int senal) { printf (“Recibida la señal ... \n”); } main() { struct sigaction accion; accion.sa_flags = 0; accion.sa_handler = manejador; sigemptyset (&accion.sa_mask); sigaction (SIGUSR1, &accion, NULL); while (1); } /* Fin de main */

Sincronización

Mutex Es un objeto de sincronización que permite el acceso exclusivo a recursos Operaciones: P() V() Soportan herencia de prioridad

Objeto de atributos Atributos de inicio: pshared: compartido o no entre procesos PTHREAD_PROCESS_SHARED PTHREAD_PROCESS_PRIVATE protocol: protocolo utilizado PTHREAD_PRIO_NONE: sin herencia PTHREAD_PRIO_INHERIT: con herencia PTHREAD_PRIO_PROTECT: protección de prioridad prioceiling: techo de prioridad Estos atributos se almacenan en el objeto de atributos

Funciones de manipulación int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol); int pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, int prioceiling); int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *prioceiling);

Manejo de mutex int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex);

Cambio del techo de prioridad int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling); int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling);

Variables de condición Es un objeto de sincronización que permite bloquear a un hilo hasta que otro decide reactivarlo Operaciones: Esperar una condición: un hilo se suspende hasta que otro señaliza la condición. En este punto se comprueba la condición y el proceso se repite si la condición es falsa Señalizar una condición: se avisa a uno o más hilos suspendidos broadcast: se reactivan todos los hilos suspendidos en la condición

Atributos pshared: indica si se puede compartir entre procesos PTHREAD_PROCESS_SHARED PTHREAD_PROCESS_PRIVATE Funciones: int pthread_condattr_init(pthread_condattr_t *attr); int pthread_condattr_destroy(pthread_condattr_t *attr);

Funciones asociadas int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

Referencias Michael González Harbour: POSIX de Tiempo Real. 1995. Curso de doctorado Ismael Ripoll. POSIX 1003. Curso de doctorado Juan Antonio de la Puente. Curso de doctorado Programming for the Real World. POSIX.4. Bill o. Gallmeister. O´Reilly. 1995 Pthreads Programming. Bradford Nichols. O’Reilly. 1996