En informática , un hilo de ejecución es la secuencia más pequeño de instrucciones programadas que se pueden administrar de forma independiente por un programador , que es típicamente una parte del sistema operativo . [1] La implementación de subprocesos y procesos difiere entre sistemas operativos, pero en la mayoría de los casos un subproceso es un componente de un proceso. Los múltiples subprocesos de un proceso dado se pueden ejecutar simultáneamente (a través de capacidades de subprocesos múltiples ), compartiendo recursos como la memoria, mientras que los diferentes procesos no comparten estos recursos. En particular, los subprocesos de un proceso comparten su código ejecutable y los valores de sus variables asignadas dinámicamente y las variables globales no locales del subproceso en un momento dado.
Esta sección necesita expansión . Puedes ayudar agregando más . ( Febrero de 2021 ) |
Los subprocesos hicieron una aparición temprana bajo el nombre de "tareas" en OS / 360 Multiprogramming with a Variable Number of Tasks (MVT) en 1967. Saltzer (1966) acredita a Victor A. Vyssotsky con el término "thread". [2]
La popularidad de los subprocesos aumentó alrededor de 2003, ya que el crecimiento de la frecuencia de la CPU fue reemplazado por el crecimiento del número de núcleos, lo que a su vez requirió concurrencia para utilizar múltiples núcleos. [3]
La programación se puede realizar a nivel de kernel o de usuario, y la multitarea se puede realizar de forma preventiva o cooperativa. Esto produce una variedad de conceptos relacionados.
A nivel del kernel, un proceso contiene uno o más subprocesos del kernel , que comparten los recursos del proceso, como la memoria y los identificadores de archivos: un proceso es una unidad de recursos, mientras que un subproceso es una unidad de programación y ejecución. La programación del núcleo se realiza típicamente de manera uniforme de manera preventiva o, con menos frecuencia, de manera cooperativa. A nivel de usuario, un proceso como un sistema en tiempo de ejecución puede programar múltiples subprocesos de ejecución. Si estos no comparten datos, como en Erlang, generalmente se denominan procesos de manera análoga, [4] mientras que si comparten datos, generalmente se denominan subprocesos (de usuario) , sobre todo si se programan de forma preventiva. Los subprocesos de usuario programados cooperativamente se conocen como fibras; diferentes procesos pueden programar hilos de usuario de manera diferente. Los hilos de usuario pueden ser ejecutados por hilos del kernel de varias formas (uno a uno, muchos a uno, muchos a muchos). El término " proceso liviano " se refiere de diversas formas a los hilos del usuario oa los mecanismos del kernel para programar los hilos del usuario en los hilos del kernel.
Un proceso es una unidad "pesada" de programación del núcleo, ya que crear, destruir y cambiar procesos es relativamente caro. Procesa los recursos propios asignados por el sistema operativo. Los recursos incluyen memoria (tanto para código como para datos), identificadores de archivos , sockets, identificadores de dispositivos, ventanas y un bloque de control de procesos . Los procesos se aíslan mediante el aislamiento de procesos y no comparten espacios de direcciones ni recursos de archivos, excepto a través de métodos explícitos, como la herencia de identificadores de archivos o segmentos de memoria compartida, o la asignación del mismo archivo de forma compartida; consulte Comunicación entre procesos.. Crear o destruir un proceso es relativamente costoso, ya que los recursos deben adquirirse o liberarse. Los procesos suelen ser multitarea preventivamente y la conmutación de procesos es relativamente costosa, más allá del costo básico de la conmutación de contexto , debido a problemas como el vaciado de la caché (en particular, la conmutación de procesos cambia el direccionamiento de la memoria virtual, lo que provoca la invalidación y, por lo tanto, el vaciado de un búfer de búsqueda de traducción sin etiquetar . especialmente en x86).
Un hilo del kernel es una unidad "liviana" de programación del kernel. Existe al menos un hilo del núcleo dentro de cada proceso. Si existen varios subprocesos del kernel dentro de un proceso, entonces comparten la misma memoria y recursos de archivo. Los subprocesos del kernel son preventivamente multitarea si el programador de procesos del sistema operativo es preventivo. Los subprocesos del kernel no poseen recursos, excepto una pila , una copia de los registros, incluido el contador del programa , y el almacenamiento local del subproceso.(si los hay) y, por lo tanto, son relativamente baratos de crear y destruir. La conmutación de subprocesos también es relativamente barata: requiere un cambio de contexto (guardar y restaurar registros y puntero de pila), pero no cambia la memoria virtual y, por lo tanto, es compatible con la caché (dejando TLB válido). El kernel puede asignar un subproceso a cada núcleo lógico en un sistema (porque cada procesador se divide en varios núcleos lógicos si admite múltiples subprocesos, o solo admite un núcleo lógico por núcleo físico si no lo hace), y puede intercambiar subprocesos que bloquearse. Sin embargo, los subprocesos del kernel tardan mucho más en intercambiarse que los subprocesos del usuario.
Los subprocesos a veces se implementan en bibliotecas de espacio de usuario , por lo que se denominan subprocesos de usuario . El kernel no los conoce, por lo que se gestionan y programan en el espacio de usuario . Algunas implementaciones basan sus subprocesos de usuario en la parte superior de varios subprocesos del kernel, para beneficiarse de las máquinas multiprocesador ( modelo M: N ). Los subprocesos de usuario implementados por máquinas virtuales también se denominan subprocesos verdes .
Como las implementaciones de subprocesos de usuario suelen estar completamente en el espacio de usuario , el cambio de contexto entre subprocesos de usuario dentro del mismo proceso es extremadamente eficiente porque no requiere ninguna interacción con el kernel en absoluto: se puede realizar un cambio de contexto guardando localmente los registros de CPU utilizados por el actualmente ejecutando hilo o fibra del usuario y luego cargando los registros requeridos por el hilo o fibra del usuario para ser ejecutados. Dado que la programación se realiza en el espacio de usuario, la política de programación se puede adaptar más fácilmente a los requisitos de la carga de trabajo del programa.
Sin embargo, el uso de llamadas al sistema de bloqueo en subprocesos de usuario (a diferencia de subprocesos del kernel) puede ser problemático. Si un hilo de usuario o una fibra realiza una llamada al sistema que se bloquea, los otros hilos y fibras del usuario en el proceso no pueden ejecutarse hasta que vuelva la llamada al sistema. Un ejemplo típico de este problema es cuando se realizan E / S: la mayoría de los programas están escritos para realizar E / S sincrónicamente. Cuando se inicia una operación de E / S, se realiza una llamada al sistema y no regresa hasta que se haya completado la operación de E / S. En el período intermedio, todo el proceso es "bloqueado" por el kernel y no puede ejecutarse, lo que impide que se ejecuten otros hilos y fibras del usuario en el mismo proceso.
Una solución común a este problema (utilizada, en particular, por muchas de las implementaciones de subprocesos ecológicos) es proporcionar una API de E / S que implementa una interfaz que bloquea el subproceso de llamada, en lugar de todo el proceso, mediante el uso de E / S sin bloqueo. internamente y programar otro hilo o fibra de usuario mientras la operación de E / S está en progreso. Se pueden proporcionar soluciones similares para otras llamadas al sistema de bloqueo. Alternativamente, el programa se puede escribir para evitar el uso de E / S síncronas u otras llamadas al sistema de bloqueo (en particular, utilizando E / S sin bloqueo, incluidas las continuaciones lambda y / o primitivas asíncronas / de espera [5] ).
Las fibras son una unidad de programación aún más liviana que se programan de manera cooperativa : una fibra en funcionamiento debe " ceder " explícitamente para permitir que se ejecute otra fibra, lo que hace que su implementación sea mucho más fácil que los hilos del núcleo o del usuario . Se puede programar una fibra para que se ejecute en cualquier hilo del mismo proceso. Esto permite que las aplicaciones obtengan mejoras de rendimiento administrando la programación por sí mismas, en lugar de depender del programador del kernel (que puede no estar ajustado para la aplicación). Los entornos de programación paralela como OpenMP suelen implementar sus tareas a través de fibras. Estrechamente relacionadas con las fibras están las corrutinas, con la distinción de que las corrutinas son una construcción a nivel de lenguaje, mientras que las fibras son una construcción a nivel de sistema.
Los subprocesos se diferencian de los procesos tradicionales del sistema operativo multitarea en varias formas:
Se dice que sistemas como Windows NT y OS / 2 tienen subprocesos baratos y procesos costosos ; en otros sistemas operativos no hay una diferencia tan grande excepto en el costo de un conmutador de espacio de direcciones , que en algunas arquitecturas (notablemente x86 ) da como resultado una descarga de búfer de búsqueda de traducción (TLB).
Las ventajas y desventajas de los subprocesos frente a los procesos incluyen:
Los sistemas operativos programan subprocesos de forma preventiva o cooperativa . Los sistemas operativos multiusuario generalmente favorecen el subproceso múltiple preventivo por su control más detallado sobre el tiempo de ejecución a través de la conmutación de contexto . Sin embargo, la programación preventiva puede cambiar de contexto los subprocesos en momentos no anticipados por los programadores, lo que provoca bloqueos de convoy , inversión de prioridad u otros efectos secundarios. Por el contrario, el multiproceso cooperativo se basa en los subprocesos para ceder el control de la ejecución, lo que garantiza que los subprocesos se ejecuten hasta su finalización . Esto puede causar problemas si un subproceso multitarea cooperativo bloqueaesperando un recurso o si deja de lado a otros subprocesos al no ceder el control de la ejecución durante el cálculo intensivo.
Hasta principios de la década de 2000, la mayoría de las computadoras de escritorio tenían solo una CPU de un solo núcleo, sin soporte para subprocesos de hardware , aunque los subprocesos todavía se usaban en tales computadoras porque el cambio entre subprocesos era generalmente más rápido que los cambios de contexto de proceso completo . En 2002, Intel agregó soporte para multiproceso simultáneo al procesador Pentium 4 , bajo el nombre de hiperproceso ; en 2005, introdujeron el procesador Pentium D de doble núcleo y AMD presentó el procesador Athlon 64 X2 de doble núcleo .
Los sistemas con un solo procesador generalmente implementan subprocesos múltiples por división de tiempo : la unidad central de procesamiento (CPU) conmuta entre diferentes subprocesos de software . Este cambio de contexto generalmente ocurre con la frecuencia suficiente para que los usuarios perciban que los subprocesos o tareas se ejecutan en paralelo (para los sistemas operativos de servidor / escritorio populares, el intervalo de tiempo máximo de un subproceso, cuando otros subprocesos están esperando, a menudo se limita a 100-200ms). En un sistema multiprocesador o multinúcleo , se pueden ejecutar varios subprocesos en paralelo , con cada procesador o núcleo ejecutando un subproceso separado simultáneamente; en un procesador o núcleo con subprocesos de hardware, los subprocesos de software separados también se pueden ejecutar al mismo tiempo por subprocesos de hardware separados.
Los subprocesos creados por el usuario en una correspondencia 1: 1 con entidades programables en el kernel [6] son la implementación de subprocesos más simple posible. OS / 2 y Win32 utilizaron este enfoque desde el principio, mientras que en Linux, la biblioteca GNU C implementa este enfoque (a través de NPTL o LinuxThreads más antiguos ). Este enfoque también lo utilizan Solaris , NetBSD , FreeBSD , macOS e iOS .
Un modelo N : 1 implica que todos los subprocesos a nivel de aplicación se asignan a una entidad programada a nivel de kernel; [6] el kernel no tiene conocimiento de los subprocesos de la aplicación. Con este enfoque, el cambio de contexto se puede realizar muy rápidamente y, además, se puede implementar incluso en núcleos simples que no admiten subprocesos. Sin embargo, uno de los principales inconvenientes es que no puede beneficiarse de la aceleración de hardware en procesadores multiproceso o computadoras multiprocesador : nunca se programa más de un subproceso al mismo tiempo. [6] Por ejemplo: si uno de los subprocesos necesita ejecutar una solicitud de E / S, todo el proceso se bloquea y no se puede utilizar la ventaja del subproceso. losGNU Portable Threads utiliza subprocesos a nivel de usuario, al igual que State Threads .
M : N asigna un número M de subprocesos de aplicación a un número N de entidades del núcleo, [6] o "procesadores virtuales". Este es un compromiso entre subprocesos a nivel de kernel ("1: 1") y a nivel de usuario (" N : 1"). En general, los sistemas de subprocesos " M : N " son más complejos de implementar que los subprocesos del kernel o del usuario, porque se requieren cambios tanto en el kernel como en el código del espacio de usuario [se necesita aclaración ]. En la implementación M: N, la biblioteca de subprocesos es responsable de programar los subprocesos de usuario en las entidades programables disponibles; esto hace que el cambio de contexto de subprocesos sea muy rápido, ya que evita llamadas al sistema. Sin embargo, esto aumenta la complejidad y la probabilidad de inversión de prioridades , así como la programación subóptima sin una coordinación extensa (y costosa) entre el programador del área de usuario y el programador del kernel.
SunOS 4.x implementó procesos ligeros o LWP. NetBSD 2.x + y DragonFly BSD implementan LWP como subprocesos del núcleo (modelo 1: 1). SunOS 5.2 a SunOS 5.8 así como NetBSD 2 a NetBSD 4 implementaron un modelo de dos niveles, multiplexando uno o más subprocesos de nivel de usuario en cada subproceso del kernel (modelo M: N). SunOS 5.9 y posteriores, así como NetBSD 5 eliminaron el soporte de subprocesos de usuario, volviendo a un modelo 1: 1. [9] FreeBSD 5 implementó el modelo M: N. FreeBSD 6 soportaba tanto 1: 1 como M: N, los usuarios podían elegir cuál debería usarse con un programa dado usando /etc/libmap.conf. A partir de FreeBSD 7, el 1: 1 se convirtió en el predeterminado. FreeBSD 8 ya no es compatible con el modelo M: N.
En programación de computadoras , un solo subproceso es el procesamiento de un comando a la vez. [10] En el análisis formal de la semántica de las variables y el estado del proceso, el término subproceso único se puede usar de manera diferente para significar "retroceso dentro de un solo subproceso", que es común en la comunidad de programación funcional . [11]
El subproceso múltiple se encuentra principalmente en sistemas operativos multitarea. El subproceso múltiple es un modelo de ejecución y programación generalizado que permite que existan varios subprocesos dentro del contexto de un proceso. Estos subprocesos comparten los recursos del proceso, pero pueden ejecutarse de forma independiente. El modelo de programación con subprocesos proporciona a los desarrolladores una útil abstracción de la ejecución concurrente. El subproceso múltiple también se puede aplicar a un proceso para permitir la ejecución en paralelo en un sistema de multiprocesamiento .
Las bibliotecas de subprocesos múltiples tienden a proporcionar una llamada de función para crear un nuevo subproceso, que toma una función como parámetro. Luego se crea un subproceso concurrente que comienza a ejecutar la función pasada y finaliza cuando la función regresa. Las bibliotecas de subprocesos también ofrecen funciones de sincronización de datos.
Los subprocesos del mismo proceso comparten el mismo espacio de direcciones. Esto permite la ejecución simultánea código para pareja firmemente y convenientemente intercambiar datos sin el añadido o la complejidad de un IPC . Sin embargo, cuando se comparten entre subprocesos, incluso las estructuras de datos simples se vuelven propensas a las condiciones de carrera si requieren más de una instrucción de CPU para actualizarse: dos subprocesos pueden terminar intentando actualizar la estructura de datos al mismo tiempo y encontrarla cambiando inesperadamente. Los errores causados por las condiciones de la carrera pueden ser muy difíciles de reproducir y aislar.
Para evitar esto, las interfaces de programación de aplicaciones (API) de subprocesos ofrecen primitivas de sincronización , como mutex, para bloquear las estructuras de datos contra el acceso concurrente. En sistemas monoprocesador, un subproceso que se ejecuta en un mutex bloqueado debe dormir y, por lo tanto, activar un cambio de contexto. En sistemas multiprocesador, el hilo puede sondear el mutex en un spinlock . Ambos pueden afectar el rendimiento y obligar a los procesadores en sistemas de multiprocesamiento simétrico (SMP) a competir por el bus de memoria, especialmente si la granularidad del bloqueo es demasiado fina.
Otras API de sincronización incluyen variables de condición , secciones críticas , semáforos y monitores .
Un patrón de programación popular que involucra subprocesos es el de los grupos de subprocesos donde se crea un número determinado de subprocesos al inicio que luego esperan a que se asigne una tarea. Cuando llega una nueva tarea, se despierta, completa la tarea y vuelve a esperar. Esto evita las funciones relativamente costosas de creación y destrucción de subprocesos para cada tarea realizada y quita la administración de subprocesos de la mano del desarrollador de la aplicación y la deja en una biblioteca o el sistema operativo que sea más adecuado para optimizar la administración de subprocesos.
Las aplicaciones multiproceso tienen las siguientes ventajas frente a las de un solo subproceso:
Las aplicaciones multiproceso tienen los siguientes inconvenientes:
Muchos lenguajes de programación admiten subprocesos de alguna manera.
Wikiversity tiene recursos de aprendizaje sobre procesos e hilos en Sistemas operativos / Procesos y subprocesos |