Sé que cuando se compila el código fuente, en C ++, la salida del compilador es el código de la máquina (ejecutable) que pensé que eran instrucciones directamente para la CPU. Recientemente estuve leyendo sobre los kernels y descubrí que los programas no pueden acceder directamente al hardware, sino que tienen que pasar por el kernel.
Entonces, cuando compilamos un código fuente simple, digamos con solo un printf()
y la compilación produce el código de máquina ejecutable, cada instrucción en este código de máquina se ejecutará directamente desde la memoria (una vez que el sistema operativo haya cargado el código) o cada comando en el código de la máquina deberá pasar por el sistema operativo (kernel) para ser ejecutado?
he leído una pregunta similar . No explicó si el código de máquina que se genera después de la compilación es una instrucción para la CPU directamente o si tendrá que pasar nuevamente por el núcleo para crear la instrucción correcta para la CPU. Es decir, ¿qué sucede después de que el código de la máquina se carga en la memoria? ¿Pasará por el núcleo o hablará directamente con el procesador?
fuente
printf
No es un gran ejemplo. Está definido explícitamente por la especificación C como una función que solo está disponible en implementaciones "alojadas" (es decir, ejecutarse en un kernel, en lugar de "independiente", que puede no requerir una). Y en la mayoría de las plataformas,printf
Es solo una función proporcionada por sulibc
eso hace un montón de cosas en su nombre (que eventualmente incluye un syscall para imprimir en stdout). Realmente no es diferente de llamarlibvlc_media_list_add_media
oPyObject_GetAttr
, salvo que algunosprintf
La implementación se garantiza enlazable sin agregar extra no estándar-l
s.Respuestas:
Como alguien que ha escrito programas que se ejecutan sin un sistema operativo, ofrezco una respuesta definitiva.
Eso depende de cómo ese programa fue escrito y construido.
Podría escribir un programa (suponiendo que tenga los conocimientos) que no requiera un sistema operativo en absoluto.
Tal programa se describe como ser único .
Los cargadores de arranque y los programas de diagnóstico son usos típicos de los programas independientes.
Sin embargo, el programa típico escrito y integrado en algún entorno de sistema operativo host se ejecutaría en ese mismo entorno de sistema operativo.
Se requieren decisiones y acciones muy explícitas para escribir y construir un programa independiente.
Correcto.
Esa es una restricción impuesta por un modo de CPU que el sistema operativo utiliza para ejecutar programas, y se facilita mediante ciertas herramientas de compilación, como compiladores y bibliotecas.
No es una limitación intrínseca en cada programa escrito.
Cada instrucción es ejecutada por la CPU.
Una instrucción que no es compatible o ilegal (por ejemplo, el proceso no tiene privilegios suficientes) causará una excepción inmediata, y la CPU ejecutará una rutina para manejar esta condición inusual.
UNA printf () La función no debe ser usada como un ejemplo de "código fuente simple" .
La traducción de un lenguaje de programación de alto nivel orientado a objetos a código de máquina puede no ser tan trivial como usted implica.
Y luego elige una de las funciones más complejas de una biblioteca de tiempo de ejecución que realiza conversiones de datos y I / O.
Tenga en cuenta que su pregunta estipula un entorno con un sistema operativo (y una biblioteca de tiempo de ejecución).
Una vez que el sistema se inicia y el sistema operativo recibe el control de la computadora, se imponen restricciones sobre lo que puede hacer un programa (por ejemplo, el sistema operativo debe realizar la E / S).
Si espera ejecutar un programa independiente (es decir, sin un sistema operativo), no debe iniciar la computadora para ejecutar el sistema operativo.
Eso depende del medio ambiente.
Para un programa independiente, se puede ejecutar, es decir, el control se entrega saltando a la dirección de inicio del programa.
Para un programa cargado por el sistema operativo, el programa debe estar vinculado dinámicamente con bibliotecas compartidas de las que depende. El sistema operativo debe crear un espacio de ejecución para el proceso que ejecutará el programa.
El código de máquina es ejecutado por la CPU.
Ellos no "ir a través del núcleo" , pero tampoco ellos "hablar con el procesador" .
El código de la máquina (que consta de un código de operación y operandos) es una instrucción a la CPU que se decodifica y se realiza la operación.
Quizás el siguiente tema que debes investigar es Modos de CPU .
fuente
gcc -O2 -ffreestanding my_kernel.c special_sauce.S
para hacer un ejecutable que no asuma que ninguna de las bibliotecas normales o el sistema operativo estarán allí. (Por supuesto, normalmente necesitaría un script de enlace para que se vincule de manera útil a un formato de archivo que un cargador de arranque querrá cargar.)El kernel es "simplemente" más código. Es solo que ese código es una capa que vive entre las partes más bajas de su sistema y el hardware real.
Todo se ejecuta directamente en la CPU, solo hace una transición a través de las capas para hacer cualquier cosa.
Su programa "necesita" el kernel de la misma manera que necesita las bibliotecas estándar de C para utilizar
printf
comando en el primer lugar.El código real de su programa se ejecuta en la CPU, pero las ramas que el código hace para imprimir algo en la pantalla pasan por el código de la C
printf
función, a través de varios otros sistemas e intérpretes, cada uno de los cuales hace su propio procesamiento para trabajar solo cómohello world!
En realidad se imprime en su pantalla.Digamos que tiene un programa de terminal ejecutándose en un administrador de ventanas de escritorio, ejecutándose en su kernel que a su vez se está ejecutando en su hardware.
Hay mucho más que sigue, pero sea sencillo ...
hello world!
hello world!
a la consolahello world!
escrito a mi, puedes ponerlo en posiciónx
,y
¿Por favor?"Esta es una simplificación masiva solo para descripción. Aquí hay dragones.
Efectivamente, todo lo que hace que necesita acceso al hardware, ya sea pantalla, bloques de memoria, bits de archivos o algo por el estilo tiene pasar por algún controlador de dispositivo en el kernel para trabajar exactamente cómo para hablar con el dispositivo correspondiente. Ya sea un controlador de sistema de archivos en la parte superior de un controlador de controlador de disco duro SATA que se encuentra en la parte superior de un dispositivo puente PCIe.
El kernel sabe cómo unir todos estos dispositivos y presenta una interfaz relativamente simple para que los programas hagan cosas sin tener que saber cómo hacer todas estas cosas por sí mismos.
Los administradores de ventanas de escritorio proporcionan una capa que significa que los programas no tienen que saber cómo dibujar ventanas y jugar bien con otros programas que intentan mostrar cosas al mismo tiempo.
Finalmente, el programa de terminal significa que su programa no necesita saber cómo dibujar una ventana, ni cómo comunicarse con el controlador de la tarjeta gráfica del kernel, ni toda la complejidad que tiene que ver con el manejo de los búferes de pantalla y la sincronización de la pantalla y, de hecho, mover el teclado. Líneas de datos a la pantalla.
Todo es manejado por capas sobre capas de código.
fuente
Depende del medio ambiente. En muchas computadoras antiguas (y más simples), como la IBM 1401, la respuesta sería "no". Su compilador y enlazador emitieron un "binario" independiente que se ejecutó sin ningún sistema operativo. Cuando su programa dejó de ejecutarse, cargó uno diferente, que también se ejecutó sin sistema operativo.
Se necesita un sistema operativo en entornos modernos porque no está ejecutando solo un programa a la vez. Compartir la (s) núcleo (s) de la CPU, la memoria RAM, el dispositivo de almacenamiento masivo, el teclado, el mouse y la pantalla, entre varios programas a la vez, requiere coordinación. El sistema operativo proporciona eso. Entonces, en un entorno moderno, su programa no puede simplemente leer y escribir el disco o SSD, tiene que pedirle al sistema operativo que lo haga en su nombre. El sistema operativo recibe dichas solicitudes de todos los programas que desean acceder al dispositivo de almacenamiento, implementa cosas como los controles de acceso (no puede permitir que los usuarios normales escriban en los archivos del sistema operativo), los pone en cola en el dispositivo y ordena la información devuelta a los programas (procesos) correctos.
Además, las computadoras modernas (a diferencia de, por ejemplo, la 1401) admiten la conexión de una gran variedad de dispositivos de E / S, no solo los que IBM le vendería en la antigüedad. Tu compilador y enlazador no pueden conocer todas las posibilidades. Por ejemplo, su teclado puede estar conectado a través de PS / 2 o USB. El sistema operativo le permite instalar "controladores de dispositivo" específicos del dispositivo que saben cómo hablar con esos dispositivos, pero presentan una interfaz común para la clase de dispositivo para el sistema operativo. Por lo tanto, su programa, e incluso el sistema operativo, no tienen que hacer nada diferente para obtener pulsaciones de teclas de un teclado USB / PS / 2, o para acceder, por ejemplo, a un disco SATA local frente a un dispositivo de almacenamiento USB vs un almacenamiento que está en algún lugar apagado en un NAS o SAN. Esos detalles son manejados por los controladores de dispositivos para los diferentes controladores de dispositivos.
Para los dispositivos de almacenamiento masivo, el sistema operativo proporciona un controlador de sistema de archivos que presenta la misma interfaz a todos los directorios y archivos, independientemente de dónde y cómo se implemente el almacenamiento. Y nuevamente, el sistema operativo se preocupa por los controles de acceso y la serialización. En general, por ejemplo, el mismo archivo no debe abrirse para que lo escriba más de un programa a la vez sin saltar por algunos aros (pero las lecturas simultáneas generalmente están bien).
Entonces, en un entorno moderno de propósito general, sí, realmente necesita un sistema operativo. Pero incluso hoy en día hay computadoras como los controladores en tiempo real que no son lo suficientemente complicados como para necesitar uno.
En el entorno de Arduino, por ejemplo, no hay realmente un sistema operativo. Claro, hay un montón de código de biblioteca que el entorno de compilación incorpora en cada "binario" que construye. Pero como no hay una persistencia de ese código de un programa a otro, no es un sistema operativo.
fuente
Creo que muchas respuestas no entienden la pregunta, que se reduce a esto:
Básicamente, La CPU ejecuta directamente el código de máquina. . Sería significativamente más lento hacer que el núcleo ejecute todas las aplicaciones. Sin embargo, hay algunas advertencias.
Cuando un sistema operativo está presente, los programas de aplicación normalmente tienen restricciones para ejecutar ciertas instrucciones o acceder a ciertos recursos. Por ejemplo, si una aplicación ejecuta una instrucción que modifica la tabla de interrupciones del sistema, la CPU saltará a un controlador de excepciones del sistema operativo para que la aplicación ofensiva finalice. Además, las aplicaciones generalmente no tienen permiso para leer / escribir en la memoria del dispositivo. (Es decir, "hablar con el hardware"). El acceso a estas regiones especiales de memoria es la forma en que el sistema operativo se comunica con dispositivos como la tarjeta gráfica, la interfaz de red, el reloj del sistema, etc.
Las restricciones que un sistema operativo coloca en las aplicaciones se logran mediante características especiales de la CPU, como los modos de privilegio, la protección de la memoria y las interrupciones. Aunque cualquier CPU que encuentre en un teléfono inteligente o PC tiene estas características, ciertas CPU no las tienen. Estas CPU sí necesitan núcleos especiales que "interpretan" el código de la aplicación para lograr las funciones deseadas. Un ejemplo muy interesante es el Gigatron , que es una computadora de 8 instrucciones que puedes construir con chips que emula una computadora de 34 instrucciones.
Algunos lenguajes como Java "compilan" a algo llamado Bytecode, que no es realmente un código de máquina. Aunque en el pasado fueron interpretados para ejecutar los programas, en estos días algo llamado Compilación justo a tiempo Normalmente se usa para que terminen ejecutándose directamente en la CPU como código de máquina.
El software de ejecución en una máquina virtual solía requerir que su código de máquina fuera "interpretado" por un programa llamado Hipervisor . Debido a la enorme demanda de máquinas virtuales de la industria, los fabricantes de CPU han agregado funciones como VTx a sus CPU para permitir que la mayoría de las instrucciones de un sistema huésped sean ejecutadas directamente por la CPU. Sin embargo, cuando se ejecuta software diseñado para un incompatible CPU en una máquina virtual (por ejemplo, emulando una NES), el código de la máquina deberá ser interpretado.
fuente
Cuando compila su código, crea el código denominado "objeto" que (en la mayoría de los casos) depende de las bibliotecas del sistema (
printf
por ejemplo), entonces su código se envuelve con un enlazador que agregará el tipo de cargador de programas que su sistema operativo particular puede reconocer (por eso no puede ejecutar un programa compilado para Windows en Linux, por ejemplo) y sabe cómo desenvolver su código y ejecutar. Por lo tanto, su programa es como una carne dentro de un sándwich y se puede comer solo como un paquete completo.Bueno, es a medias la verdad; Si su programa es un controlador en modo kernel, en realidad puede acceder directamente al hardware si sabe cómo "hablar" con el hardware, pero generalmente (especialmente para hardware no documentado o complicado) las personas usan controladores que son bibliotecas del kernel. De esta manera, puede encontrar funciones de la API que saben cómo hablar con el hardware de forma casi humana y sin la necesidad de conocer las direcciones, los registros, la sincronización y muchas otras cosas.
Bueno, el núcleo es como una camarera, cuya responsabilidad es acompañarte a una mesa y servirte. Lo único que no puede hacer es comer por ti, debes hacerlo tú mismo. Al igual que con su código, el núcleo desempaquetará su programa en una memoria e iniciará su código, que es un código de máquina ejecutado directamente por la CPU. Un kernel solo necesita supervisarlo, lo que se le permite y lo que no se le permite hacer.
El código de máquina que se genera después de la compilación es una instrucción para la CPU directamente. No hay duda de eso. Lo único que debe tener en cuenta es que no todos los códigos en el archivo compilado son códigos reales de la máquina / CPU. Linker envolvió su programa con algunos metadatos que solo el kernel puede interpretar, como una pista, qué hacer con su programa.
Si su código es simplemente opcodes como la adición de dos registros, entonces será ejecutado directamente por la CPU sin asistencia del núcleo, pero si su código utiliza funciones de las bibliotecas, dichas llamadas serán asistidas por el núcleo, como en el ejemplo con la camarera, si lo desea. para comer en un restaurante te darían una herramienta: tenedor, cuchara (y aún así sus activos), pero lo que harás con ella, - depende de tu "código".
Bueno, solo para evitar las llamas en los comentarios: es un modelo muy simplificado que espero ayude a OP a entender las cosas básicas, pero las buenas sugerencias para mejorar esta respuesta son bienvenidas.
fuente
Esencialmente, solo las llamadas al sistema van al kernel. Cualquier cosa que tenga que ver con la E / S o la asignación / desasignación de la memoria generalmente da como resultado una llamada al sistema. Algunas instrucciones solo pueden ejecutarse en modo kernel y harán que la CPU active una excepción. Las excepciones provocan un cambio al modo kernel y un salto al código del kernel.
El kernel no procesa cada instrucción en un programa. Simplemente hace las llamadas al sistema y cambia entre programas en ejecución para compartir la CPU.
No es posible realizar la asignación de memoria en modo usuario (sin el núcleo), si accede a la memoria a la que no tiene permiso para acceder, la MMU, programada anteriormente por el núcleo, avisa y provoca una excepción de "falla de segmentación" a nivel de la CPU , que activa el kernel, y el kernel mata el programa.
Hacer E / S en modo usuario (sin el núcleo) no es posible, si accede a puertos de E / S o registros de dispositivos, o direcciones conectadas a dispositivos (uno o ambos necesarios para realizar cualquier E / S), estos activan una excepción de la misma manera.
Depende del tipo de ejecutable.
Los kernels, además de mediar el acceso compartido a la RAM y al hardware, también realizan una función de cargador.
Muchos "formatos ejecutables", como ELF o PE, tienen metadatos en el archivo ejecutable además del código, y el trabajo del cargador para procesarlo. Leer el detalles sangrientos sobre el formato PE de Microsoft para más información.
Estos ejecutables también hacen referencia a las bibliotecas (Windows
.dll
o objeto compartido de Linux.so
archivos) - su código debe ser incluido.Si su compilador produce un archivo que está destinado a ser procesado por un cargador del sistema operativo, y ese cargador no está allí, no funcionará.
Por supuesto. Debe convencer al sistema operativo para que de alguna manera ejecute su código en bruto sin procesar ningún metadato. Si su código llama a las API del kernel, aún no funcionará.
Si carga este ejecutable de alguna manera desde un sistema operativo (es decir, si permite que se cargue y ejecute un código en bruto), aún estará en modo de usuario. Si su código accede a elementos que están prohibidos en el modo de usuario, a diferencia del modo de kernel, como la memoria no asignada o las direcciones / registros de dispositivos de E / S, se bloqueará con privilegios o violaciones de segmento (nuevamente, las excepciones van al modo de kernel y se manejan allí) y todavía no funcionará.
Entonces funcionará.
fuente
TL; DR No.
El desarrollo de Arduino viene a la mente como un entorno actual donde no hay SO. Confía en mí, en uno de estos bebes No tienes el espacio para un sistema operativo.
Del mismo modo, los juegos para Sega Genesis no tenían un sistema operativo provisto por Sega para llamar. Acaba de crear su juego en el ensamblador 68K, escribiendo directamente en el bare metal.
O donde me corté los dientes, haciendo un trabajo integrado en el Intel 8051. Una vez más, todo lo que tiene es un eprom 2716 con un tamaño de 2k * 8, no tiene espacio para un sistema operativo.
Por supuesto, esto supone un uso muy amplio de la aplicación word. Como pregunta retórica, vale la pena preguntarse si un boceto de Arduino en realidad es una aplicación.
fuente
Si bien no quiero dar a entender que las otras respuestas no son correctas por sí mismas, brindan demasiados detalles que, me temo, aún son muy oscuros para usted.
La respuesta básica es que el código se ejecutará directamente en el procesador. Y no, el código de la máquina no "hablará" con nadie, es al revés. El procesador es el componente activo y todo lo que hagas en tu computadora será realizado por ese procesador (estoy simplificando un poco las cosas aquí, pero eso está bien por ahora). El procesador leerá el código, lo ejecutará y escupirá los resultados, el código de la máquina es solo alimento para el procesador.
Su confusión se deriva del uso de la palabra hardware. Aunque la división no es tan clara como solía ser, es mejor si piensa en términos de periféricos en lugar de simplemente llamar hardware a todo. Entonces, si hay un sistema operativo o similar en su máquina, su programa tiene que usar sus servicios para acceder a los periféricos, pero el procesador en sí no es un periférico, es la unidad de procesamiento principal en la que se ejecuta su programa directamente.
Los kernels, sistemas operativos y capas intermedias similares se usan normalmente solo en sistemas más grandes donde existe la expectativa de que se ejecutarán varios programas y existe la necesidad de que el sistema administre la forma en que estos programas pueden usar los periféricos de la computadora (con bastante frecuencia en el sistema). Mismo tiempo). En estos casos, los programas en ejecución solo pueden acceder a estos periféricos utilizando el sistema que decidirá cómo compartirlos y se asegurará de que no haya conflictos. Los sistemas pequeños donde no hay necesidad de administración entre los programas competidores porque no hay ninguno, a menudo no tienen ningún sistema subyacente y el programa único que normalmente se ejecuta en estos sistemas es más o menos libre de hacer lo que quiera con los periféricos.
fuente
El BIOS que se ejecuta en su computadora en el encendido es un código ejecutable almacenado en la ROM. Se compone de instrucciones de la máquina más datos. Hay un compilador (o ensamblador) que ensambla este BIOS a partir del código fuente. Este es un caso especial.
Otros casos especiales incluyen el programa bootstrap que carga el kernel y el kernel mismo. Estos casos especiales generalmente están codificados en un lenguaje distinto de C ++.
En el caso general, es mucho más práctico que el compilador produzca algunas instrucciones que invocan los servicios del sistema proporcionados por un núcleo o por las rutinas de la biblioteca. Hace que el compilador sea mucho más ligero. También hace que el código compilado sea más ligero.
En el otro extremo del espectro está Java. En Java, el compilador no traduce el código fuente en instrucciones de la máquina, como suele entenderse este término. En su lugar, el código fuente se traduce en "instrucciones de máquina" para una máquina imaginaria, llamada Máquina Virtual de Java. Antes de que un programa Java pueda ejecutarse, debe combinarse con el tiempo de ejecución de Java, que incluye un intérprete para la Máquina Virtual de Java.
fuente
En los viejos tiempos, su programa era responsable de hacer todo lo que debía hacerse durante la ejecución de su programa, ya sea que lo hiciera usted mismo o agregando un código de biblioteca que otros escribieron a su programa. Lo único que se ejecutaba junto a eso en la computadora era el código para leer en tu programa compilado, si tenías suerte. Algunas computadoras tenían que haber ingresado un código a través de los interruptores antes de poder hacer más (el proceso original de "arranque"), o incluso todo el programa ingresado de esta manera.
Rápidamente se encontró que era bueno tener un código ejecutable capaz de cargar y ejecutar programas. Más tarde, se descubrió que las computadoras eran lo suficientemente poderosas como para permitir la ejecución de varios programas al mismo tiempo al hacer que la CPU cambiara entre ellas, especialmente si el hardware podía ayudar, pero con la complejidad agregada de los programas, no con los pasos en cada uno de los otros dedos (por ejemplo, , ¿cómo manejar múltiples programas tratando de enviar datos a la impresora a la vez?).
Todo esto dio lugar a una gran cantidad de código de ayuda que se trasladó de los programas individuales al "sistema operativo", con una forma estandarizada de invocar el código de ayuda de los programas del usuario.
Y ahí es donde estamos hoy. Sus programas se ejecutan a toda velocidad, pero cada vez que necesitan algo administrado por el sistema operativo, se llaman rutinas de ayuda proporcionadas por el sistema operativo, y ese código no es necesario y no está presente en los programas del usuario. Esto incluía escribir en la pantalla, guardar archivos, acceder a la red, etc.
Se han escrito microkernels que proporcionan justo lo que se necesita para que un programa determinado se ejecute sin un sistema operativo completo. Esto tiene algunas ventajas para los usuarios experimentados al tiempo que regala la mayoría de los demás. Si lo desea, puede leer la página de Wikipedia al respecto - https://en.wikipedia.org/wiki/Microkernel - Si quieres saber más.
Experimenté con un Microkernel capaz de ejecutar una Máquina Virtual Java, pero luego descubrí que el punto óptimo para eso es Docker.
fuente
En los sistemas operativos de escritorio típicos, el kernel sí mismo Es un ejecutable. (Windows tiene
ntoskrnl.exe
; Linux tienevmlinux
, etc.) Si necesita un kernel para que se ejecute un ejecutable, entonces esos sistemas operativos no podrían existir.Para lo que necesitas un kernel es para hacer las cosas que hace un kernel. Permita que se ejecuten varios ejecutables a la vez, arbitren entre ellos, abstraigan el hardware, etc. La mayoría de los programas no son capaces de hacer esas cosas de manera competente, y usted no querría que lo hicieran, incluso si pudieran. En la época de DOS, que apenas podía llamarse un sistema operativo en sí mismo, los juegos a menudo utilizaban el sistema operativo como poco más que un cargador, y accedían directamente al hardware de manera muy parecida a como lo haría un núcleo. Pero a menudo tenías que saber qué marcas y modelos de hardware había en tu máquina antes de comprar un juego. Muchos juegos solo admitían ciertas familias de tarjetas de video y de sonido, y funcionaban muy mal con las marcas de la competencia, si es que funcionaban. Ese es el tipo de cosa que se obtiene cuando el programa controla el hardware directamente en lugar de hacerlo a través de la abstracción que generalmente se proporciona a través del kernel.
fuente