Tengo mucha curiosidad ahora. Soy un programador de Python, y esta pregunta me dejó perplejo: escribes un sistema operativo. ¿Cómo lo ejecutas? Tiene que ejecutarse de alguna manera, y de esa manera está dentro de otro sistema operativo?
¿Cómo puede ejecutarse una aplicación sin estar en un sistema operativo? ¿Cómo le dice a la computadora que ejecute, digamos, C, y ejecute estos comandos en la pantalla, si no tiene un sistema operativo para ejecutar?
¿Tiene que ver con un núcleo UNIX? Si es así, ¿qué es un núcleo Unix, o un núcleo en general?
Estoy seguro de que los sistemas operativos son más complicados que eso, pero ¿cómo funciona?
kernel
operating-systems
Thor Correia
fuente
fuente
Respuestas:
Hay muchos sitios web que pasan por el proceso de arranque (como How Computers Boot Up ). En pocas palabras, es un proceso de varias etapas que sigue construyendo el sistema poco a poco hasta que finalmente puede iniciar los procesos del sistema operativo.
Comienza con el firmware de la placa base que intenta poner en funcionamiento la CPU. Luego carga el BIOS, que es como un mini sistema operativo que pone en funcionamiento el otro hardware. Una vez hecho esto, busca un dispositivo de arranque (disco, CD, etc.) y, una vez encontrado, localiza el MBR (registro de arranque maestro) y lo carga en la memoria y lo ejecuta. Es este pequeño fragmento de código que luego sabe cómo inicializar e iniciar el sistema operativo (u otros cargadores de arranque a medida que las cosas se vuelven más complicadas). Es en este punto que cosas como el núcleo se cargarán y comenzarán a ejecutarse.
¡Es increíble que funcione!
fuente
Un sistema operativo "bare metal" no funciona dentro de nada. Ejecuta el conjunto completo de instrucciones en la máquina física y tiene acceso a toda la memoria física, todos los registros del dispositivo y todas las instrucciones privilegiadas, incluidas las que controlan el hardware de soporte de memoria virtual.
(Si el sistema operativo se ejecuta en una máquina virtual, puede pensar que se encuentra en la misma situación que la anterior. La diferencia es que ciertas cosas son emuladas o de alguna otra manera manejadas por el hipervisor; es decir, el nivel que ejecuta las máquinas virtuales .)
De todos modos, aunque el sistema operativo podría implementarse en (por ejemplo) C, no tendrá todas las bibliotecas C normales disponibles. En particular, no tendrá las bibliotecas 'stdio' normales. Por el contrario, implementará (por ejemplo) un controlador de dispositivo de disco que le permite leer y escribir bloques de disco. Implementará un sistema de archivos en la parte superior de la capa de bloque de disco, y además implementará las llamadas al sistema que hacen las bibliotecas de tiempo de ejecución de una aplicación de usuario para (por ejemplo) crear, leer y escribir archivos ... y así sucesivamente.
Debe ser un tipo especial de aplicación (por ejemplo, un sistema operativo) que sepa cómo interactuar directamente con el hardware de E / S, etc.
Usted no
La aplicación (que fue por el argumento escrito en C) se compila y se vincula en alguna otra máquina para dar una imagen de código nativo. Luego, la imagen se escribe en el disco duro en un lugar donde el BIOS pueda encontrarla. El BIOS carga la imagen en la memoria y ejecuta una instrucción para saltar al punto de entrada de la aplicación.
No hay (típicamente) ningún "ejecutando C y ejecutando comandos" en la aplicación a menos que sea un sistema operativo completo. Y en ese caso, es responsabilidad del sistema operativo implementar toda la infraestructura requerida para que esto suceda. Sin magia Solo mucho código.
La respuesta de Bill cubre el arranque, que es el proceso en el que se pasa de una máquina apagada a una máquina en la que el sistema operativo normal está en funcionamiento. Sin embargo, vale la pena señalar que cuando el BIOS completa sus tareas, (por lo general) cede el control completo del hardware al sistema operativo principal, y no desempeña ningún papel adicional, hasta el próximo reinicio del sistema. El sistema operativo principal ciertamente no se ejecuta "dentro" del BIOS en el sentido convencional.
Si lo hace
El núcleo UNIX es el núcleo del sistema operativo UNIX. Es la parte de UNIX que hace todo el "metal desnudo" descrito anteriormente.
La idea de un "núcleo" es que intente separar el software del sistema en elementos básicos (que requieren acceso al dispositivo físico, toda la memoria, etc.) y elementos no básicos. El núcleo consta de las cosas principales.
En realidad, la distinción entre kernel / core y non-kernel / non-core es más complicada que eso. Y se ha debatido mucho sobre lo que realmente pertenece a un núcleo y lo que no. (Busque micro-kernel por ejemplo).
fuente
The idea of a "kernel" is that you try to separate the system software into core stuff
Fácil de recordar al señalar que el términokernel
es del alemánKern
, que significa núcleo / núcleo.Al principio no había energía en la CPU.
Y el Hombre dijo "que haya energía", y la CPU comenzó a leer de una dirección dada en la memoria y ejecutar la instrucción que estaba presente allí. Luego el siguiente y así sucesivamente hasta el final del poder.
Este fue el arranque. Su tarea consistía en cargar otra pieza de software para obtener acceso al entorno, donde estaba el software principal, y cargarlo.
Finalmente, una pantalla amigable lo invitó a iniciar sesión.
fuente
0x7C00
para cualquierx86
arquitectura compatible y primero tiene que ser completada por el BIOS, que generalmente carga el primer sector de cualquier dispositivo de arranque que prefiera ... Buena respuesta: -7Lamento llegar tarde, pero lo describiré como tal:
La placa base recibe energía.
Los circuitos de temporización comienzan y se estabilizan si es necesario, basándose únicamente en sus características eléctricas. Algunos dispositivos más nuevos pueden usar un microprocesador o secuenciador muy limitado.
- Jkerian a las 5:20 el 25 de octubre
Se le da energía a la CPU y la RAM.
La CPU carga (según su cableado interno) datos del BIOS. En algunas máquinas, el BIOS puede duplicarse en la RAM y luego ejecutarse desde allí, pero eso es raro en el IIRC.
-Micheal Steil, 17 errores cometidos por Microsoft en el sistema de seguridad de Xbox ( archivo )
El BIOS realiza llamadas a los puertos y direcciones de hardware utilizados por la placa base para el disco y otras E / S de hardware y hace girar discos, hace que el resto de la RAM funcione, entre otras cosas.
El código del BIOS (a través de la configuración de CMOS, almacenada en el hardware) usa comandos IDE o SATA de bajo nivel para leer el sector de arranque de cada disco, en un orden especificado por el CMOS o un usuario anula con un menú.
El primer disco con un sector de arranque ejecuta su sector de arranque. Este sector de arranque es ensamblado que tiene instrucciones para cargar más datos del disco, cargar una
NTLDR
etapa posterior más grandeGRUB
, etc.Finalmente, el gestor de arranque ejecuta el código de máquina del sistema operativo, directa o indirectamente a través de la carga en cadena cargando un sector de arranque desde una ubicación alternativa o desplazada.
Luego obtienes un pánico amigable del núcleo, un pingüino sofocado, o tu disco se detiene debido a un golpe en la cabeza. =) En el escenario alternativo, su kernel configura tablas de proceso, estructuras en memoria y monta discos, carga controladores, módulos y una GUI o conjunto de servicios (si está en un servidor). Luego, los programas se ejecutan a medida que se leen sus encabezados, y su ensamblaje se lleva a la memoria y se asigna en consecuencia.
fuente
Hay muchas buenas respuestas, pero quería agregar esto: usted mencionó que proviene de un fondo de Python. Python es un lenguaje no interpretado (o "interpilado" o lo que sea, al menos en los casos de uso típicos de CPython). Esto significa que tiene otro software (el intérprete de Python) mirando la fuente y ejecutándola de alguna manera. Este es un modelo excelente y permite lenguajes de alto nivel bastante agradables y bien abstraídos del hardware real. Lo malo es que siempre necesita este software de intérprete primero.
Dicho software de interpretación, por lo general, está escrito en un lenguaje que se compila en código máquina, por ejemplo, C o C ++. El código de máquina es lo que la CPU puede manejar. Lo que puede hacer una CPU es leer algunos bytes de la memoria y, según los valores de bytes, comenzar una operación específica. Entonces, una secuencia de bytes es un comando para cargar algunos datos de la memoria en un registro, otra secuencia para agregar dos valores, otra para almacenar el valor de un registro en la memoria principal y pronto (un registro es un área de memoria especial que es parte de la CPU donde puede funcionar mejor), la mayoría de estos comandos son bastante bajos en ese nivel. La lectura humana de estas instrucciones de código de máquina es el código del ensamblador. Este código de máquina, básicamente, es lo que se almacena en archivos .exe o.com en Windows o dentro de los archivos binarios de Linux / Unix.
Ahora, si se inicia una computadora, es tonto, aunque tiene algunos cables que leerán las instrucciones del código de la máquina. En una PC, esto generalmente (actualmente) es un chip EEPROM en la placa base que contiene el BIOS (sistema de salida de entrada básico), este sistema no puede hacer mucho, puede facilitar el acceso a algún hardware, etc. y luego hacer una operación clave: vaya a arranque y copie los primeros bytes (también conocido como el registro de arranque maestro, MBR) en la memoria y luego diga a la CPU "aquí está su programa", la CPU tratará esos bytes allí como código de máquina y lo ejecutará. Por lo general, este es un cargador de sistema operativo que cargará el kernel con algunos parámetros y luego entregará el control a ese kernel, que luego cargará todos sus controladores para acceder a todo el hardware, cargar algún programa de escritorio o shell o lo que sea y permitir al usuario iniciar sesión y usa el sistema.
fuente
Usted pregunta "¿Cómo puede ejecutarse una aplicación sin estar en un sistema operativo?". La respuesta fácil es "un sistema operativo no es una aplicación". Si bien un SO se puede crear con las mismas herramientas que una aplicación, y estar hecho de la misma materia prima, no son lo mismo. Un sistema operativo no tiene que jugar con las mismas reglas que una aplicación.
OTOH, puede pensar en el hardware y firmware reales como el "SO" en el que se ejecuta la "aplicación" del SO. El hardware es un sistema operativo muy simple: sabe cómo ejecutar las instrucciones escritas en el código de la máquina y sabe que cuando se inicia debe buscar una dirección de memoria muy específica para su primera instrucción. Entonces, se inicia y luego ejecuta inmediatamente la primera instrucción, seguida de la segunda, y así sucesivamente.
Por lo tanto, el sistema operativo es simplemente un código de máquina que existe en una ubicación conocida y que puede interactuar directamente con el hardware.
fuente
La respuesta a su pregunta requiere el conocimiento de cómo se ve el código nativo (para CPU) y cómo es interpretado por la CPU.
Por lo general, todo el proceso de compilación se basa en traducir cosas que escribe en C, Pascal o incluso Python (usando pypy) y C # en cosas que la CPU entiende, es decir, instrucciones simples como "almacenar algo en [dirección de memoria]", "agregar números almacenados en registros eax y ebx "," call function foo "," compare eax con 10 ". Esas instrucciones, ejecutadas una por una, hacen las cosas que querías hacer con tu código.
Ahora piense en esto: ¡realmente no necesita un sistema operativo para ejecutar este código nativo! Todo lo que necesita es cargar este código en la memoria y decirle a la CPU que está allí y que desea que se ejecute. Sin embargo, no te preocupes demasiado por eso. Ese es el trabajo del que debe preocuparse el BIOS: carga su código (solo uno y un sector), justo después de que se inicia la CPU, bajo la dirección física 0x7C00. Luego, la CPU comienza a ejecutar este sector (512 B) de su código. ¡Y puedes hacer lo que imagines! Sin, por supuesto, ningún soporte del sistema operativo. Eso es porque USTED es el sistema operativo. Genial, ¿eh? ¡Sin biblioteca estándar, sin impulso, sin python, sin programas, sin controladores! Tienes que escribir todo por ti mismo.
¿Y cómo te comunicas con el hardware? Bueno, tienes dos opciones:
Ahora estás preguntando qué es el núcleo. En breve, el núcleo es todo lo que no ve y experimenta directamente. Gestiona, junto con los controladores, todo, desde su teclado hasta casi todas las piezas de hardware dentro de su PC. Te comunicas con él por shell gráfico o terminal. O por funciones dentro de su código, ahora ejecutadas, por suerte, con soporte del sistema operativo.
Para una mejor comprensión, puedo darle un consejo: intente escribir su propio sistema operativo. Incluso si va a escribir "Hola mundo" en la pantalla.
fuente
Existen algunas diferencias en cuanto al funcionamiento de un sistema operativo que dependen mucho del sistema. Para ser útil, un sistema debe tener un comportamiento predecible al inicio, como "comenzar a ejecutar en la dirección X". Para los sistemas que tienen un almacenamiento no volátil (como la memoria Flash) mapeado en su espacio de programa, esto es bastante fácil ya que solo se asegura de colocar el código de inicio en la ubicación correcta dentro del espacio de programa del procesador. Esto es extremadamente común para los microcontroladores. Algunos sistemas tienen que recuperar sus programas de inicio desde otra ubicación antes de ejecutarlo. Estos sistemas tendrán algunas operaciones cableadas (o casi cableadas) en ellas. Hay algunos procesadores que recuperan su código de inicio a través de i2c desde otro chip,
Los sistemas que utilizan la familia de procesadores x86 suelen utilizar un proceso de arranque de varias etapas que es bastante complejo debido a su evolución y problemas de compatibilidad con versiones anteriores. El sistema ejecuta algún firmware (llamado BIOS - Sistema básico de entrada / salida, o similar) que se encuentra en alguna memoria no volátil en la placa base. A veces, parte o la totalidad de este firmware se copia (reubica) en la RAM para que se ejecute más rápido. Este código fue escrito con conocimiento de qué hardware estaría presente y utilizable para el arranque.
El firmware de inicio generalmente se escribe con suposiciones sobre qué hardware estará presente en el sistema. Hace años en una máquina 286 probablemente habría una suposición de que habría un controlador de unidad de disquete en la dirección de E / S X y cargaría el sector 0 a una determinada ubicación de memoria si se le da un determinado conjunto de comandos (y el código en el sector 0 sabe cómo usar las funciones propias del BIOS para cargar más código, y eventualmente se carga suficiente código para ser un SO). En un microcontrolador puede suponerse que hay un puerto serie que funciona con ciertas configuraciones y que debe esperar los comandos (para actualizar el firmware más complejo) durante X cantidad de tiempo antes de continuar con el proceso de arranque.
El proceso exacto de inicio de un sistema dado no es tan importante para usted como saber que difiere en los diferentes sistemas, pero también que todos tienen cosas en común. A menudo, dentro del código de inicio (bootstrapping) cuando se necesita hacer E / S, los dispositivos de E / S se sondean en lugar de depender de interrupciones. Esto se debe a que las interrupciones son complejas, utilice la memoria RAM de la pila (que puede no estar completamente configurada todavía) y no necesita preocuparse por bloquear otras operaciones cuando es la única operación.
Al cargarse por primera vez, el núcleo del sistema operativo (el núcleo es la parte principal de la mayoría de los sistemas operativos) inicialmente actuará de manera muy similar al firmware. Tendrá que ser programado con conocimiento o descubrir hardware presente, configurar algo de RAM como espacio de pila, hacer varias pruebas, configurar varias estructuras de datos, posiblemente descubrir y montar un sistema de archivos, y luego probablemente iniciar algún programa que sea más como los programas que está acostumbrado a escribir (un programa que se basa en un sistema operativo presente).
El código del sistema operativo generalmente se escribe en una mezcla de C y ensamblaje. El primer código para el kernel del sistema operativo probablemente siempre esté en ensamblado y hace cosas como configurar la pila, en la que se basa el código C, y luego llama a una función C. También habrá otro ensamblaje escrito a mano porque algunas operaciones que un sistema operativo debe realizar a menudo no se pueden expresar en C (como cambio de contexto / intercambio de pilas). A menudo, se deben pasar banderas especiales al compilador de C para decirle que no confíe en las bibliotecas estándar que utilizan la mayoría de los programas de C y que no espere que haya un
int main(int argc, char *argv[])
en el programa. Además, se deben usar opciones de enlazador especiales que la mayoría de los programadores de aplicaciones nunca usan. Esto puede hacer que el programa del kernel espere cargarse en una determinada dirección o configurar cosas para que parezcan que hay variables externas en ciertas ubicaciones a pesar de que esas variables nunca se declararon en ningún código C (esto es útil para E / S mapeadas en memoria o otras ubicaciones especiales de memoria).Al principio, toda la operación parece mágica, pero después de mirarla y comprender partes de ella, la magia se convierte en un conjunto de programas que requieren mucha más planificación y conocimiento del sistema para su implementación. Sin embargo, depurarlos requiere magia.
fuente
Para comprender cómo funcionan los sistemas operativos, puede ser útil dividirlos en dos categorías: aquellos que simplemente brindan servicios a las aplicaciones a pedido y aquellos que usan funciones de hardware en la CPU para evitar que las aplicaciones hagan cosas que no deberían. MS-DOS era del estilo anterior; Todas las versiones de Windows desde 3.0 han sido el último estilo (al menos cuando se ejecuta algo más potente que un 8086).
La PC original de IBM con PC-DOS o MS-DOS habría sido un ejemplo del estilo anterior de "SO". Si una aplicación quisiera mostrar un personaje en la pantalla, habría habido algunas formas de hacerlo. Podría llamar a la rutina que le pediría a MS-DOS que la envíe a "salida estándar". Si lo hiciera, MS-DOS comprobaría si la salida se estaba redirigiendo y, si no, llamaría a una rutina almacenada en la ROM (en una colección de rutinas que IBM llamó el Sistema básico de entrada / salida) que mostraría un carácter en el posición del cursor y mueva el cursor ("escribir teletipo"). Esa rutina de BIOS almacenaría un par de bytes en algún lugar en el rango de 0xB800: 0 a 0xB800: 3999; el hardware del Adaptador de gráficos en color buscará repetidamente pares de bytes dentro de ese rango, usando el primer byte de cada par para seleccionar la forma de un personaje y el segundo para seleccionar los colores de primer plano y fondo. Los bytes se obtienen y procesan en señales rojas, verdes y azules, en una secuencia que produce una visualización de texto legible.
Los programas en la PC de IBM podrían mostrar texto usando la rutina de "salida estándar" de DOS, o usando la rutina de "teletipo de escritura" del BIOS, o almacenándola directamente para mostrar la memoria. Muchos programas que necesitaban mostrar mucho texto optaron rápidamente por este último enfoque, ya que podría ser literalmente cientos de veces más rápido que usar las rutinas de DOS. Esto no fue porque las rutinas de DOS y BIOS eran excepcionalmente ineficientes; a menos que la pantalla estuviera en blanco, solo podría escribirse en ciertos momentos. La rutina de BIOS para generar un carácter se diseñó para que se pueda llamar en cualquier momento; Por lo tanto, cada solicitud tenía que comenzar de nuevo esperando el momento adecuado para realizar una operación de escritura. Por el contrario, el código de aplicación que sabía lo que tenía que hacer podría organizarse en torno a las oportunidades disponibles para escribir la pantalla.
Un punto clave aquí es que, si bien el DOS y el BIOS proporcionaron un medio para enviar texto a la pantalla, no había nada particularmente "mágico" en tales habilidades. Una aplicación que quisiera escribir texto en la pantalla podría hacerlo con la misma eficacia, al menos si el hardware de la pantalla funcionara de la manera esperada (si alguien hubiera instalado un Adaptador de pantalla monocromo, que era similar al CGA pero tenía su memoria de caracteres) ubicado en 0xB000: 0000-0xB000: 3999), el BIOS generaría automáticamente caracteres allí; una aplicación que fue programada para funcionar con el MDA o el CGA también podría hacerlo, pero una aplicación que fue programada solo para el CGA sería totalmente inútil en el MDA).
En los sistemas más nuevos, las cosas son un poco diferentes. Los procesadores tienen varios modos de "privilegio". Comienzan en el modo más privilegiado, donde el código puede hacer lo que quiera. Luego pueden cambiar a un modo restringido, donde solo están disponibles rangos seleccionados de memoria o instalaciones de E / S. El código no puede cambiar directamente de un modo restringido a modo privilegiado, pero el procesador ha definido puntos de entrada en modo privilegiado, y el código en modo restringido puede pedirle al procesador que comience a ejecutar código en uno de esos puntos de entrada en modo privilegiado. Además, hay puntos de entrada en modo privilegiado asociados con una serie de operaciones que estarían prohibidas en modo restringido. Supongamos, por ejemplo, que alguien quisiera ejecutar múltiples aplicaciones de MS-DOS simultáneamente, cada una con su propia pantalla. Si las aplicaciones pudieran escribir directamente en el controlador de pantalla a 0xB800: 0, no habría forma de evitar que una aplicación sobrescriba la pantalla de otra aplicación. Por otro lado, un sistema operativo podría ejecutar la aplicación en modo restringido y atrapar cualquier acceso a la memoria de la pantalla; Si descubriera que una aplicación que se suponía que estaba en el "fondo" intentaba escribir 0xB800: 160, podría almacenar los datos en alguna memoria que había guardado como un búfer de pantalla de la aplicación en segundo plano. Si esa aplicación luego se cambia al primer plano, el búfer podría copiarse en la pantalla real. un sistema operativo podría ejecutar la aplicación en modo restringido y atrapar cualquier acceso a la memoria de visualización; Si descubriera que una aplicación que se suponía que estaba en el "fondo" intentaba escribir 0xB800: 160, podría almacenar los datos en alguna memoria que había guardado como un búfer de pantalla de la aplicación en segundo plano. Si esa aplicación luego se cambia al primer plano, el búfer podría copiarse en la pantalla real. un sistema operativo podría ejecutar la aplicación en modo restringido y atrapar cualquier acceso a la memoria de visualización; Si descubriera que una aplicación que se suponía que estaba en el "fondo" intentaba escribir 0xB800: 160, podría almacenar los datos en alguna memoria que había guardado como un búfer de pantalla de la aplicación en segundo plano. Si esa aplicación luego se cambia al primer plano, el búfer podría copiarse en la pantalla real.
Las cosas clave a tener en cuenta son (1) aunque a menudo es útil tener un conjunto estándar de rutinas para realizar diversos servicios estándar como mostrar texto, no hacen nada que una aplicación que se ejecutaba en "modo privilegiado" no pudiera hacer si se programó correctamente para manejar el hardware que se instaló; (2) aunque el sistema operativo evitaría que la mayoría de las aplicaciones que se ejecutan hoy en día realicen tales E / S directamente, un programa que se inicia en modo privilegiado puede hacer lo que quiera y puede establecer las reglas que desee para el modo restringido programas
fuente
Como dijo Stephen C., no se trata solo de iniciar el sistema operativo, también se trata de cómo se ejecuta, interactúa con el hardware y con el software que se encuentra encima.
Solo agregaré a su respuesta, que es posible que desee echar un vistazo a "Los elementos de los sistemas informáticos" . Es un libro y algunas herramientas que explican cómo interactúan una computadora, un sistema operativo y compiladores. Lo único es que le brinda las herramientas para desarrollar rápidamente su propio sistema operativo en un entorno simulado, ignorando los muchos detalles necesarios para uno real, de modo que pueda comprender los conceptos . Hace un gran trabajo al permitirte ver el bosque en lugar de los árboles.
Si desea obtener más detalles sobre cómo interactúa el sistema operativo con el hardware, consulte Minix .
fuente
Su aplicación se ejecuta dentro de un sistema operativo. Este sistema operativo proporciona servicios a su aplicación, como abrir un archivo y escribir bytes en él. Estos servicios generalmente se proporcionan a través de llamadas al sistema.
El sistema operativo se ejecuta dentro del hardware. El hardware proporciona servicios al sistema operativo, como configurar la velocidad en baudios de un puerto serie y escribir bytes en él. Estos servicios generalmente se proporcionan a través de registros mapeados en memoria o puertos de E / S.
Para dar un ejemplo muy simplificado de cómo funciona esto:
Su aplicación le dice al sistema operativo que escriba algo en un archivo. Para su aplicación, el sistema operativo proporciona conceptos como archivos y directorios.
En el hardware, estos conceptos no existen. El hardware proporciona conceptos como discos divididos en bloques fijos de 512 bytes. El sistema operativo decide qué bloques usar para su archivo, y algunos otros bloques para metadatos como el nombre del archivo, el tamaño y la ubicación en el disco. Luego le dice al hardware: escriba estos 512 bytes en el sector con este número en el disco con ese número; escriba estos otros 512 bytes en el sector con este número diferente en el disco con ese mismo número; y así.
La forma en que el sistema operativo le dice al hardware que haga eso varía mucho. Una de las funciones de un sistema operativo es proteger las aplicaciones de estas diferencias. Para el ejemplo de disco, en un tipo de hardware, el sistema operativo tendría que escribir el disco y el número de sector en un puerto de E / S, y luego escribir los bytes uno por uno en un puerto de E / S separado. En otro tipo de hardware, el sistema operativo tendría que copiar los 512 bytes completos de un sector en un área de memoria, escribir la ubicación de esa área de memoria en una ubicación de memoria especial y escribir el disco y el número de sector en otro ubicación especial de memoria.
El hardware de gama alta de hoy es extremadamente complicado. Los manuales que dan todos sus detalles de programación son topes de puerta con miles de páginas; por ejemplo, el último manual de CPU de Intel tiene siete volúmenes, con un total de más de 4000 páginas, y eso es solo para la CPU. La mayoría de los otros componentes exponen bloques de memoria o puertos de E / S, que el sistema operativo puede indicarle a la CPU que asigne a las direcciones dentro de su espacio de direcciones. Varios de estos componentes exponen aún más cosas detrás de unos pocos puertos de E / S o direcciones de memoria; Como ejemplo, el RTC (Reloj en tiempo real, el componente que mantiene el tiempo de la computadora mientras está apagado) expone unos cientos de bytes de memoria detrás de un par de puertos de E / S, y ese es un componente muy simple que se remonta a La PC / AT original. Cosas como los discos duros tienen procesadores completamente separados, con el que el sistema operativo se comunica a través de comandos estandarizados. Las GPU son aún más complicadas.
Varias personas en los comentarios anteriores sugirieron el Arduino. Estoy de acuerdo con ellos, es mucho más simple de entender: el ATmega328, que hace todo en el Arduino Uno, excepto exponer el conector USB como puerto serie, tiene un manual con solo unos cientos de páginas. En Arduino, se ejecuta directamente en el hardware, sin sistema operativo en el medio; solo algunas pequeñas rutinas de biblioteca, que no tiene que usar si no lo desea.
fuente
Ejemplos ejecutables
Técnicamente, un programa que se ejecuta sin un sistema operativo es un sistema operativo. Así que veamos cómo crear y ejecutar algunos minúsculos sistemas operativos hello world.
El código de todos los ejemplos a continuación está presente en este repositorio de GitHub .
Sector de arranque
En x86, lo más simple y de nivel más bajo que puede hacer es crear un Master Boot Sector (MBR) , que es un tipo de sector de arranque , y luego instalarlo en un disco.
Aquí creamos uno con una sola
printf
llamada:Salir:
Probado en Ubuntu 18.04, QEMU 2.11.1.
main.img
contiene lo siguiente:\364
en octal ==0xf4
en hexadecimal: la codificación de unahlt
instrucción, que le dice a la CPU que deje de funcionar.Por lo tanto, nuestro programa no hará nada: solo iniciar y detener
Usamos octal porque
\x
POSIX no especifica los números hexadecimales.Podríamos obtener esta codificación fácilmente con:
pero la
0xf4
codificación también está documentada en el manual de Intel, por supuesto.%509s
Producir 509 espacios. Necesario para completar el archivo hasta el byte 510.\125\252
en octal ==0x55
seguido de0xaa
: bytes mágicos requeridos por el hardware. Deben ser bytes 511 y 512.Si no está presente, el hardware no lo tratará como un disco de arranque.
Tenga en cuenta que incluso sin hacer nada, algunos caracteres ya están impresos en la pantalla. Los imprime el firmware y sirven para identificar el sistema.
Ejecutar en hardware real
Los emuladores son divertidos, pero el hardware es el verdadero negocio.
Sin embargo, tenga en cuenta que esto es peligroso, y podría borrar su disco por error: ¡solo haga esto en máquinas viejas que no contienen datos críticos! O incluso mejor, paneles de desarrollo como Raspberry Pi, vea el ejemplo ARM a continuación.
Para una computadora portátil típica, debe hacer algo como:
Grabe la imagen en una memoria USB (¡destruirá sus datos!):
conecte el USB a una computadora
encenderlo
dile que arranque desde el USB.
Esto significa hacer que el firmware elija USB antes que el disco duro.
Si ese no es el comportamiento predeterminado de su máquina, siga presionando Intro, F12, ESC u otras teclas extrañas después del encendido hasta que obtenga un menú de inicio donde puede seleccionar iniciar desde el USB.
A menudo es posible configurar el orden de búsqueda en esos menús.
Por ejemplo, en mi viejo Lenovo Thinkpad T430, UEFI BIOS 1.16, puedo ver:
Hola Mundo
Ahora que hemos creado un programa mínimo, pasemos a un mundo hola.
La pregunta obvia es: ¿cómo hacer IO? Algunas opciones:
puerto serie . Este es un protocolo estandarizado muy simple que envía y recupera caracteres de un terminal host.
Fuente .
Desafortunadamente, no está expuesto en la mayoría de las computadoras portátiles modernas, pero es la forma más común de obtener placas de desarrollo, consulte los ejemplos de ARM a continuación.
Esto es realmente una pena, ya que tales interfaces son realmente útiles para depurar el kernel de Linux, por ejemplo .
usar funciones de depuración de chips. ARM llama a ellos semihosting, por ejemplo. En hardware real, requiere soporte adicional de hardware y software, pero en emuladores puede ser una opción conveniente y gratuita. Ejemplo .
Aquí haremos un ejemplo de BIOS, ya que es más simple en x86. Pero tenga en cuenta que no es el método más robusto.
red eléctrica
link.ld
Ensamblar y vincular con:
Salir:
Probado en: Lenovo Thinkpad T430, UEFI BIOS 1.16. Disco generado en un host Ubuntu 18.04.
Además de las instrucciones de montaje estándar del usuario, tenemos:
.code16
: le dice a GAS que muestre código de 16 bitscli
: deshabilita las interrupciones de software. Esos podrían hacer que el procesador vuelva a funcionar después dehlt
int $0x10
: hace una llamada de BIOS. Esto es lo que imprime los personajes uno por uno.Los indicadores de enlace importantes son:
--oformat binary
: genera el código de ensamblaje binario sin formato, no lo deforma dentro de un archivo ELF, como es el caso de los archivos ejecutables del usuario habitual.Use C en lugar de ensamblar
Como C se compila para ensamblar, usar C sin la biblioteca estándar es bastante simple, básicamente solo necesitas:
main
, en particular:TODO: enlace, así que algún ejemplo x86 en GitHub. Aquí hay un ARM que he creado .
Sin embargo, las cosas se vuelven más divertidas si desea utilizar la biblioteca estándar, ya que no tenemos el kernel de Linux, que implementa gran parte de la funcionalidad de la biblioteca estándar C a través de POSIX .
Algunas posibilidades, sin recurrir a un sistema operativo completo como Linux, incluyen:
Newlib
Ejemplo detallado en: https://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931
En Newlib, debe implementar las llamadas al sistema usted mismo, pero obtiene un sistema muy mínimo y es muy fácil implementarlas.
Por ejemplo, puede redirigir
printf
a los sistemas UART o ARM, o implementarexit()
con semihosting .sistemas operativos integrados como FreeRTOS y Zephyr .
Dichos sistemas operativos generalmente le permiten desactivar la programación preventiva, lo que le brinda un control total sobre el tiempo de ejecución del programa.
Pueden verse como una especie de Newlib pre-implementado.
BRAZO
En ARM, las ideas generales son las mismas. He subido:
algunos ejemplos simples de QEMU baremetal aquí en GitHub . El ejemplo prompt.c toma la entrada de su terminal host y devuelve la salida a través del UART simulado:
Ver también: https://stackoverflow.com/questions/38914019/how-to-make-bare-metal-arm-programs-and-run-them-on-qemu/50981397#50981397
una configuración de Blinker Raspberry Pi completamente automatizada en: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker
Ver también: https://stackoverflow.com/questions/29837892/how-to-run-ac-program-with-no-os-on-the-raspberry-pi/40063032#40063032
Para Raspberry Pi, https://github.com/dwelch67/raspberrypi parece el tutorial más popular disponible en la actualidad.
Algunas diferencias de x86 incluyen:
IO se realiza por escrito a las direcciones de magia directa, no hay
in
yout
las instrucciones.Esto se llama memoria asignada IO .
para un hardware real, como Raspberry Pi, puede agregar el firmware (BIOS) usted mismo a la imagen del disco.
Eso es algo bueno, ya que hace que la actualización de ese firmware sea más transparente.
Firmware
En verdad, su sector de arranque no es el primer software que se ejecuta en la CPU del sistema.
Lo que realmente se ejecuta primero es el llamado firmware , que es un software:
Los firmwares bien conocidos incluyen:
El firmware hace cosas como:
haga un bucle sobre cada disco duro, USB, red, etc. hasta que encuentre algo de arranque.
Cuando ejecutamos QEMU,
-hda
dice quemain.img
es un disco duro conectado al hardware, yhda
es el primero en probarse y se usa.cargue los primeros 512 bytes en la dirección de memoria RAM
0x7c00
, coloque el RIP de la CPU allí y déjelo funcionarmostrar cosas como el menú de inicio o las llamadas de impresión del BIOS en la pantalla
El firmware ofrece una funcionalidad similar a la de un sistema operativo, de la cual dependen la mayoría de los sistemas operativos. Por ejemplo, un subconjunto de Python ha sido portado para ejecutarse en BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM
Se puede argumentar que los firmwares son indistinguibles de los sistemas operativos, y que el firmware es la única programación "verdadera" de metal desnudo que uno puede hacer.
Como dice este desarrollador de CoreOS :
Publicar estado inicial del BIOS
Al igual que muchas cosas en el hardware, la estandarización es débil, y una de las cosas en las que no debe confiar es en el estado inicial de los registros cuando su código comienza a ejecutarse después del BIOS.
Hágase un favor y use un código de inicialización como el siguiente: https://stackoverflow.com/a/32509555/895245
Los registros tienen gusto
%ds
y%es
tienen efectos secundarios importantes, por lo que debe ponerlos a cero incluso si no los está usando explícitamente.Tenga en cuenta que algunos emuladores son mejores que el hardware real y le dan un buen estado inicial. Luego, cuando se ejecuta en hardware real, todo se rompe.
GNU GRUB Multiboot
Los sectores de arranque son simples, pero no son muy convenientes:
Es por esas razones que GNU GRUB creó un formato de archivo más conveniente llamado multiboot.
Ejemplo de trabajo mínimo: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world
También lo uso en mi repositorio de ejemplos de GitHub para poder ejecutar fácilmente todos los ejemplos en hardware real sin quemar el USB un millón de veces. En QEMU se ve así:
Si prepara su sistema operativo como un archivo de arranque múltiple, GRUB podrá encontrarlo dentro de un sistema de archivos normal.
Esto es lo que hacen la mayoría de las distribuciones, poner las imágenes del sistema operativo debajo
/boot
.Los archivos de arranque múltiple son básicamente un archivo ELF con un encabezado especial. GRUB los especifica en: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
Puede convertir un archivo de arranque múltiple en un disco de arranque con
grub-mkrescue
.El Torito
Formato que se puede grabar en CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29
También es posible producir una imagen híbrida que funcione en ISO o USB. Esto se puede hacer con
grub-mkrescue
( ejemplo ), y también lo hace el kernel de Linux almake isoimage
usarloisohybrid
.Recursos
fuente