En informática , un cambio de contexto es el proceso de almacenar el estado de un proceso o hilo , de modo que pueda restaurarse y reanudar la ejecución en un punto posterior, y luego restaurar un estado diferente, previamente guardado. [1] Esto permite que varios procesos compartan una sola unidad central de procesamiento (CPU), y es una característica esencial de un sistema operativo multiprogramación o multitarea . En una CPU tradicional, cada proceso (un programa en ejecución) utiliza los diversos registros de la CPU para almacenar datos y mantener el estado actual del proceso en ejecución. Sin embargo, en un sistema operativo multitarea, el sistema operativo cambia entre procesos o hilos para permitir la ejecución de múltiples procesos simultáneamente. [2] Para cada cambio, el sistema operativo debe guardar el estado del proceso que se está ejecutando actualmente, seguido de la carga del siguiente estado del proceso, que se ejecutará en la CPU. Esta secuencia de operaciones que almacena el estado del proceso en ejecución y carga el siguiente proceso en ejecución se denomina cambio de contexto.
El significado preciso de la frase "cambio de contexto" varía. En un contexto multitarea, se refiere al proceso de almacenar el estado del sistema para una tarea, de modo que esa tarea se pueda pausar y se pueda reanudar otra tarea. Un cambio de contexto también puede ocurrir como resultado de una interrupción , como cuando una tarea necesita acceder al almacenamiento en disco , liberando tiempo de CPU para otras tareas. Algunos sistemas operativos también requieren un cambio de contexto para moverse entre las tareas en modo usuario y modo kernel . El proceso de cambio de contexto puede tener un impacto negativo en el rendimiento del sistema. [3] : 28
Los cambios de contexto suelen requerir un uso intensivo de recursos computacionales, y gran parte del diseño de los sistemas operativos tiene como objetivo optimizar el uso de los cambios de contexto. Cambiar de un proceso a otro requiere una cierta cantidad de tiempo para realizar tareas administrativas (guardar y cargar registros y mapas de memoria, actualizar varias tablas y listas, etc.). Lo que realmente implica un cambio de contexto depende de las arquitecturas, los sistemas operativos y la cantidad de recursos compartidos (los subprocesos que pertenecen al mismo proceso comparten muchos recursos en comparación con procesos no relacionados que no cooperan).
Por ejemplo, en el núcleo Linux , el cambio de contexto implica cargar el bloque de control de proceso (PCB) correspondiente almacenado en la tabla PCB en la pila del núcleo para recuperar información sobre el estado del nuevo proceso. La información del estado de la CPU, incluidos los registros, el puntero de pila y el contador de programa , así como la información de gestión de memoria, como las tablas de segmentación y las tablas de páginas (a menos que el proceso anterior comparta la memoria con el nuevo), se cargan desde el PCB para el nuevo proceso. Para evitar una traducción de dirección incorrecta en el caso de que los procesos anteriores y actuales utilicen una memoria diferente, se debe vaciar el búfer de búsqueda de traducción (TLB). Esto afecta negativamente al rendimiento porque cada referencia de memoria al TLB será un error porque está vacío después de la mayoría de los cambios de contexto. [4] [5]
Además, el cambio de contexto análogo ocurre entre subprocesos de usuario , en particular subprocesos verdes , y suele ser muy ligero, ya que ahorra y restaura un contexto mínimo. En casos extremos, como cambiar entre goroutines en Go , un cambio de contexto es equivalente a un yield de corrutina , que es solo marginalmente más costoso que una llamada a subrutina .
Hay tres posibles desencadenantes de un cambio de contexto:
Lo más común, dentro de algún esquema de programación , es que un proceso deba salir de la CPU para que otro proceso pueda ejecutarse. Este cambio de contexto puede ser provocado por el proceso que se vuelve inejecutable, por ejemplo, al esperar a que se complete una operación de E/S o de sincronización . En un sistema multitarea preventivo , el programador también puede sacar de la CPU procesos que aún se pueden ejecutar. Para evitar que otros procesos se queden sin tiempo de CPU, los programadores preventivos suelen configurar una interrupción del temporizador para que se active cuando un proceso excede su franja de tiempo . Esta interrupción garantiza que el programador obtendrá el control para realizar un cambio de contexto.
Las arquitecturas modernas se basan en interrupciones . Esto significa que si la CPU solicita datos de un disco, por ejemplo, no necesita esperar hasta que finalice la lectura; puede enviar la solicitud (al dispositivo de E/S) y continuar con alguna otra tarea. Cuando finaliza la lectura, la CPU puede ser interrumpida (por un hardware en este caso, que envía una solicitud de interrupción a PIC ) y presentarle la lectura. Para las interrupciones, se instala un programa llamado manejador de interrupciones , y es el manejador de interrupciones el que maneja la interrupción desde el disco.
Cuando se produce una interrupción, el hardware cambia automáticamente una parte del contexto (al menos lo suficiente para permitir que el controlador vuelva al código interrumpido). El controlador puede guardar contexto adicional, dependiendo de los detalles de los diseños de hardware y software particulares. A menudo, solo se cambia una parte mínima del contexto para minimizar la cantidad de tiempo empleado en el manejo de la interrupción. El núcleo no genera ni programa un proceso especial para manejar las interrupciones, sino que el controlador se ejecuta en el contexto (a menudo parcial) establecido al comienzo del manejo de la interrupción. Una vez que se completa el servicio de interrupción, se restaura el contexto vigente antes de que ocurriera la interrupción para que el proceso interrumpido pueda reanudar la ejecución en su estado adecuado.
Cuando el sistema pasa del modo usuario al modo kernel , no es necesario un cambio de contexto; una transición de modo no es en sí misma un cambio de contexto. Sin embargo, según el sistema operativo, también puede producirse un cambio de contexto en este momento.
Se debe guardar el estado del proceso que se está ejecutando actualmente para que pueda restaurarse cuando se reprograme para su ejecución.
El estado del proceso incluye todos los registros que el proceso puede estar utilizando, especialmente el contador de programa , además de cualquier otro dato específico del sistema operativo que pueda ser necesario. Esto suele almacenarse en una estructura de datos denominada bloque de control de proceso (PCB) o switchframe .
La PCB puede almacenarse en una pila por proceso en la memoria del núcleo (en contraposición a la pila de llamadas del modo usuario ), o puede haber alguna estructura de datos específica definida por el sistema operativo para esta información. Se agrega un identificador de la PCB a una cola de procesos que están listos para ejecutarse, a menudo llamada cola de procesos listos .
Dado que el sistema operativo ha suspendido efectivamente la ejecución de un proceso, puede cambiar de contexto eligiendo un proceso de la cola de procesos listos y restaurando su PCB. Al hacerlo, se carga el contador de programa de la PCB y, por lo tanto, la ejecución puede continuar en el proceso elegido. La prioridad del proceso y del subproceso puede influir en qué proceso se elige de la cola de procesos listos (es decir, puede ser una cola de prioridad ).
Los detalles varían según la arquitectura y el sistema operativo, pero estos son escenarios comunes.
Considerando una operación de suma aritmética general A = B+1. La instrucción se almacena en el registro de instrucciones y el contador de programa se incrementa. A y B se leen de la memoria y se almacenan en los registros R1, R2 respectivamente. En este caso, B+1 se calcula y se escribe en R1 como la respuesta final. Esta operación, como hay lecturas y escrituras secuenciales y no hay esperas para las llamadas de función utilizadas, no se produce ningún cambio de contexto/espera en este caso.
Supongamos que se está ejecutando un proceso A y se produce una interrupción del temporizador. Los registros de usuario (contador de programa, puntero de pila y registro de estado) del proceso A se guardan implícitamente por la CPU en la pila del núcleo de A. A continuación, el hardware cambia al modo de núcleo y salta al controlador de interrupciones para que el sistema operativo tome el control. A continuación, el sistema operativo llama a la switch()
rutina para guardar primero los registros de usuario de propósito general de A en la pila del núcleo de A, luego guarda los valores de registro de núcleo actuales de A en la PCB de A, restaura los registros de núcleo desde la PCB del proceso B y cambia el contexto, es decir, cambia el puntero de pila del núcleo para que apunte a la pila del núcleo del proceso B. A continuación, el sistema operativo vuelve de la interrupción. A continuación, el hardware carga los registros de usuario desde la pila del núcleo de B, cambia al modo de usuario y comienza a ejecutar el proceso B desde el contador de programa de B. [6]
El cambio de contexto en sí mismo tiene un costo en el rendimiento, debido a la ejecución del programador de tareas , los vaciados de TLB e indirectamente debido a compartir la memoria caché de la CPU entre múltiples tareas. [7] El cambio entre subprocesos de un solo proceso puede ser más rápido que entre dos procesos separados porque los subprocesos comparten los mismos mapas de memoria virtual , por lo que no es necesario un vaciado de TLB. [8]
El tiempo que transcurre entre dos procesos separados se denomina latencia de conmutación de proceso . El tiempo que transcurre entre dos subprocesos del mismo proceso se denomina latencia de conmutación de subproceso . El tiempo transcurrido desde que se genera una interrupción de hardware hasta que se atiende la interrupción se denomina latencia de interrupción .
Cambiar entre dos procesos en un sistema operativo con un solo espacio de direcciones puede ser más rápido que cambiar entre dos procesos en un sistema operativo con espacios de direcciones privados por proceso. [9]
El cambio de contexto se puede realizar principalmente por software o hardware. Algunos procesadores, como el Intel 80386 y sus sucesores, [10] tienen soporte de hardware para cambios de contexto, haciendo uso de un segmento de datos especial denominado segmento de estado de tarea (TSS). Un cambio de tarea se puede activar explícitamente con una instrucción CALL o JMP dirigida a un descriptor TSS en la tabla de descriptores globales . Puede ocurrir de forma implícita cuando se activa una interrupción o excepción si hay una puerta de tarea en la tabla de descriptores de interrupción (IDT). Cuando se produce un cambio de tarea, la CPU puede cargar automáticamente el nuevo estado desde el TSS.
Al igual que con otras tareas realizadas en hardware, se esperaría que esto fuera bastante rápido; sin embargo, los sistemas operativos convencionales, incluidos Windows y Linux , [11] no utilizan esta característica. Esto se debe principalmente a dos razones:
TS
, aunque el bit se activa automáticamente en el CR0
registro de control , lo que genera una falla al ejecutar instrucciones de punto flotante y le da al sistema operativo la oportunidad de guardar y restaurar el estado de punto flotante según sea necesario).El cambio de contexto es la base del acto de malabarismo entre procesos. Consiste en detener el cálculo actual, guardar suficiente información para poder reiniciarlo más tarde y reiniciar otro proceso.