¿Qué es el Kernel? Espacio del Kernel y espacio de usuario

Hemos hablado hasta cierto punto de los sistemas operativos y de cómo se ven desde el punto de vista del programador, a través de librerías estándar.

En uno de los anteriores artículos, cuando explicábamos un primer programa en C, hacíamos una referencia algo vaga a la programación de aplicaciones en contraposición a la programación de sistemas operativos, así:

[...] Eso no quiere decir que exista una forma práctica de ejecutar la versión en lenguaje C de paridad, o el “Hello, world!” en un ordenador sin sistema operativo; la forma de programar, construir y compilar sistemas operativos es algo que no se parece a lo que hemos visto en este artículo: que no se hace igual. Sin embargo, la capacidad está ahí, y la inmensa mayoría de los sistemas operativos modernos están escritos en C o en sus sucesores (como C++ y Objective C).

Hoy vamos a ver una de las características de los microprocesadores y de los sistemas operativos que explican esas diferencias fundamentales.

Modo kernel y modo usuario en el microprocesador

Los microprocesadores tienen dos modos de funcionamiento, que definen las instrucciones que un programa puede ejecutar. El modo kernel da acceso a todas las instrucciones del microprocesador, mientras que el modo usuario permite utilizar un conjunto más reducido de las instrucciones, impidiendo el acceso a las instrucciones de control de dispositivos físicos y de gestión de la memoria.

El modo del microprocesador se cambia de kernel a usuario, o viceversa, escribiendo un valor concreto en uno de los registros del microprocesador. Los detalles exactos no importan a este nivel, pero lo que sí conviene entender es que estos modos existen para proteger la estabilidad del ordenador en una sesión de trabajo: si un programa ejecuta instrucciones de Entrada / Salida incorrectamente, o escribe en una posición de memoria que no le pertenece, la estabilidad de la sesión de trabajo se verá comprometida casi con total seguridad, haciendo que el ordenador o alguno de sus componentes se comporten de forma errática, o que el usuario pierda información porque un programa se cuelgue. Si, por el contrario, un proceso pudiese leer posiciones de memoria arbitrariamente, tendríamos problemas de privacidad y seguridad muy serios.

Así pues, es de esperar que gran parte del sistema operativo se ejecute en modo kernel y que, además, gobierne las situaciones bajo las cuales el microprocesador cambie su estado entre modo kernel y modo usuario.

El kernel del sistema operativo y los controladores

Cuando se enciende un ordenador, parte de la placa base contiene una serie de instrucciones para cargar un sistema operativo disponible en alguno de los dispositivos de almacenamiento. Sin entrar, hoy, en ese proceso de carga, el sistema operativo se inicia ejecutando una secuencia que le permite descubrir y apoderarse de los dispositivos presentes en el ordenador: las características avanzadas del procesador, la memoria, unidades de almacenamiento de datos, las tarjetas de ampliación como las tarjetas de sonido y gráfica, las tarjetas de red, puertos de entrada / salida como los puertos USB, y así hasta que ya no quedan más componentes que encontrar.

El proceso que lleva a cabo este descubrimiento y que, más adelante, pondrá todos esos recursos y dispositivos al alcance del resto de programas, se llama núcleo, o en inglés, kernel, que es la palabra que da nombre en inglés a las pepitas gordas, o huesos, de frutas como el melocotón o el aguacate. Por comodidad y consistencia con la mayoría de la documentación disponible, que está escrita en inglés, haré una excepción en micromáquina y llamaré a esta parte central del sistema operativo “kernel”.

El hecho de que modo kernel se llame así, y el que el componente más fundamental de los sistemas operativos también se llame kernel, no es coincidencia; como hemos visto antes, el modo kernel del microprocesador ofrece acceso a todo el repertorio de instrucciones del mismo y es fácil entender que el kernel necesita todas esas instrucciones para gobernar el funcionamiento de todos los componentes y dispositivos, incluyendo la memoria, y arbitrar el acceso a los mismos durante el funcionamiento normal del ordenador. Así pues, el kernel de un sistema operativo, como Darwin en macOS, Linux en todos los sistemas GNU/Linux, o el kernel de Windows, se ejecutan con el microprocesador en modo kernel.

