Dejando ya un poco atrás la distribución del Espacio de Direcciones, hoy vamos a afrontar una distribución a menor escala: la del código objeto. Esto es necesario hacerlo por dos motivos: el Sistema Operativo se presenta como código objeto, y tendrá que estar organizado de alguna manera para que sea rápido y fácil de desarrollar; y lo mismo ocurre con las aplicaciones que se ejecuten sobre él, para que éste sepa procesarlas.
Lo primero que habría que hacer es definir por alto qué es el código objeto. La licencia LSW indica que el código objeto es "cualquier otro conjunto de datos en el que se presente la Solución Informática, de forma inequívoca, que no se ajuste a la definición de 'código fuente'"; por lo tanto, no nos cerraremos a hablar de un conjunto de instrucciones en código máquina, sino de un conjunto mayor. A nivel práctico, catalogaremos como código objeto el propio Sistema Operativo y todo aquello que se ejecute sobre él, incluyendo funciones, métodos, y también los propios datos binarios.
Quizás esto último sea un poco extraño: ¿cómo va a ser código objeto una... fotografía? Adelantaré un poco acontecimientos: en el Sistema Operativo todo van a ser funciones. Por lo tanto, abrir una fotografía sería ejecutar una fotografía.
Una vez presentada esta revelación, y antes de poner el grito en el cielo, voy a explicar cómo el concepto del Sistema de Paquetes, que ya se presentó en las jornadas anteriores, evita pensar que ejecutar una fotografía como código objeto sea una idea de locos. Esta metodología hablaba de dividir (divide et vinces) todo en paquetes (sectores, mensajes, recursos...) es una característica que tenían en común los tres niveles en los que desde el comienzo de este proyecto segmenté la informática. El código objeto no podría librarse de ser dividido. Por lo tanto, diferenciaremos tres partes en el código objeto con unos objetivos específicos:
- Cargador o lanzador: es la parte de código máquina común del código objeto.
- Se encarga de poner el área de datos en el Espacio de Direcciones.
- Está compuesto por código máquina (ejecutable) mayoritariamente.
- Cuando se invoque el código objeto se apuntará a su primera instrucción.
- Este paquete es una parte común entre instancias (funciones, métodos, datos binarios) del mismo tipo.
- Datos o imagen: es la parte donde se almacenan datos binarios.
- Incluye parámetros, constantes, espacio para variables, o demás información.
- Está compuesto por datos binarios mayoritariamente.
- Distingue dos instancias diferentes del mismo tipo.
- Código o instrucciones: es la parte de código máquina propio del código objeto.
- Está compuesto por código máquina (ejecutable) exclusivamente.
- Sirve para incluir instrucciones propias del código objeto.
Estas tres partes no tienen porqué estar necesariamente contiguas en el Espacio de Direcciones, aunque es preferible que el cargador esté compacto. El cargador está compuesto mayoritariamente de código máquina, aunque tiene algunas áreas de datos que sirven para apuntar a las otras dos partes. En los códigos máquina funcionales, los datos reservan el sitio para ubicar las variables y constantes que se utilizarán en tiempo de ejecución, mientras que la parte de código incluirá las instrucciones para la implementación de métodos y funciones.
Indicar que tanto la parte de datos como la de código tienen un tamaño constante y conocido en tiempo de compilación. Puede que sea uno de los aspectos más radicales que presentaré, por la ausencia de una estructura de pilas en el código objeto. Lo explicaré más en detalle próximamente, pero para ir entendiéndolo con el ejemplo más claro (funciones recursivas) hay que centrarse en un concepto: instancias. Todas las funciones que se ejecutan lo hacen en forma de instancia. Cuando una función a() se llama a sí misma, no se hará literalmente; en vez de llamarse otra vez a la misma a(), lo que se hace es, como cuando se cargó la propia función a(), cargar una instancia de la función a(). Por lo tanto, tendremos dos instancias de la misma función a(), en las que la parte cargador es común, la parte de datos es propia de cada una, y (en el caso de las funciones) la parte código está vacía en ambas.
Quería hacer especial mención a una característica útil del cargador en el caso de datos binarios como fotografías. Al ser una parte común, cuando se cargue por primera vez en el Sistema Operativo un dato binario de un tipo en concreto, se instalará (guardará) en el Sistema Operativo este cargador. De esta forma, se evita la redundancia y se mejora la estandarización además de otros beneficios que veremos cuando profundicemos en los datos binarios.