Procesos e hilos Pablo San Segundo (C-206) pablo.sansegundo@upm.es KERNEL DEL SISTEMA OPERATIVO Al arrancar el PC siempre se carga en memoria una copia del Kernel desde disco El Kernel del sistema operativo es un programa, típicamente programado en C ¿Qué es un Proceso? ` ` Un programa en ejecución La unidad de procesamiento gestionada por el S.O. Presencia en memoria y tabla BCP ` Registros de la CPU ` ` Contexto Entorno ` ` ` Identificadores de usuario y grupo Identificador de proceso Variables de entorno ` ` ` Usuario Sistema Control (tablas) ` ` ` ` Estado: Bloqueado, ejecución, listo Prioridad: cola de prioridades E/S: Desc. recursos, ,mensajería etc. Presencia en memoria Bloque de Control de Proceso typedef struct bcp { struct bcp *siguiente; struct bcp *anterior; struct bcp *hebra int retardo; palabra marco; palabra pila; palabra marcador; int pri; int estado; struct bcp *todosenl; int mascara; k_ptrmens retenmens procnombre nombre; } bcp; ESTADOS BÁSICOS DE UN PROCESO ` EN EJECUCIÓN ` ` BLOQUEADO ` ` Un proceso por procesador En espera de completar E/S LISTO ` Preparado para pasar al estado de ejecución ENTORNO DEL PROCESO ` Definición ` ` Ubicación ` ` Tabla NOMBRE-VALOR que se pasa al proceso en su creación Imagen en Memoria: típicamente en la pila Valor ` ` Por defecto Mandatos Shell (API S.O.) SHELL PATH=/usr/bin:/home/pss/bin echo $PATH echo $HOME TERM=tty100 HOME=/home/pss SHELL PATH=$PATH:XXX PDW=/home/pss/ingsw CARACTERÍSTICAS DE PROCESOS ` Jerarquías (familias) ` ` Vida ` ` ` ` Procesos hijo, padre, hermano, abuelo etc.. Creación Ejecución Muerte Tipo de ejecución ` ` Batch (lotes) Interactivo INI INI SES INI SES SHELL SHELL PROC 1 PROC 2 USUARIO ` Definición ` ` Autenticación ` ` ` User login (internamente es un número para el S.O.: i.e uid) Contraseña (password) Super-usuario (sudo, su) ` ` Persona autorizada a utilizar el sistema Derechos de administrador Grupo de usuarios ` ` Colectivo de usuarios con los mismos permisos Todo usuario ha de pertenecer a un grupo FIN DE INTRODUCCIÓN Clasificación de los Sistemas Operativos Fundamento del multiproceso ` Definición ` ` Ejecución concurrente real de varios procesos con un solo procesador Bases ` ` Alternancia de los procesos en la CPU durante la fase de E/S Almacenamiento en memoria de varios procesos simultáneamente Ejemplo Ejecución de proceso nulo VENTAJAS DE LA MULTITAREA ` Funcionamiento del SO en modo ‘interactivo’ de forma eficiente ` ` ` Aprovecha tiempos de espera de procesos durante E/S Aumenta el uso de la CPU Nuevo paradigma de programación ` Principio de ‘modularidad’: Un programa-varios ‘procesos’ GRADO DE MULTIPROGRAMACIÓN ` Definición ` ` Recurso ` ` Número de procesos activos en un instante de tiempo Memoria principal Sistema SIN memoria virtual GRADO DE MULTIPROGRAMACIÓN ` Sistema CON memoria virtual GRADO DE MULTIPROGRAMACIÓN ` Sistema CON memoria virtual RENDIMIENTO DEL PROCESADOR HIPER-PAGINACIÓN FIN DE S.O. MULTITAREA INFORMACIÓN DEL PROCESO EN S.O. i.e. RIED CODIGO PILA DATOS Se gestionan fuera del BCP. ¿Por qué? ESTADO DEL PROCESO : CONTEXTO ` ` ` ` ` ` Registros generales ` Motorola 68000: A0-A7, D0-D7 Contador de programa (PC) ` Siguiente instrucción a ejecutar Puntero de Pila (“Call Stack pointer”) ` Referencia la pila en memoria asociado a todo proceso ` Motorola 68000 (USP y SSP) Puntero de marco (“Frame pointer”) Registro de Estado ` Modo de ejecución, máscaras de interrupciones, información acerca de la última operación realizada en la ALU, etc… ` Motorola 68000 (SR) Registros especiales ` Ejemplo: RIED: Registro que Identifica el Espacio de Direcciones que ocupa en memoria el proceso en ejecución PROCESO EN MEMORIA Pertenece al BCP Segmentación fija Hoy en día la segmentación es variable Preasignación inicial de zona de swapping TABLAS DEL SISTEMA OPERATIVO ` Tabla de procesos (BCP) ` ` Tabla de Memoria (alias paginado) ` ` Información para la gestión de memoria Tabla de Entrada / Salida ` ` Información exclusiva de cada proceso que no se puede COMPARTIR y cuyo tamaño no varía durante la ejecución de cada proceso (IMPLEMENTACIÓN) Información relativa a periféricos y a las operaciones E/S Tabla de Ficheros ` Información sobre los ficheros utilizados por los procesos Ejemplo Tabla de Ficheros Doble direccionamiento Puntero de posición COMPARTEN LECTURA Y ESCRITURA EN FICHERO TABLA DE FICHEROS EJEMPLO: INFORMACIÓN EN EL BCP TREEPS (LINUX) FIN DE PRESENCIA EN MEMORIA DE UN PROCESO NACIMIENTO DE UN PROCESO Linker Transmisión de ficheros al Motorola 68000 A imagen en memoria e.g. kernel.lib/dll FORMACIÓN DE UN PROCESO SE ACTUALIZA LA PRIMERA VEZ Y EN CADA CAMBIO DE CONTEXTO Tablas S.O. COPIA DE LAS BIBLIOTECAS DEL SISTEMA EN LA IMAGEN DE MEMORIA (kernel.lib) EL EJECUTABLE NO CONTIENE A LAS BIBLIOTECAS DEL SISTEMA FIN DE FORMACIÓN DE UN PROCESO SERVICIOS DE GESTIÓN DE PROCESOS ` ` ` ` ` ` ` Identificación de procesos Entorno de un proceso Creación de procesos Cambio de imagen de un proceso Espera a la terminación de un proceso Finalización de la ejecución de un proceso Información adicional SERVICIO POSIX: fork ` Definición ` pid_t fork (void) : Crea un proceso hijo clonando al padre. Copia no exacta (i.e. señales) SERVICIO POSIX: fork ` Librerías ` ` Retorno ` ` ` ` #include <sys/types.h> Identificador de procesoÆpadre 0 Æhijo -1 si error Descripción ` ` ` Crea un proceso hijo que ejecuta el mismo programa que el padre Se heredan los descriptores (i.e. archivos) Se desactivan las alarmas pendientes Depende de la distribución SERVICIO POSIX: exec ` Definición ` Cambia el programa del proceso Los descriptores abiertos se mantienen EJEMPLO: Servicio fork #include <sys/types.h> #include <iostream> int main() { pid_t pid; pid=fork(); if (pid= = -1) cout<<“Error”<<endl; else if (pid == 0) { cout<<“Proceso hijo”<<endl; } else{ cout<<“Proceso padre”<<endl; } return 0; } SERVICIO POSIX: exec ` Declaraciones ` ` ` ` Retorno ` ` int execl (const char* path, const char* arg,…) int execlp(const char* file, const char* arg,…) int execvp(const char* file, const char* arg[]) -1 si error o NO RETORNA Ultimo parámetro NULL Descripción ` ` ` El mismo proceso ejecuta otro programa Los descriptores abiertos permanecen abiertos Las señales toman la acción por defecto #include <unistd.h> EJEMPLO: Servicio execl #include <sys/types.h> #include <unistd.h> #include <iostream> int main() { pid_t pid; if ((pid = fork()) = = -1) cout<<“Error”<<endl; else if (pid == 0) { if(execl ("/bin/ls, “ls", “-l”, NULL)){ cout<<“Error al lanzar la tarea”<<endl; } } int status; wait(&satus); return 0; } EJEMPLO: Servicio execv #include <sys/types.h> #include <unistd.h> #include <iostream> int main() { pid_t pid; char * arg[3]; arg[0]=“gedit”; arg[1]=“test.cpp”;arg[2]=NULL; if ((pid = fork()) = = -1) cout<<“Error”<<endl; else if (pid == 0) { if(execvp (arg[0], arg)){ cout<<“Error al lanzar la tarea”<<endl; } } int status; wait(&satus); return 0; } SERVICIO POSIX: int exit (int status) ` ` Definición ` Termina un proceso, devolviendo el parámetro status al proceso padre Declaración ` void exit (int status) ` ` Argumento ` ` EXIT_SUCCESS, EXIT_FAILURE Código de retorno al proceso padre Descripción ` ` ` Finaliza la ejecución del proceso Cierra todos los descriptores Se liberan todos los recursos #include <stdlib.h> SERVICIO POSIX: wait ` Definición ` ` ` WUNTRACED pid_t wait (int* status) WNOHANG pid_t waitpid (int pid, int* status, int options) Retorno ` ` Espera la terminación de un proceso hijo CUALQUIERA Declaración ` ` #include <sys/wait.h> Identificador del proceso hijo, -1 si error Descripción ` ` Permite a un proceso padre esperar hasta que termine un proceso hijo Devuelve el estado del proceso hijo al terminar ` Macros para analizar el estado: WIFEXITED, WEXITSTATUS WIFSIGNALED, WTERMSIG, etc… EJEMPLO : Servicio wait con MACROS int main() { Capturar el error int status; pid_t pid; pid= fork(); if(pid==0){ execlp(“gedit”, “main.cpp”, NULL); exit(EXIT_SUCCESS); } wait(&status) ; if(WIFEXITED(status)){ if(WEXITSATUS(status)==EXIT_SUCCESS){ cout<<“OK”<<endl; }else if(WEXITSATUS(status)==EXIT_FAILURE)){ cout<<“ERROR”<<endl; } } cout<<"Proceso padre terminado"<<endl; exit(EXIT_SUCCESS); } EJEMPLO : Servicio wait multiproceso int main() { int status; pid_t pid; for(int i=0; i<10; i++){ pid= fork(); if(pid==0){ cout<<"hilo: "<<i<<endl; exit(EXIT_SUCCESS); } } //Espera a todos los hijos while ( wait(&status) !=-1 ){;} cout<<"Proceso padre terminado"<<endl; exit(EXIT_SUCCESS); } EJEMPLO: waitpid int main(void) { int status; pid_t pid; pid= fork(); if(pid==0){ sleep(5); cout<<"proceso hijo terminado"<<endl; exit (EXIT_SUCCESS); } pid_t endid= waitpid(pid,&status, WNOHANG); //no bloqueante if(endid==pid){ cout<<"status del hijo diposnible"<<endl; } else{ cout<<"padre termina sin conocer el status del hjo"<<endl; } exit (EXIT_SUCCESS); } Caso de uso típico de exec /fork /wait Padre e hijo comparten recursos EVOLUCIÓN DE PROCESOS: ZOMBIES ` El hijo muere antes de la ejecución de un servicio wait(…) del padre ` Hijo: Proceso ZOMBIE BCP de B sigue existiendo EJEMPLO: Zombies int main(void) { pid_t pid; pid= fork(); switch(pid){ case -1: cout<<"Error al creado proceso duplicado"<<endl; exit(EXIT_FAILURE); break; case 0: sleep(1); cout<<"proceso hijo terminado"<<endl; exit(EXIT_SUCCESS); break; } cout<<“hijo terminará sin wait del padre: ¡ZOMBIE!"<<endl; sleep(30); int status; wait(&status); exit(EXIT_SUCESS); } EJEMPLO: JERARQUÍAS DE PROCESOS #include <sys/types.h> #include <sys/wait.h> int main (int argc, char** argv ) { int i, valor; pid_t pid_1, pid_2; Ejecución desde Terminal <Nombre Ejecutable> 3 i=atoi(argv[1]); pid_1=fork(); while(i>0){ pid_2=fork(); if(pid_2!=0) { wait(&valor) break; } i=i-1; } return 0; } ¿JERARQUÍA DE PROCESOS ? A) Figura B) Sin Bloque Ejercicio: Procesos distribuidos (I) Impleméntese un proceso que lance la tarea de leer un grafo en formato DIMACS de un fichero de texto y devuelva su grado en pantalla. El proceso principal debe esperar a la terminación de dicha tarea e indicar si se ha resuelto correctamente. FIN DE SERVICIOS POSIX PARA PROCESOS ESTADOS DE UN PROCESO Planificador Corto Plazo SWAPPING PLANIFICACIÓN DE PROCESOS ` ` ` Planificador (Planner): Programa del S.O. que determina que proceso tiene que ejecutarse. Activador (Dispatcher) : Programa del SO que carga el programa seleccionado por el planificador en memoria Formas de planificar del S.O: ` ` Largo plazo: Decidir que procesos batch entran a ejecutarse Medio plazo: Añadir procesos a RAM ` ` ` Finalización del estado de suspendido. Sistemas con swapping. CORTO PLAZO: ` DECIDIR QUÉ PROCESO TOMA EL CONTROL DE LA CPU NOCIONES BÁSICAS DEL CORTO PLAZO ` Tipos de planificación ` ` ` Sin expulsión: El proceso conserva la CPU mientras lo desee Con expulsión: El S.O. quita la CPU al proceso cada cierto tiempo. Colas de procesos ` ` Por prioridad (niveles) Por tipo ` ` Lotes Interactivo OBJETIVOS DE LA PLANIFICACIÓN ` ` ` ` Reparto de la CPU equitativo Eficiencia (optimizar uso CPU) Mejorar el tiempo de respuesta ` Modo interactivo ` Modo lotes Cumplir plazos de ejecución en sistemas de tiempo real INCOMPATIBILIDAD DE OBJETIVOS! Windows NT Server ≠ Windows 7 Home Edition COLAS DE PROCESOS ` EJEMPLO BATCH Implementación de colas de procesos ` ` Implementación con punteros a otros BCP Acceso eficiente COLAS DE PRIORIDAD ALGORITMOS DE PLANIFICACIÓN ` CÍCLICA (Round Robin) ` ` ` Asignación de CPU rotatoria Se asigna un tiempo máximo a todos los procesos (time slot ) Los procesos se organizan en una cola circular ` Un proceso que excede su slot se expulsa y se coloca al final de la cola SISTEMAS DE TIEMPO REPARTO por UID en vez COMPARTIDO de por TIEMPO de PROCESAMIENTO ALGORITMOS DE PLANIFICACIÓN (II) ` FIFO (‘first in-first out’) ` ` ` ` Los procesos se organizan según una cola ‘clásica’. Entran por el final y esperan su turno. Política de planificación SIN EXPULSIÓN El proceso en ejecución sólo será expulsado cuando él mismo realiza una llamada bloqueante al sistema (i.e. E/S) Problema: INANICIÓN ` Se suele combinar con política de prioridades (i.e. POSIX) PROCESAMIENTO POR LOTES ALGORITMOS DE PLANIFICACIÓN (III) ` Prioridades ` ` ` El trabajo más corto primero ` ` ` ` ` Fijas: Problema de inanición (p. ej. RTOS) Variables (aumentan con envejecimiento) Aplicación en sistemas Batch Menor tiempo de respuesta Penaliza las tareas largas Dificultad en conocer ‘a priori’ las tareas de mayor duración. Aleatorio ALGORITMOS DE PLANIFICACIÓN RT (IV) ` Definición Sistemas de Tiempo Real (RTOS) ` ` Sistemas donde los procesos deben ejecutarse en instantes predeterminados Tipos de RTS ` ` EJEMPLOS QNX Requisitos blandos / duros Ejecución a plazo fijo / periódico RTLinux VxWorks Windows CE Reloj de tiempo Prioridad 9-2-2006 / 2h:30m:15s 2h:30m:10s 2h:30m:10s 2h:30m:20s REQUISITOS DUROS: En ningún caso de puede rebasar La CAPACIDAD DEreloj PROCESAMIENTO NO es especialmente el tiempo de asociado al proceso PREPROCESAMIENTO PREDECIBLE: Sin caches, sin alta paginación, sin predicción de saltos PLANIFICACIÓN EN POSIX ` Prioridad ` Absoluta (RT) ` ` (0)-normal | (>0)-RT Dinámica ` POSIX 19 a -20 (mayor número Æ menor prioridad) ` El planificador siempre elige aquellos procesos con mayor prioridad ` Políticas de planificación ` FIFO (procesamiento por lotes) ` Cíclica (modo interactivo) ` Otra Ejercicio: Procesos distribuidos (I) ` Algunos servicios POSIX para prioridad dinámica ` int setpriotiy (PRIO_PROCESS, int pid, int niceval) ` ` RETURN VALUE: -1 ERROR, 0 OK int getpriority (PRIO_PROCESS, int pid) Escriba un programa en C para POSIX que lance dos tareas P1 y P2 que compartan escritura en un fichero de texto “log.txt”. Modifique la prioridad de P1 y P2 y compruebe como afecta a la salida. #include <sys/wait.h> Solución int main(void) { pid_t pid, pid2; //fichero compartido ofstream f("log.txt", ios::app); pid=fork(); if(pid==0){ sleep(1); cout<<"Proc 1: "<<getpriority(PRIO_PROCESS,pid)<<endl; //…escribir en fichero exit(EXIT_SUCCESS); } pid2=fork(); if(pid2==0){ sleep(1); cout<<"Proc 2: "<<getpriority(PRIO_PROCESS,pid)<<endl; //…escribir en fichero exit(EXIT_SUCCESS); } setpriority(PRIO_PROCESS,pid,19); setpriority(PRIO_PROCESS,pid2,0); int status; while(wait(&status)!=-1) continue; f.close(); execlp("gedit", "gedit", "log.txt", NULL); exit (EXIT_SUCCESS); } //opcional PLANIFICACIÓN WINDOWS NT FIN DE POLÍTICAS DE PLANIFICACIÓN THREAD (Proceso ligero) ` Definición ` ` Un thread es un proceso que comparte un espacio en memoria con otros threads. Estructura de un proceso en WIN NT ¡Si termina el hilo principal mueren el resto de hilos! COMPARTEN MEMORIA Paralelización con procesos ligeros PROGRAMACIÓN CON THREADS ` Un thread es equiparable a una función que se ejecuta en PARALELO con otras funciones (threads) ` Programación con hilos ` ` ` ` Comunicación entre hilos ` ` ` ` Elevada dificultad Al escribir código es fundamental tener en cuenta un PARALELISMO REAL (aun cuando se tenga un solo procesador) Imaginar LLAMADAS DISTINTAS AL MISMO CÓDIGO Variables compartidas en memoria Descriptores de recursos compartidos, Elementos de sincronización compartidos etc. Aumenta la velocidad de ejecución de tareas con E/S APLICACIÓN: DISEÑO DE SERVIDORES ` Tres arquitecturas de servidor GESTIÓN DE PROCESOS LIGEROS JOINING es uno de los mecanismos de sincronización de hilos SERVICIOS POSIX PARA PROCESOS LIGEROS (I) ` int pthread_create (pthread_t* , const pthread_attr_t*, void* (*func)(void*), void* arg ) ` ` Crea un proceso ligero que ejecuta func con argumentos arg y atributos attr Atributos (modificables mediante servicios) ` ` ` ` RETURN VALUE: 0-OK o ERRNUM ¿COMPARATIVA CON EXIT PARA PROCESOS? void pthread_exit (void* value) ` ` Tamaño de la pila Prioridad, Política de planificación etc. Finaliza la ejecución e indica el estado de terminación pthread_t pthread_self (void) ` Devuelve el identificador del thread que realiza la llamada SERVICIOS POSIX PARA PROCESOS LIGEROS (II) ` int pthread_join (pthread_t tid, void ** value_ptr) ` ` ` ` Suspende la ejecución de un thread JOINABLE hasta que termina el thread (a no ser que haya terminado) Devuelve el estado de terminación del thread en value_ptr según devuelto por pthread_exit o PTHREAD_CANCELED . RETURN VALUE: 0-OK int pthread_detach (pthread_t pid) ` ` Convierte un hilo en DETACHED en tiempo de ejecución RETURN VALUE: 0-OK #include <pthread.h> Linkar con pthread Ejemplos: Hilos-Creación void* sumar_thread (void*) { int a=5; int b=8; cout<<“la suma del hilo es:”<<a+b<<endl; phtread_exit(NULL); } int main(void) { pthread_t thid; pthread_create(&thid, NULL, sumar_thread, NULL); //0-OK if(pthread_join(thid,NULL)){ cout<<“Hilo joinable no terminado adecuadamente”<<endl; } exit(EXIT_SUCCESS); } Ejemplos: Hilos-Paso de parámetros void* sumar_thread (void*param) { sSUMANDOS* p=(sSUMANDOS*) param; cout<<“la suma del hilo es:”<<pÆa +pÆb<<endl; phtread_exit(NULL); } struct sSUMANDOS {int a; int b;} ; int main(void) { sSUMANDOS sum; sum.a=10; sum.b=20; pthread_t thid; pthread_create(&thid, NULL, sumar_thread, &sum); pthread_join(thid,NULL); exit(EXIT_SUCCESS); } Ejemplos: Hilos-Comunicación (I) void* sumar_thread (void*param) { sSUMANDOS* p=(sSUMANDOS*) param; //…modifica variable en bloque invocante pÆa=8; pÆb=10; phtread_exit(NULL); } struct sSUMANDOS {int a; int b;} ; int main(void) { sSUMANDOS sum; sum.a=10; sum.b=20; pthread_t thid; pthread_create(&thid, NULL, sumar_thread, &sum); pthread_join(thid,NULL); cout<<“Los valores nuevo son:”<<sum.a<“:”<<sum.b<<endl; exit(EXIT_SUCCESS); } Ejemplos: Hilos-Comunicación (II) void* sumar_thread (void*param) { sSUMANDOS* p=(sSUMANDOS*) param; sSUMANDOS* pret=new sSUMANDOS; pret->a=pÆa*2; pret->b=pÆb*2; //mod. de valores phtread_exit(pret); } struct sSUMANDOS {int a; int b;} ; int main(void) { sSUMANDOS sum; sum.a=10; sum.b=20; pthread_t thid; pthread_create(&thid, NULL, sumar_thread, &sum); void* ret_thread; pthread_join (thid, &ret_thread); sSUMANDOS ret; ret.a=((sSUMANDOS*) ret_thread)Æa; ret.b=((sSUMANDOS*) ret_thread)Æb; cout<<“Los valores devueltos son:”<<ret.a<“:”<<ret.b<<endl; exit(EXIT_SUCCESS); } Ejemplos: Hilos-Comunicación (III) void* sumar_thread (void*param) { sSUMANDOS* p=(sSUMANDOS*) param; suma=pÆa + pÆ b; phtread_exit(NULL); } struct sSUMANDOS {int a; int b;} ; //variable compartida en memoria int suma=0; int main(void) { sSUMANDOS sum; sum.a=10; sum.b=20; pthread_t thid; pthread_create(&thid, NULL, sumar_thread, &sum); pthread_join(NULL); cout<<“La suma es:”<<suma<<endl; exit(EXIT_SUCCESS); } Ejemplos: Hilos-DETACHED void* sumar_thread (void*param) { sleep(10); cout<<“hilo terminado”<<endl; phtread_exit(NULL); } int main(void) { pthread_t thid; pthread_create(&thid, NULL, sumar_thread, NULL); pthread_detach (thid); pthread_join(NULL); cout<<“hilo principal terminado”<<endl; exit(EXIT_SUCCESS); } SERVICIOS POSIX PARA PROCESOS LIGEROS (III) ` int pthread_attr_init (pthread_attr_t* attr) ` ` ` Inicializa la estructura de atributos de un thread a sus valores Por defecto por defecto previstos por el S.O. RETURN VALUE: 0-OK int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate) ` ` ` ` ` Establece la forma de terminar de un proceso ligero Si detachstate = PTHREAD_CREATE_DETACHED el proceso ligero libera sus recursos cuando finalice su ejecución Si detachstate = PTHREAD_CREATE_JOINABLE no se liberan los recursos. Hay que utilizar pthread_join(…) En el caso general los hilos deberían ser generados como joinable (recomendado en el standard de POSIX) RETURN VALUE: 0-OK SERVICIOS POSIX PARA PROCESOS LIGEROS (IV) ` int pthread_attr_setschedpolicy(pthread_attr_t* attr, int schedpol) ` ` ` ` ` Establece la política de planificación de un proceso ligero Tiene que tener la proiedad de PTHREAD_EXPLICIT_SCHED Si schedpol = SCHED_FIFO: política FIFO Si schedpol = SCHED_RR : política ROUND ROBIN Por defecto int pthread_attr_setinheritsched (pthread_attr_t* attr, int inheritsched) ` ` ` Establece la política de planificación en relación con el padre Si inheritsched = PTHREAD_EXPLICIT_SCHED se permite que tenga una política de planificación diferente a la del padre Si inheritsched = PTHREAD_INHERIT_SCHED se hereda la política de planificación del padre Por defecto EJEMPLO JERARQUÍA DE THREADS (I) LIBRERÍA THREAD ATRIBUTOS EJEMPLO THREADS (II) ¿PASO DE PARÁMETROS? ATRIBUTO DETACHED ESPERA A TERMINACIÓN DE HIJOS FIN DE SERVICIOS POSIX PARA HILOS