El kernel utiliza una serie de controladores, o módulos especializados para entender y poder utilizar los recursos de cada dispositivo, de tal forma que los programadores del kernel se pueden concentrar en la gestión de los dispositivos a través de estos módulos, y en conseguir que todos los componentes del ordenador se ejecuten en armonía y sin conflictos como, por ejemplo, el que tendríamos si dos dispositivos accediesen a la misma zona de memoria con propósitos diferentes.

Cuando el kernel termina de apoderarse de todo lo que es capaz de entender, a base de cargar todos los controladores que tiene disponibles, inicializando todo, ¿qué es lo que ocurre?

El espacio de usuario

El espacio de usuario es lo que ocurre después de que el kernel haga su trabajo inicial de inicializarlo todo. El kernel da paso a los programas diseñados para atender a las necesidades de los usuarios finales.

Suponiendo que el sistema operativo inicia un intérprete de comandos o un interfaz gráfico de alguna forma, el usuario podrá solicitar la carga de sus programas escribiendo comandos o utilizando el ratón.

Cuando esto ocurre, el kernel busca memoria libre suficiente para cargar el proceso ejecutable en memoria. Si el sistema tiene memoria disponible suficiente, carga el proceso y le cede control.

“Ceder el control” a un proceso cargado en memoria es un proceso que conlleva una serie de operaciones. El kernel decide en qué modo de microprocesador debe ejecutarse dicho proceso, usuario en la mayoría de los casos, y kernel para aquellos procesos que formen parte del sistema operativo como los controladores de dispositivo, y solicita al microprocesador un cambio de contexto para el nuevo proceso predeterminando dicho modo. Este cambio de contexto guarda el estado del los registros y del proceso en general, en memoria, y restaura los valores correspondientes al proceso a ejecutar. En uno de los registros específicos, el microprocesador escribe el valor de modo que el kernel ha decido para el proceso: kernel o usuario.

Los programas que se ejecutan en modo usuario harán uso de las capacidades del ordenador mediante llamadas al sistema, a través de librerías estándar como las que ya hemos visto en artículos anteriores. Por ejemplo, un programa escrito en C que espera la entrada de texto por parte del usuario, utilizará la librería stdio para recibir ese texto. Mediante llamadas al sistema y servicios que interactúan con los controladores del dispositivo ofreciendo librerías a los programas, éstos pueden hacer todo lo que estamos acostumbrados a ver en nuestros ordenadores: representaciones gráficas, videollamadas, imprimir documentos, acceder a servicios, carpetas u otros ordenadores en red (incluyendo Internet).

Entre las llamadas al sistema más importantes encontramos aquellas que sirven para solicitar al kernel la asignación de memoria adicional. Muchas de las variables que definimos en el código del programa representan espacios de memoria que se preasignan al proceso cuando éste se carga, pero un proceso puede necesitar memoria adicional para trabajar; de hecho, casi con total seguridad la va a necesitar. La forma mediante la cual un programa, ejecutándose en modo de usuario y por lo tanto sin acceso a la memoria, puede obtener memoria para trabajar con ella, es llamando al kernel mediante una función de una librería estándar, como puede ser malloc en lenguage C. Cuando el kernel recibe estas llamadas, al estar ejecutándose en modo kernel, es capaz de asignar memoria extra al proceso para que éste haga sus cosas. Este tipo de asignación de memoria se llama asignación de memoria dinámica, en contraposición a la asignación de memoria estática, que es la que ocurre cuando usamos variables definidas como, por ejemplo, las variables siguientes en nuestro primer programa en C:

        char input[10], ch;
        int count = 0;

Conclusiones

Al principio del artículo distinguíamos la programación de aplicaciones y programas de usuario de la programación de sistemas operativos como problemas distintos, dejando entrever que siguen reglas y utilizan mecanismos diferentes.

Estas diferencias empiezan en los modos del procesador, kernel y usuario, y qué nos permite hacer el microprocesador con el resto del ordenador, en las condiciones que definen estos modos.

Conocer este tipo de cosas nos ayuda a entender no sólo cómo funciona un ordenador y su sistema operativo, pero también los conceptos y los mecanismos que entran en juego en situaciones como las infecciones por malware y los problemas de seguridad, cuando leemos cosas como que *un proceso gana acceso al espacio del kernel.

👉 Sigue a micromáquina en @gabriel@micromaquina.com 👉 Sigue a Gabriel Viso en @gabriel@fedi.gvisoc.com