La especificación POSIX 1003.1c ha sido finalizada tras un periodo de varios años desde que se inició su redacción. El esfuerzo realizado por conseguir un estándar para la programación multihilo ha conseguido su resultado mediante la aprobación del estándar IEEE POSIX 1003.1c-1995 en Junio de 1995. Actualmente, también es estándar ISO/IEC 9945-1:1996, que es parte de la edición de 1996 del estándar ANSI/IEEE 1003.1.
El estándar final 1003.1c fue conocido anteriormente como 1003.4, y existen diversos borradores del estándar que han sido implementados por distintos paquetes antes de que se aprobase el estándar final, que fue el Draft 10. Así, borradores importantes han sido el Draft 6, el Draft 8 y el Draft 10. En los primeros borradores se incluían semánticas y funciones que en el borrador final fueron eliminadas, como es el caso de las operaciones sobre semáforos.
Sin embargo, hasta ahora existían numerosas especificaciones de APIs de hilos entre las que un programador podía elegir a la hora de diseñar una aplicación basada en hilos. El problema potencial con esta situación es la portabilidad de la aplicación resultante. Este problema es tanto para los programadores como para los clientes de la aplicación. Cuando la especificación del estándar POSIX se hizo oficial, los principales fabricantes de sistemas operativos comenzaron a trabajar en la implementación de dicho estándar en sus sistemas, sin embargo muchos de ellos aún no cumplen el estándar en su totalidad, aunque las diferencias tienden a ser mínimas. La utilización del API POSIX protege las inversiones de los clientes en las aplicaciones, y proporciona al fabricante la posibilidad de vender su producto en un mayor número de plataformas heterogéneas.
Todos los fuentes que empleen POSIX 1003.1c (abreviadamente POSIX.1c o simplemente Pthreads) deben incluir el fichero de cabecera pthread.h con la directiva :
#include <pthread.h>
La librería de hilos POSIX.1c debe ser la última librería especificada en la línea de comandos del compilador.
Ejemplos : cc .... -lpthread
El estándar POSIX Threads especifica un modelo de hilos dirigido por prioridades con políticas de planificación preemptivas, gestión de señales, y primitivas para proporcionar exclusión mutua así como espera sincronizada. El estándar proporciona una base uniforme para aplicaciones basadas en un sistema multiprocesador de memoria compartida y entornos de tiempo real, así como un modelo sencillo para aplicaciones multihilo en un entorno uniprocesador.
El estándar POSIX Threads especifica varios servicios que deben ser proporcionados para soportar aplicaciones multihilo. La mayoría de las especificaciones del interfaz dejan los detalles al tipo de implementación concreta. Las distintas implementaciones del estándar pueden variar substancialmente en algunos aspectos.
Un hilo es una secuencia de instrucciones que puede ser planificada, de manera similar en concepto a un proceso. Un hilo tiene la ventaja de que está diseñado para ser bastante menos costoso, ya que es más rápido y barato crear, destruir, finalizar, y sincronizar un hilo que un proceso.
POSIX.1c modifica la definición de proceso de POSIX.1 de "un espacio de direcciones con un solo flujo de control" a "un espacio de direcciones con uno o más flujos de control". Todos los hilos de un proceso comparten las siguientes características :
Cada hilo tiene la siguiente información específica :
Si un hilo cambia alguna de las entidades del proceso,
el cambio será visto por todos los hilos. La función
main() de un programa está
asociada con el hilo principal e inicial del proceso.
A continuación se presentan los conceptos básicos necesarios para comprender la sintaxis del API de programación de hilo que define el estándar POSIX 1003.1c Draft 10.
A continuación se define la estructura de los tipos de dato y las funciones que se definen en estándar POSIX.1c.
pthread[_object]_t
pthread[_object]_operation[_np|_NP]
donde :
- object es un tipo (no requerido si es thread - hilo - ).
- operation es un tipo específico de operación.
- np (o NP) es
usado para identificar funciones específicas implementadas
no portables.
Todas las funciones POSIX.1c (excepto pthread_exit, pthread_getspecific y pthread_self) devuelven 0 en caso de éxito, o un valor de error en errno si la operación falla.
Existen 8 tipos de datos POSIX.1c :
Tipo | Descripción |
pthread_attr_t | Atributo de hilo. |
pthread_mutexattr_t | Atributo de mútex. |
pthread_condattr_t | Atributo de variable de condición. |
pthread_mutex_t | Mútex (bloqueo con exclusión mutua). |
pthread_cond_t | Variable de condición. |
pthread_t | Hilo (identificador de hilo). |
pthread_once_t | Ejecución una sola vez. |
pthread_key_t | Clave sobre datos específicos de hilo. |
El estándar POSIX.1c está formado por un componente
base y un número de componentes opcionales en la implementación.
La base es el conjunto de operaciones necesarias y mínimas
que debe presentar toda implementación del estándar.
El símbolo del preprocesador _POSIX_THREADS
puede ser empleado para comprobar la presencia de la base POSIX.1c.
Además, el estándar describe un conjunto de 6 componentes
opcionales. Se puede emplear un símbolo de preprocesador
para comprobar la presencia de cada uno de estos componentes opcionales.
Los símbolos opcionales empleados para ello son los siguientes :
Macros de comprobación | Descripciones |
_POSIX_THREADS | Componente base sobre hilos. |
_POSIX_THREAD_ATTR_STACKADDR | Atributo dirección de pila. |
_POSIX_THREAD_ATTR_STACKSIZE | Atributo tamaño de pila. |
_POSIX_THREAD_PRIORITY_SCHEDULING | Planificación con prioridades. |
_POSIX_THREAD_PRIO_INHERIT | Prioridad heredada en los mútex. |
_POSIX_THREAD_PRIO_PROTECT | Prioridad límite en los mútex. |
_POSIX_THREAD_PROCESS_SHARED | Sincronización entre procesos. |
Descripción del API del estándar IEEE POSIX 1003.1c.
En las anotaciones posteriores, los argumentos de función que son de la forma :
tipo name = NULL
indican que el valor NULL
puede ser empleado de forma segura para name.
pthread_atfork()
int pthread_atfork( void (*prepare)(void) = NULL, void (*parent)(void) = NULL, void (*child)(void) = NULL );
Registra funciones para ser llamadas durante la ejecución de fork.
Errores ENOMEM
Notas Las funciones prepare son llamadas en orden inverso al de registro.
Las funciones parent y
child son llamadas en orden inverso
al de registro.
Existen una serie de funciones que son básicas en la gestión de hilos que realiza una aplicación multihilo. Estas funciones son :
pthread_create()
int pthread_create( pthread_t *thread, const pthread_attr_t *attr = NULL, void *(*entry)(void *), void *arg );
Crea un nuevo hilo de ejecución, indicando los atributos del hilo, la función que ejecutará y los argumentos para dicha función de inicio.
Errores EAGAIN, EINVAL
Notas PTHREAD_THREADS_MAX
es el nº máximo de hilos por proceso.
pthread_equal()
int pthread_equal( pthread_t t1, pthread_t t2 );
. Compara si dos identificadores de hilo son el mismo.
Errores ninguno
pthread_exit()
void pthread_exit( void *status = NULL );
Finaliza el hilo que realiza la llamada.
Errores ninguno
pthread_join()
int pthread_join( pthread_t thread, void **status = NULL );
Sincroniza el hilo actual con la finalización del hilo especificado, devolviendo el estado del hilo por el que se espera..
Errores EINVAL, ESRCH, EDEADLK
Notas Esta función es un punto de cancelación.
pthread_self()
pthread_t pthread_self( void );
Devuelve el identificador del hilo que realiza la llamada.
Errores ninguno
pthread_detach()
int pthread_detach( pthread_t thread );
Establece el estado del hilo especificado al valor PTHREAD_CREATE_DETACHED, es decir, convierte el hilo especificado en independiente.
Errores EINVAL,
ESRCH
pthread_getschedparam()
int pthread_getschedparam( pthread_t thread, int *policy, struct sched_param *param );
Obtiene la política de planificación y los parámetros del hilo especificado.
Control _POSIX_THREAD_PRIORITY_SCHEDULING
Errores ENOSYS,
ESRCH
pthread_setschedparam()
int pthread_setschedparam( pthread_t thread, int policy, const struct sched_param *param );
Establece la política de planificación y los parámetros del hilo especificado.
Control _POSIX_THREAD_PRIORITY_SCHEDULING
Errores ENOSYS, EINVAL, ENOTSUP, EPERM, ESRCH
policy { SCHED_RR, SCHED_FIFO, SCHED_OTHER }
Un mútex es empleado para proteger el acceso compartido a los recursos. Las funciones de gestión de mútex son las siguientes :
pthread_mutex_init()
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t *attr = NULL );
Inicializa un mutex con los atributos especificados.
Errores EAGAIN,
ENOMEM, EPERM, EBUSY, EINVAL
pthread_mutex_destroy()
int pthread_mutex_destroy( pthread_mutex_t *mutex );
Destruye el mútex especificado.
Errores EBUSY,
EINVAL
pthread_mutex_lock()
int pthread_mutex_lock( pthread_mutex_t *mutex );
Adquiere el mútex indicado (bloquea el mútex).
Errores EINVAL,
EDEADLK
pthread_mutex_trylock()
int pthread_mutex_trylock( pthread_mutex_t *mutex );
Intenta adquirir el mútex indicado.
Errores EINVAL,
EBUSY
pthread_mutex_unlock()
int pthread_mutex_unlock( pthread_mutex_t *mutex );
Libera el mútex previamente adquirido (bloqueado) que se especifica.
Errores EINVAL,
EPERM
pthread_mutex_getprioceiling()
int pthread_mutex_getprioceiling( const pthread_mutex_t *mutex int *prioceiling );
Obtiene el valor de prioridad límite (prioceiling) del mútex especificado.
Control _POSIX_THREAD_PRIO_PROTECT
Errores ENOSYS,
EINVAL, EPERM
pthread_mutex_setprioceiling()
int pthread_mutex_setprioceiling( pthread_mutex_t *mutex, int prioceiling, int *old_ceiling );
Establece el valor de prioridad límite (prioceiling) y devuelve el anterior valor (old_ceiling) en el mútex especificado.
Control _POSIX_THREAD_PRIO_PROTECT
Errores ENOSYS,
EINVAL, EPERM
Las variables de condición están asociadas con un mútex y se emplean para sincronizar los hilos. Las funciones que permiten gestionar las variables de condición son :
pthread_cond_init()
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init( pthread_cond_t *cond, const pthread_condattr_t *attr = NULL );
Inicializa una variable de condición con los atributos especificados.
Errores EAGAIN,
ENOMEM, EBUSY, EINVAL
pthread_cond_destroy()
int pthread_cond_destroy( pthread_cond_t *cond );
Destruye la variable de condición especificada.
Errores EBUSY,
EINVAL
pthread_cond_wait()
int pthread_cond_wait( pthread_cond_t *cond, pthread_mutex_t *mutex );
Bloquea el hilo actual a la espera de la variable de condición especificada.
Errores EINVAL
pthread_cond_timedwait()
int pthread_cond_timedwait( pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime );
Bloquea el hilo actual a la espera de la variable de condición especificada, no más tiempo del especificado en forma absoluta por abstime.
Errores ETIMEDOUT, EINVAL
Notas Esta función es un punto de cancelación.
pthread_cond_signal()
int pthread_cond_signal( pthread_cond_t *cond );
Desbloquea un hilo actualmente bloqueado en la variable de condición especificada.
Errores EINVAL
pthread_cond_broadcast()
int pthread_cond_broadcast( pthread_cond_t *cond );
Desbloquea todos los hilos actualmente bloqueados en la variable de condición especificada.
Errores EINVAL
Este mecanismo es empleado para inicializar datos u objetos una sola vez para todos los hilos. La forma de conseguir este objetivo es a través de las siguientes primitivas :
pthread_once_t once_control = PTHREAD_ONCE_INIT;
Inicializa un variable especial para control de ejecución una sola vez.
pthread_once()
int pthread_once( pthread_once_t *once_control, void (*init_routine)(void) );
Ejecución una sola vez de la rutina de inicialización init_routine, empleando para ello la variable de control once_control. Esta función garantiza que una nueva llamada a la rutina no ejecutará la función nuevamente.
Errores ninguno especificado.
Si un hilo desea crear datos que no sean globales a todos los hilos pero sí a sus procedimientos, puede crear datos especiales de este tipo mediante una serie de funciones :
pthread_key_create()
int pthread_key_create( pthread_key_t *key, void (*destructor)(void *) = NULL );
Crea una clave para datos específicos de hilo, que puede ser empleada por todos los hilos del proceso.
Errores EAGAIN, ENOMEM
Notas PTHREAD_KEY_MAX es el límite máximo del sistema de claves por
proceso. PTHREAD_DESTRUCTOR_ITERATIONS
es el límite máximo del sistema de llamadas al destructor
cuando se produce la finalización del hilo.
pthread_key_delete()
int pthread_key_delete( pthread_key_t key );
Destruye un clave de datos específicos del hilo.
Errores EINVAL
pthread_setspecific()
int pthread_setspecific( pthread_key_t key, const void *value );
Establece el valor para la clave dada en el hilo que realiza la llamada.
Errores ENOMEM,
EINVAL
pthread_getspecific()
void * pthread_getspecific( pthread_key_t key );
Devuelve el último valor para la clave dada del hilo que realiza la llamada.
Errores ninguno
Un hilo puede ser cancelado llamando a la función
pthread_cancel. La cancelación
es gestionada como una petición de envío de una
señal interna especial SIGCANCEL
a un hilo. La acción a tomar depende del estado de cancelación
del hilo receptor.
Estado | Tipo | |
desactivado | cualquiera | Señal SIGCANCEL pendiente para el hilo hasta que la cancelación sea activada. |
Activado | diferida | Señal SIGCANCEL pendiente para el hilo hasta que se alcance un punto de interrupción. |
Activado | asíncrona | La cancelación se realiza inmediatamente. |
Los puntos de interrupción son funciones definidas en el interfaz de la biblioteca de hilos que pueden suspender un hilo indefinidamente (ej. espera por una variable de condición), con la excepción del bloqueo de un mútex, que no debe ser un punto de interrupción para permitir una implementación eficiente. Un punto de interrupción también puede ser introducido mediante la función pthread_testcancel.
Cuando se va a realizar una petición de cancelación, el estado de cancelación del hilo receptor es cambiado a desactivado, el resto de señales para el hilo son deshabilitadas, y se introduce una llamada falsa a pthread_exit en la pila del hilo.
La cancelación de un hilo hace que se ejecuten los gestores
de limpieza introducidos en la pila de cancelación, de
forma que se encargan de liberar los recursos que tuviese bloqueados
el hilo, como mútex, semáforos, ficheros, etc.
pthread_cleanup_push()
void pthread_cleanup_push( void (*routine)(void *), void *arg );
Introduce (push) una función en la cima de la pila de cancelación, que será ejecutada cuando se produzca la cancelación del hilo.
Errores ninguno especificado
pthread_cleanup_pop()
void pthread_cleanup_pop( int execute );
Extrae (pop)una función de la cima de la pila de cancelación y opcionalmente la ejecuta.
Errores ninguno especificado
Notas : Las operaciones push y pop deben aparecer en el mismo nivel
léxico.
execute { 1, 0 }. 1=Ejecuta. 0=No ejecuta.
pthread_setcancelstate()
int pthread_setcancelstate( int state, int *oldstate );
Establece el estado de cancelación y devuelve el estado anterior para el hilo que realiza la llamada.
Errores EINVAL
state { PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE }
pthread_setcanceltype()
int pthread_setcanceltype( int type, int *oldtype );
Establece el tipo de cancelación y devuelve el tipo anterior para el hilo que realiza la llamada.
Errores EINVAL
type { PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS }
pthread_cancel()
int pthread_cancel( pthread_t thread );
Cancela el hilo especificado.
Errores ESRCH
Notas Los hilos que han sido cancelados terminan con el estado (status)
PTHREAD_CANCELLED.
pthread_testcancel()
void pthread_testcancel( void );
Introduce un punto de cancelación, que es un punto en el que un hilo puede ser finalizado si se le notificó una señal de cancelación.
Errores ninguno
Notas Esta función es un punto de cancelación.
Los atributos permiten crear patrones de hilo, de forma que se puedan crear hilos con las mismas características sin necesidad de especificarlas en cada uno de los hilos, sino simplemente indicando el molde (objeto de atributos) que se empleará para ello.
Existen funciones que permiten establecer o recuperar un determinado atributo de un objeto de atributos de hilo :
int pthread_attr_setnombre( pthread_attr_t *attr, Tipo t );
int pthread_attr_getnombre( const pthread_attr_t *attr, Tipo *t );
Siendo nombre y Tipo valores de la siguiente tabla :
Tipo y nombre | Macro de comprobación | Valor(es) |
int inheritsched | _POSIX_THREAD_PRIORITY_SCHEDULING | PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED |
int schedpolicy | _POSIX_THREAD_PRIORITY_SCHEDULING | SCHED_FIFO, SCHED_RR, SCHED_OTHER |
struct sched_param schedparam | _POSIX_THREADS | POSIX.1b, Sección 13 |
int contentionscope | _POSIX_THREAD_PRIORITY_SCHEDULING | PTHREAD_SCOPE_SYSTEM, PTHREAD_SCOPE_PROCESS |
size_t stacksize | _POSIX_THREAD_ATTR_STACKSIZE | >= PTHREAD_STACK_MIN |
void *stackaddr | _POSIX_THREAD_ATTR_STACKADDR | void *stack |
int detachstate | _POSIX_THREADS | PTHREAD_CREATE_DETACHED PTHREAD_CREATE_JOINABLE |
pthread_attr_init()
int pthread_attr_init( pthread_attr_t *attr );
Inicializa un objeto de atributos de hilo.
Errores ENOMEM
pthread_attr_destroy()
int pthread_attr_destroy( pthread_attr_t *attr );
Destruye un objeto de atributos de hilo.
Errores ninguno
Los atributos permiten crear patrones de mútex, de forma que se puedan crear mútex con las mismas características sin necesidad de especificarlas en cada uno de los mútex, sino simplemente indicando el molde (objeto de atributos) que se empleará para ello.
Existen funciones que permiten establecer o recuperar un determinado atributo de un objeto de atributos de mútex :
int pthread_mutexattr_setnombre( pthread_attr_t *attr, Tipo t );
int pthread_mutexattr_getnombre( const pthread_attr_t *attr, Tipo *t);
Siendo nombre y Tipo valores de la siguiente tabla :
Tipo y nombre | Macro de comprobación | Valor(es) |
int protocol | _POSIX_THREAD_PRIO_INHERIT, _POSIX_THREAD_PRIO_PROTECT | PTHREAD_PRIO_NONE, PTHREAD_PRIO_PROTECT, PTHREAD_PRIO_INHERIT |
int pshared | _POSIX_THREAD_PROCESS_SHARED | PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE |
int prioceiling | _POSIX_THREAD_PRIO_PROTECT | POSIX.1b Sección 13 |
pthread_mutexattr_init()
int pthread_mutexattr_init( pthread_mutexattr_t *attr );
Inicializa un objeto de atributos de mútex.
Errores ENOMEM
pthread_mutexattr_destroy()
int pthread_mutexattr_destroy( pthread_mutexattr_t *attr );
Destruye un objeto de atributos de mútex.
Errores EINVAL
Los atributos permiten crear patrones de variables de condición, de forma que se puedan crear mútex con las mismas características sin necesidad de especificarlas en cada una de las variables de condición, sino simplemente indicando el molde (objeto de atributos) que se empleará para ello.
Existen funciones que permiten establecer o recuperar un determinado atributo de un objeto de atributos de variables de condición :
int pthread_condattr_setnombre( pthread_condattr_t *attr, Tipo t );
int pthread_condattr_getnombre( const pthread_condattr_t *attr, Tipo *t );
Siendo nombre y Tipo valores de la siguiente tabla :
Tipo y nombre | Macro de comprobación | Valor(es) |
int pshared | _POSIX_THREAD_PROCESS_SHARED | PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE |
pthread_condattr_init()
int pthread_condattr_init( pthread_condattr_t *attr );
Inicializa un objeto de atributos de variable de condición.
Errores ENOMEM
pthread_condattr_init()
int pthread_condattr_destroy( pthread_condattr_t *attr );
Destruye un objeto de atributos de variable de condición.
Errores EINVAL
La gestión de señales suele ser un aspecto bastante delicado en los paquetes de hilos. Cada hilo tiene su propia máscara de señales. Los gestores de señales son compartidos por todos los hilos del proceso. Si se puede seleccionar más de un hilo para recibir una cierta señal, el receptor. Las señales pueden estar pendientes de un hilo, cuando el hilo tiene la señal deshabilitada (enmascarada), o pendientes de un proceso, cuando todos los hilos del proceso tienen la señal deshabilitada. Las acciones por defecto se realizan respecto al proceso.
Cada hilo tiene su propia máscara de señales, que es heredada en la creación del hilo. Los gestores de señales, sin embargo, se instalan a nivel proceso, y son comunes a todos los hilos del proceso.
El estándar especifica las siguientes funciones para su manejo por parte del programador :
pthread_sigmask()
int pthread_sigmask( int how, const sigset_t *newmask = NULL, sigset_t *oldmask = NULL );
Examina o modifica la máscara de señales del hilo que realiza la llamada.
Errores EINVAL
how { SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK }
pthread_kill()
int pthread_kill( pthread_t thread, int signo );
Envía la señal especificada al hilo indicado.
Errores ESRCH,
EINVAL
sigwait()
int sigwait( const sigset_t *set, int *sig );
Acepta de forma síncrona una señal (suspende el hilo hasta la llegada de la señal indicada).
Errores EINVAL, EINTR
Notas Esta función es un punto de cancelación.
La seguridad de una aplicación es básica. Las funciones empleadas en una aplicación multihilo deben ser seguras, en el sentido de que deben permitir que varios hilos de control invoquen a la misma función de forma concurrente, sin que ello genere problemas o efectos indeseables. Existen cuatro niveles de seguridad que deben ser observados cuando se escribe código multihilo.