platform independence - ¿Cómo se ejecuta el programa? ¿Dónde entran en juego los sistemas operativos?
platform-independence (9)
Un programa se compila desde algún lenguaje a ASM -> Código de máquina (ejecutable directamente). Cuando la gente dice que esto depende de la plataforma, significa que los binarios formados se ejecutarán (correctamente) solo en las CPU con la misma arquitectura de conjunto de instrucciones como x86, x86-64. Puede (incorrectamente) / puede no (en absoluto) ejecutarse en otros procesos debido a la diferencia en ISA. ¿Derecha?
Ahora, el concepto de binarios me confunde. Todo se trata del "Código de lenguaje de máquina" y "CPU". ¿Dónde entra en juego el sistema operativo? Me refiero a que el binario compilado tiene instrucciones directas para la CPU cuando se carga en la memoria. & CPU ejecuta una instrucción a la vez. No pude ver el rol del sistema operativo en ningún lugar excepto en el texto del enlace de administración de procesos. Debe estar ejecutándose en la CPU de la misma ISA independientemente del sistema operativo. ¿Correcto?
Sin embargo, no es el caso. Si construyo un código para x86 en la máquina de Windows. No se ejecutará en una máquina Mac x86 o una máquina Linux x86.
Me estoy perdiendo algo aquí. Por favor, aclarar mi confusión.
Dos caminos:
En primer lugar, la respuesta es "llamadas al sistema". Cada vez que llame a una función que necesita hacer cualquier E / S, interactuar con dispositivos, asignar memoria, procesos de bifurcación, etc., esa función debe hacer una "llamada al sistema". Si bien la instrucción syscall en sí misma es parte de X86, las llamadas y los parámetros del sistema disponibles para ellos son específicos del sistema operativo.
Incluso si su programa no hace NINGUNA llamada al sistema (lo cual no estoy seguro es posible, y ciertamente no sería muy útil), los formatos que envuelven el código de la máquina son diferentes para diferentes sistemas operativos. Por lo tanto, los formatos de archivo de exe (PE) y un ejecutable de Linux (ELF generalmente) son diferentes, por lo que un archivo exe no se ejecuta en Linux.
EDIT: estos son detalles de bajo nivel. La respuesta de nivel superior es decir que cualquier cosa que necesite acceder a los archivos, la consola / GUI, asignar memoria, etc. es específica del sistema operativo.
El sistema operativo entra en juego cuando intenta acceder a "un servicio" que se extrae para usted a nivel de hardware, por ejemplo, abra un archivo dentro de la "base de datos" llamado sistema de archivos, genere un número aleatorio (cada sistema operativo moderno tiene esta característica).
Bajo GNU / Linux, por ejemplo, debes completar los registros y llamar a int 80h para acceder a un "servicio" (en realidad llamado "syscall").
Su programa no se ejecutará en otro sistema operativo también porque existen diferentes formatos de archivo para los ejecutables, por ejemplo, Win tiene COFF / PE, Linux tiene el formato de archivo ELF (al igual que cualquier otro formato de archivo, también contiene "metadatos", por ejemplo, el formato de archivo HTML (o SGML)).
El sistema operativo proporciona (a) el entorno en el que se ejecuta el código de su máquina y (b) los servicios estándar. Sin (a), tu código nunca se ejecutará en primer lugar, y sin (b), deberías implementar absolutamente todo por ti mismo y golpear el hardware directamente.
El sistema operativo proporciona las herramientas y la API para acceder a ciertas funciones y al hardware.
Por ejemplo, para crear una ventana en Microsoft Windows, necesita la DLL del sistema operativo para crear la ventana.
A menos que desee escribir la API usted mismo, usará la API que proporciona el sistema operativo. Ahí es donde el sistema operativo entra en juego.
Las instrucciones de la máquina generadas por un lenguaje de alto nivel serán apropiadas para las convenciones de llamadas para las bibliotecas que brindan las llamadas que usted realiza, incluidas las llamadas al sistema (aunque estas generalmente están envueltas en una biblioteca de espacio de usuario en algún lugar, así que especifique cómo hacer una llamada al sistema podría no ser necesario).
Además, será apropiado para la arquitectura del conjunto de instrucciones de destino, con algunas excepciones (por ejemplo, se debe tener cuidado con los supuestos sobre tamaños de punteros, tipos primitivos, diseños de estructuras, implementaciones de clase en C ++, etc.).
El formato del archivo dictará los enganches necesarios / funciones y datos visibles públicamente para permitir que el sistema operativo ejecute su código como un proceso, y para que el proceso vuelva al estado requerido. Si está familiarizado con el desarrollo de C / C ++ en Windows, el concepto de subsistema determina el nivel de arranque, los recursos proporcionados y la firma del punto de entrada (normalmente main(int, char **)
en la mayoría de los sistemas).
Hay algunos buenos ejemplos de cómo la elección del lenguaje de alto nivel, la arquitectura del conjunto de instrucciones y el formato de archivo ejecutable pueden afectar la capacidad de ejecutar un binario en cualquier sistema dado:
Los lenguajes de ensamblaje deben codificarse para una ISA específica. Usan instrucciones que son específicas para una familia de tipos de CPU. Estas instrucciones pueden funcionar en otras familias de CPU, si esas CPU admiten el conjunto de instrucciones dado. Por ejemplo, el código x86 funcionará hasta cierto punto, en un sistema operativo amd64, y definitivamente funcionará en una CPU amd64 que ejecuta un sistema operativo x86.
C abstrae gran parte de los detalles de una ISA. Algunas excepciones obvias incluyen tamaños de punteros y endianness. Se proporcionarán varias interfaces conocidas a un nivel esperado a través de libc, como printf
, main
, fopen
y otros. Estos incluyen los estados esperados de registro y pila para realizar estas llamadas, permitiendo que el código C funcione en diferentes sistemas operativos y arquitecturas sin cambios. Se pueden proporcionar otras interfaces, ya sea directamente o envolviendo la plataforma específica en la interfaz esperada para aumentar la portabilidad del código C.
Python y otros lenguajes "virtualizados" similares operan en otro nivel de abstracción, y nuevamente con algunas excepciones, por ejemplo, las características que no existen en plataformas particulares, o las diferencias de codificación de caracteres, pueden ejecutarse sin modificaciones en numerosos sistemas. Esto se logra al proporcionar una interfaz uniforme para muchas combinaciones diferentes de ISA y sistemas operativos, a expensas del rendimiento y el tamaño del ejecutable.
No se ejecutará en otros procesadores ya que 01010110011 significa algo en x86 y algo más en ARM. Resulta que x86-64 es compatible con versiones anteriores de x86, por lo que puede ejecutar programas x86.
El binario está en un formato específico que su sistema operativo entiende (windows = PE, mac / linux = ELF)
Con cualquier binario normal, su sistema operativo lo carga en la memoria y rellena varios campos con ciertos valores. Estos "ciertos valores" son direcciones a funciones de api que existen en bibliotecas compartidas (dll, so) como kernel32 o libc. Las direcciones API son necesarias porque el binario en sí mismo no sabe cómo acceder a los discos duros, tarjetas de red, gamepads, etc. El programa utiliza estas direcciones para invocar ciertas funciones que existen en su sistema operativo o en otras bibliotecas.
En esencia, al binario le faltan algunas partes vitales que el SO debe llenar para que todo funcione. Si el sistema operativo rellena las partes equivocadas, el binario no funcionará ya que no pueden comunicarse entre sí. Eso es lo que sucedería si reemplaza user32.dll con otro archivo, o si intenta ejecutar un ejecutable de linux en mac osx.
Entonces, ¿cómo sabe libc cómo abrir un archivo?
libc utiliza syscalls, que es un acceso de bajo nivel a las funciones básicas del sistema operativo. Es algo así como una llamada de función, excepto que lo haces llenando ciertos registros de la CPU y luego activando una interrupción (instrucción especial de la CPU)
Entonces, ¿cómo sabe el sistema operativo cómo abrir archivos?
Esa es una de las cosas que hace un sistema operativo. Pero, ¿cómo sabe cómo hablar con un disco duro? No sé exactamente cómo funcionan esas cosas, pero me imagino que el sistema operativo hace esto escribiendo / leyendo ciertas ubicaciones de memoria que se asignan a las funciones del BIOS.
Entonces, ¿cómo sabe la BIOS cómo hablar con un disco duro?
Tampoco lo sé, nunca he hecho ninguna programación a ese nivel. Imagino que el BIOS está cableado a los conectores del disco duro y puede enviar la secuencia correcta de 1 y 0 para hablar "SATA" con el disco duro. Probablemente solo puede decir cosas simples como "leer este sector"
Entonces, ¿cómo sabe el disco duro cómo leer un sector?
Realmente no lo sé, así que dejaré que continúe algún tipo de hardware.
Para empezar, una CPU moderna tiene (al menos) dos modos, un modo en el que ejecuta el núcleo del propio Sistema Operativo ("modo kernel") y un modo en el que ejecuta programas ("modo de usuario"). Cuando está en modo de usuario, la CPU no puede hacer muchas cosas.
Por ejemplo, un clic del mouse normalmente se nota en el núcleo, no en el modo de usuario. Sin embargo, el sistema operativo envía el evento al modo de usuario y desde allí al programa correcto. Al revés también requiere cooperación: un programa no puede dibujar en la pantalla libremente, pero necesita pasar por el sistema operativo y el modo de kernel para dibujar por su parte.
Del mismo modo, el acto de iniciar un programa es típicamente una cooperación. La parte de shell del sistema operativo es también un programa en modo usuario. Obtiene el clic del mouse y determina que se trata de un clic del mouse para iniciar un proceso. La shell le dice a la parte del sistema operativo en modo kernel que inicie un nuevo proceso para ese programa.
Cuando el modo kernel necesita iniciar un nuevo proceso, primero asigna memoria para la contabilidad y luego procede a cargar el programa. Esto implica recuperar las instrucciones del binario, pero también conectar el programa al sistema operativo. Esto generalmente requiere encontrar el punto de entrada (clásico int main(int argc, char** argv)
) del binario, y todos los puntos donde el programa quiere llamar al sistema operativo.
Los diferentes sistemas operativos utilizan diferentes formas de conectar programas con el sistema operativo. Como resultado, el proceso de carga difiere y los formatos de archivo para los binarios también pueden diferir. No es absoluto; el formato ELF para binarios se usa para varios sistemas operativos, y Microsoft usa su formato PE en todos sus sistemas operativos actuales. En ambos casos, el formato describe el formato exacto del binario, por lo que el sistema operativo puede decidir si el programa se puede conectar al sistema operativo. Por ejemplo, si es un binario de Win32, estará en el formato PE, por lo tanto, Linux no lo cargará, Windows 2000 lo hará, al igual que Windows 7-64. Un binario de Win64, por otro lado, también está en formato PE, pero Windows 2000 lo rechazará.
También quiero añadir que el sistema operativo se encarga de la puesta en marcha del programa. Prepara el espacio de proceso y lo inicializa para que el programa pueda comenzar, carga las instrucciones del programa y le da el control.
Una analogía:
Digamos que contratas a un mayordomo de otro país. Él no entiende una palabra de lo que dices, así que obtienes un dispositivo de traductor similar a Star Trek. Ahora él puede entender tu lenguaje de alto nivel, porque cuando hablas él escucha su propio lenguaje (más bien crudo).
Ahora, supongamos que quieres que camine de A a B. ¡No hablarías directamente con sus piernas o pies, le dirías a él su cara! Él está en control de su propio cuerpo. Si 1) comunica correctamente su solicitud y 2) él decide que corresponde a sus obligaciones laborales, se trasladará de A a B.
Ahora obtienes un nuevo servidor, del mismo país que el anterior (porque prefieres no comprar un nuevo traductor de Star Trek). Usted quiere que él camine de A a B también. Pero este sirviente requiere que usted hable más alto y diga por favor mientras pregunta. Aguanta esto porque es más flexible: puede pedirle que vaya de A a B a través de C si lo desea; el mayordomo anterior podría hacerlo pero arrastró los pies y se quejó.
Otro golpe de suerte es que puede ajustar la configuración de su traductor para manejar esto, por lo que, desde la perspectiva de su idioma, nada cambia. Pero si tuviera que hablar con el viejo mayordomo con la nueva configuración, estaría confundido y no lo entendería aunque esté hablando su idioma.
En caso de que no quede claro, los mayordomos son computadoras con la misma ISA pero con diferentes sistemas operativos. El traductor es la cadena de herramientas de compilación cruzada que se dirige a su ISA.