En los sistemas informáticos, un cargador es la parte de un sistema operativo que se encarga de cargar programas y bibliotecas . Es una de las etapas esenciales en el proceso de inicio de un programa, ya que coloca los programas en la memoria y los prepara para su ejecución. La carga de un programa implica la asignación de memoria o la copia del contenido del archivo ejecutable que contiene las instrucciones del programa en la memoria y, a continuación, la realización de otras tareas preparatorias necesarias para preparar el ejecutable para su ejecución. Una vez que se completa la carga, el sistema operativo inicia el programa pasando el control al código del programa cargado.
Todos los sistemas operativos que admiten la carga de programas tienen cargadores, a excepción de los sistemas informáticos altamente especializados que solo tienen un conjunto fijo de programas especializados. Los sistemas integrados normalmente no tienen cargadores y, en su lugar, el código se ejecuta directamente desde la ROM o similar. Para cargar el propio sistema operativo, como parte del arranque , se utiliza un cargador de arranque especializado . En muchos sistemas operativos, el cargador reside permanentemente en la memoria, aunque algunos sistemas operativos que admiten memoria virtual pueden permitir que el cargador se ubique en una región de la memoria que sea paginable .
En el caso de los sistemas operativos que admiten memoria virtual, el cargador puede no copiar realmente el contenido de los archivos ejecutables en la memoria, sino que simplemente puede declarar al subsistema de memoria virtual que existe una correlación entre una región de memoria asignada para contener el código del programa en ejecución y el contenido del archivo ejecutable asociado. (Véase archivo asignado a memoria ). El subsistema de memoria virtual se entera entonces de que las páginas con esa región de memoria deben rellenarse a demanda si y cuando la ejecución del programa llega realmente a esas áreas de memoria no rellenadas. Esto puede significar que partes del código de un programa no se copien realmente en la memoria hasta que se utilicen realmente, y que el código no utilizado puede no cargarse nunca en la memoria.
En Unix , el cargador es el controlador de la llamada al sistema execve()
. [1] Las tareas del cargador de Unix incluyen:
_start
).En Microsoft Windows 7 y versiones posteriores, el cargador es la LdrInitializeThunk
función contenida en ntdll.dll , que hace lo siguiente:
RtlCreateHeap
);BaseThreadInitThunk
;NtContinue
al parámetro de contexto dado a la función del cargador (es decir, saltando a RtlUserThreadStart
, eso iniciará el ejecutable)Algunos sistemas operativos necesitan cargadores de reubicación , que ajustan las direcciones (punteros) en el ejecutable para compensar las variaciones en la dirección en la que comienza la carga. Los sistemas operativos que necesitan cargadores de reubicación son aquellos en los que un programa no siempre se carga en la misma ubicación en el espacio de direcciones (virtual) y en los que los punteros son direcciones absolutas en lugar de desplazamientos de la dirección base del programa . Algunos ejemplos conocidos son el OS/360 de IBM para sus mainframes System/360 y sus descendientes, incluido z/OS para los mainframes z/Architecture .
En OS/360 y sistemas descendientes, la función del sistema operativo (privilegiada) se llama IEWFETCH, [2] y es un componente interno del Supervisor del SO, mientras que la aplicación LOADER (no privilegiada) puede realizar muchas de las mismas funciones, además de las del Editor de vínculos, y es completamente externa al Supervisor del SO (aunque ciertamente utiliza muchos servicios del Supervisor).
IEWFETCH utiliza programas de canal altamente especializados y, en teoría, es posible cargar y reubicar un ejecutable completo dentro de una revolución del medio DASD (aproximadamente 16,6 ms máximo, 8,3 ms promedio, en unidades "heredadas" de 3600 rpm). Para módulos de carga que exceden el tamaño de una pista, también es posible cargar y reubicar el módulo completo sin perder una revolución del medio.
IEWFETCH también incorpora facilidades para las llamadas estructuras superpuestas, lo que facilita la ejecución de ejecutables potencialmente muy grandes en un modelo de memoria mínimo (tan pequeño como 44 KB en algunas versiones del sistema operativo, pero 88 KB y 128 KB son más comunes).
El núcleo del SO (la parte siempre residente del Supervisor) está formateado de manera que sea compatible con una versión reducida de IEWFETCH. A diferencia de los ejecutables normales, el núcleo del SO está "cargado de forma dispersa": partes del núcleo se cargan en diferentes partes de la memoria; en particular, se requiere que ciertas tablas del sistema residan por debajo de los 64 KB iniciales, mientras que otras tablas y códigos pueden residir en otro lugar.
La aplicación Linkage Editor del sistema se llama IEWL. [3] La función principal de IEWL es asociar módulos de carga (programas ejecutables) y módulos de objetos (la salida de, por ejemplo, ensambladores y compiladores), incluyendo "llamadas automáticas" a bibliotecas ("funciones integradas" de lenguaje de alto nivel), en un formato que pueda ser cargado de manera más eficiente por IEWFETCH. Hay una gran cantidad de opciones de edición, pero para una aplicación convencional solo se emplean comúnmente unas pocas de ellas.
El formato del módulo de carga incluye un "registro de texto" inicial, seguido inmediatamente por el "registro de reubicación y/o control" para ese registro de texto, seguido por más instancias de pares de registro de texto y registro de reubicación y/o control, hasta el final del módulo.
Los registros de texto suelen ser muy grandes; los registros de reubicación y/o control son pequeños, ya que los tres buffers de registros de reubicación y/o control de IEWFETCH están fijados en 260 bytes (ciertamente son posibles registros de reubicación y/o control más pequeños, pero 260 bytes es el máximo posible, e IEWL garantiza que se cumpla esta limitación, insertando registros de reubicación adicionales, según sea necesario, antes del siguiente registro de texto, si es necesario; en este caso especial, la secuencia de registros puede ser: ..., registro de texto, registro de reubicación, ..., registro de control, registro de texto, ...).
Un byte especial dentro del búfer de registro de reubicación y/o control se utiliza como un área de comunicación de "giro de bit deshabilitado", y se inicializa con un valor único. El CCW de lectura para ese registro de reubicación y/o control tiene establecido el bit de interrupción controlada por programa. De este modo, se notifica al procesador cuando el canal ha accedido a ese CCW a través de una salida especial de IOS . En este punto, el procesador ingresa al bucle de "giro de bit deshabilitado" (a veces llamado "el bucle más corto del mundo"). Una vez que ese byte cambia de su valor inicializado, la CPU sale del giro de bit y se produce la reubicación durante el "espacio" dentro del medio entre el registro de reubicación y/o control y el siguiente registro de texto. Si la reubicación finaliza antes del siguiente registro, el CCW NOP posterior a la lectura se cambiará a un TIC, y la carga y la reubicación continuarán utilizando el siguiente búfer; si no, entonces el canal se detendrá en el CCW NOP, hasta que IEWFETCH lo reinicie a través de otra salida especial de IOS. Los tres buffers están en una cola circular continua, cada uno apunta al siguiente y el último apunta al primero, y los tres buffers se reutilizan constantemente a medida que avanza la carga y la reubicación.
IEWFETCH puede, de este modo, cargar y reubicar un módulo de carga de cualquier tamaño práctico y en el mínimo tiempo posible.
Los cargadores de enlaces dinámicos son otro tipo de cargador que carga y vincula bibliotecas compartidas (como archivos .so , archivos .dll o archivos .dylib) a programas en ejecución ya cargados.
Cuando dichas bibliotecas compartidas pueden ser compartidas por múltiples procesos, con solo una única copia del código compartido posiblemente apareciendo en una dirección (virtual) diferente en el espacio de direcciones de cada proceso, se requiere que el código en la biblioteca compartida sea reubicable, es decir, la biblioteca solo debe usar direcciones internas autorrelativas o relativas a la base del segmento de código en todo momento. Algunos procesadores tienen instrucciones que pueden usar referencias de código autorrelativas para facilitar esto.