¿Por qué el software OS es específico?


77

Estoy tratando de determinar los detalles técnicos de por qué el software producido usando lenguajes de programación para ciertos sistemas operativos solo funciona con ellos.

Entiendo que los binarios son específicos de ciertos procesadores debido al lenguaje de máquina específico del procesador que entienden y los diferentes conjuntos de instrucciones entre los diferentes procesadores. Pero, ¿de dónde viene la especificidad del sistema operativo? Solía ​​suponer que eran API proporcionadas por el sistema operativo, pero luego vi este diagrama en un libro: Diagrama

Sistemas operativos: principios internos y principios de diseño 7ª ed. W. Stallings (Pearson, 2012)

Como puede ver, las API no están indicadas como parte del sistema operativo.

Si, por ejemplo, construyo un programa simple en C usando el siguiente código:

#include<stdio.h>

main()
{
    printf("Hello World");

}

¿El compilador está haciendo algo específico del sistema operativo al compilar esto?


15
¿Imprime en una ventana? o una consola? o a la memoria de gráficos? ¿Cómo pones los datos allí? Mirando printf para Apple] [+ sería silenciosamente diferente que para un Mac OS 7 y de nuevo bastante diferente a Mac OS X (solo con una 'línea' de computadoras).

3
Porque si escribiste ese código para Mac OS 7, aparecería en texto en una nueva ventana. Si lo hicieras en Apple] [+, estaría escribiendo en algún segmento de memoria directamente. En Mac OS X, lo escribe en una consola. Por lo tanto, se trata de tres formas diferentes de escribir manejando el código basado en el hardware de ejecución manejado por la capa de la biblioteca.

2
@StevenBurnap sip - en.wikipedia.org/wiki/Aztec_C

10
Su función FFT se ejecutará felizmente bajo Windows o Linux (en la misma CPU), incluso sin recompilar. Pero entonces, ¿cómo vas a mostrar el resultado? Usando una API de sistema operativo, por supuesto. ( printfdesde msvcr90.dll no es lo mismo que printfdesde libc.so.6)
user253751

99
Incluso si las API "no son parte del sistema operativo", siguen siendo diferentes si pasa de un sistema operativo a otro. (Lo que, por supuesto, plantea la pregunta de qué significa realmente la frase "no forma parte del sistema operativo", según el diagrama).
Theodoros Chatzigiannakis

Respuestas:


78

Usted menciona cómo si el código es específico para una CPU, ¿por qué debe ser específico también para un sistema operativo? Esta es realmente una pregunta más interesante que muchas de las respuestas aquí han asumido.

Modelo de seguridad de la CPU

El primer programa que se ejecuta en la mayoría de las arquitecturas de CPU se ejecuta dentro de lo que se denomina anillo interno o anillo 0 . La forma en que un arco de CPU específico implementa los anillos varía, pero es evidente que casi todas las CPU modernas tienen al menos 2 modos de operación, uno que es privilegiado y ejecuta código 'bare metal' que puede realizar cualquier operación legal que la CPU pueda realizar y el otro es no confiable y ejecuta código protegido que solo puede realizar un conjunto seguro de capacidades definido. Sin embargo, algunas CPU tienen una granularidad mucho mayor y, para poder usar las VM de forma segura, se necesitan al menos 1 o 2 anillos adicionales (a menudo etiquetados con números negativos), sin embargo, esto está fuera del alcance de esta respuesta.

Donde entra el sistema operativo

Primeros sistemas operativos de tareas individuales

En los primeros sistemas basados ​​en DOS y otros sistemas basados ​​en tareas individuales, todo el código se ejecutaba en el anillo interno, cada programa que ejecutaba tenía plena potencia sobre toda la computadora y podía hacer literalmente cualquier cosa si se comportaba mal, incluso borrar todos sus datos o incluso dañar el hardware. En algunos casos extremos, como la configuración de modos de visualización no válidos en pantallas de visualización muy antiguas, peor aún, esto podría deberse a un código con errores sin malicia alguna.

De hecho, este código era en gran medida independiente del sistema operativo, siempre que tuviera un cargador capaz de cargar el programa en la memoria (bastante simple para los primeros formatos binarios) y el código no dependía de ningún controlador, implementando todo el acceso de hardware en sí mismo bajo el cual debería ejecutarse cualquier sistema operativo siempre que se ejecute en el anillo 0. Tenga en cuenta que un sistema operativo muy simple como este generalmente se denomina monitor si simplemente se utiliza para ejecutar otros programas y no ofrece funcionalidad adicional.

Sistemas operativos multitarea modernos

Los sistemas operativos más modernos, como UNIX , las versiones de Windows que comienzan con NT y otros sistemas operativos ahora oscuros decidieron mejorar esta situación, los usuarios querían características adicionales como la multitarea para poder ejecutar más de una aplicación a la vez y protección, por lo que un error ( o código malicioso) en una aplicación ya no podría causar daños ilimitados a la máquina y los datos.

Esto se realizó utilizando los anillos mencionados anteriormente, el sistema operativo ocuparía el único lugar ejecutándose en el anillo 0 y las aplicaciones se ejecutarían en los anillos externos no confiables, solo capaces de realizar un conjunto restringido de operaciones que el sistema operativo permitía.

Sin embargo, esta mayor utilidad y protección tuvo un costo, los programas ahora tenían que trabajar con el sistema operativo para realizar tareas que no se les permitía hacer, ya no podían, por ejemplo, tomar el control directo sobre el disco duro accediendo a su memoria y cambiar arbitrariamente datos, en su lugar, tuvieron que pedirle al sistema operativo que realizara estas tareas por ellos para que pudiera verificar que se les permitiera realizar la operación, sin cambiar los archivos que no les pertenecían, también verificaría que la operación fuera realmente válida y no dejaría el hardware en un estado indefinido.

Cada sistema operativo decidió una implementación diferente para estas protecciones, parcialmente basada en la arquitectura para la cual fue diseñado el sistema operativo y parcialmente basada en el diseño y los principios del sistema operativo en cuestión, UNIX, por ejemplo, se enfocó en que las máquinas sean buenas para el uso multiusuario y enfocadas Las características disponibles para esto mientras Windows fue diseñado para ser más simple, para ejecutarse en hardware más lento con un solo usuario. La forma en que los programas de espacio de usuario también se comunican con el sistema operativo es completamente diferente en X86 como lo sería en ARM o MIPS, por ejemplo, lo que obliga a un sistema operativo multiplataforma a tomar decisiones basadas en la necesidad de trabajar en el hardware al que está dirigido.

Estas interacciones específicas del sistema operativo generalmente se denominan "llamadas al sistema" y abarcan cómo un programa espacial de usuario interactúa completamente con el hardware a través del sistema operativo, fundamentalmente difieren según la función del sistema operativo y, por lo tanto, un programa que hace su trabajo a través de las llamadas del sistema necesita ser específico del sistema operativo.

El cargador de programas

Además de las llamadas al sistema, cada sistema operativo proporciona un método diferente para cargar un programa desde el medio de almacenamiento secundario y en la memoria , para que un sistema operativo específico pueda cargarlo, el programa debe contener un encabezado especial que describa al sistema operativo cómo puede ser cargado y corrido.

Este encabezado solía ser lo suficientemente simple como para que escribir un cargador para un formato diferente fuera casi trivial, sin embargo, con formatos modernos como elf que admiten funciones avanzadas como el enlace dinámico y declaraciones débiles, ahora es casi imposible que un sistema operativo intente cargar binarios. que no fueron diseñados para ello, esto significa que, incluso si no existieran las incompatibilidades de llamadas del sistema, es inmensamente difícil incluso colocar un programa en ram de una manera que pueda ejecutarse.

Bibliotecas

Los programas rara vez usan las llamadas del sistema directamente, sin embargo, obtienen casi exclusivamente su funcionalidad a través de bibliotecas que envuelven las llamadas del sistema en un formato un poco más amigable para el lenguaje de programación, por ejemplo, C tiene la Biblioteca estándar C y glibc en Linux y libs similares y win32 en Windows NT y superior, la mayoría de los otros lenguajes de programación también tienen bibliotecas similares que ajustan la funcionalidad del sistema de manera adecuada.

Hasta cierto punto, estas bibliotecas pueden superar los problemas de plataforma cruzada como se describió anteriormente, hay una gama de bibliotecas que están diseñadas para proporcionar una plataforma uniforme a las aplicaciones mientras se administran internamente las llamadas a una amplia gama de sistemas operativos como SDL , esto significa que aunque los programas no pueden ser compatibles con los binarios, los programas que usan estas bibliotecas pueden tener una fuente común entre plataformas, lo que hace que la transferencia sea tan simple como la recompilación.

Excepciones a lo anterior

A pesar de todo lo que he dicho aquí, ha habido intentos de superar las limitaciones de no poder ejecutar programas en más de un sistema operativo. Algunos buenos ejemplos son el proyecto Wine que ha emulado con éxito el cargador de programas win32, el formato binario y las bibliotecas del sistema, lo que permite que los programas de Windows se ejecuten en varios UNIX. También hay una capa de compatibilidad que permite que varios sistemas operativos BSD UNIX ejecuten software de Linux y, por supuesto, la propia cuña de Apple que permite ejecutar software antiguo de MacOS en MacOS X.

Sin embargo, estos proyectos funcionan a través de enormes niveles de esfuerzo de desarrollo manual. Dependiendo de cuán diferentes sean los dos sistemas operativos, la dificultad varía desde una cuña bastante pequeña hasta una emulación casi completa del otro sistema operativo, que a menudo es más complejo que escribir un sistema operativo completo en sí mismo, por lo que esta es la excepción y no la regla.


66
+1 "¿Por qué el sistema operativo del software es específico?" Porque la historia
Paul Draper

2
¿Se originó el modelo de seguridad de CPU x86? ¿Por qué y cuándo se inventó el modelo?
n611x007

8
@naxa No, es anterior a x86, se implementó por primera vez parcialmente para Multics en 1969, que es el primer sistema operativo con funciones útiles de tiempo compartido para múltiples usuarios que requieren este modelo en la computadora GE-645 , sin embargo, esta implementación estaba incompleta y se confiaba en soporte de software, la primera implementación completa y segura en hardware fue en su sucesor, el Honeywell 6180 . Esto estaba completamente basado en hardware y permitía a Multics ejecutar código de múltiples usuarios sin la oportunidad de interferir de forma cruzada.
Vality

@Vality También, IBM LPAR es ~ 1972.
Elliott Frisch

@ElliottFrisch wow, eso es impresionante. No me había dado cuenta de que era tan temprano. Gracias por esa información
Vality

48

Como puede ver, las API no están indicadas como parte del sistema operativo.

Creo que estás leyendo demasiado en el diagrama. Sí, un sistema operativo especificará una interfaz binaria para la forma en que se llaman las funciones del sistema operativo, y también definirá un formato de archivo para los ejecutables, pero también proporcionará una API, en el sentido de proporcionar un catálogo de funciones a las que puede llamar una aplicación para invocar servicios del sistema operativo.

Creo que el diagrama solo está tratando de enfatizar que las funciones del sistema operativo generalmente se invocan a través de un mecanismo diferente al de una simple llamada a la biblioteca. La mayoría de los sistemas operativos comunes utilizan interrupciones del procesador para acceder a las funciones del sistema operativo. Los sistemas operativos modernos típicos no permitirán que un programa de usuario acceda directamente a ningún hardware. Si desea escribir un personaje en la consola, tendrá que pedirle al sistema operativo que lo haga por usted. La llamada al sistema utilizada para escribir en la consola variará de un sistema operativo a otro, por lo que hay un ejemplo de por qué el software es específico del sistema operativo.

printf es una función de la biblioteca de tiempo de ejecución C y en una implementación típica es una función bastante compleja. Si buscas en Google, puedes encontrar la fuente de varias versiones en línea. Vea esta página para una visita guiada de uno . Abajo, aunque termina haciendo una o más llamadas al sistema, y ​​cada una de esas llamadas al sistema es específica del sistema operativo del host.


44
¿Qué pasaría si todo el programa hiciera era agregar dos números, sin entrada ni salida? ¿Ese programa seguiría siendo específico del sistema operativo?
Paul

2
Los sistemas operativos están destinados a poner la mayoría de las cosas específicas del hardware detrás / en una capa de abstracción. Sin embargo, el sistema operativo en sí (la abstracción) puede diferir de una implementación a otra. Hay POSIX a los que se adhieren algunos sistemas operativos (más o menos) y tal vez algunos otros, pero los sistemas operativos generales simplemente difieren demasiado en su parte "visible" de la abstracción. Como se dijo antes: no puede abrir / home / user en Windows y no puede acceder a HKEY_LOCAL_MACHINE \ ... en un sistema * N * X. Usted puede escribir software virtual ( "emulación") para que esto ayudar a que estos sistemas más cerca juntos, pero que siempre será "tercera parte" (de OS POV).
RobIII

16
@Paul Sí. En particular, la forma en que se empaqueta en un ejecutable sería específica del sistema operativo.
OrangeDog

44
@TimSeguine No estoy de acuerdo con su ejemplo de XP vs 7. Microsoft hace mucho trabajo para garantizar que exista la misma API en 7 que en XP. Claramente, lo que ha sucedido aquí es que el programa fue diseñado para ejecutarse contra una determinada API o contrato. El nuevo sistema operativo simplemente se adhirió a la misma API / contrato. Sin embargo, en el caso de Windows, esa API es muy propietaria, por lo que ningún otro proveedor de sistemas operativos lo admite. Incluso entonces hay muchos ejemplos de programas que NO se ejecutan el 7.
ArTs

3
@Paul: Un programa que no hace Entrada / Salida es el programa vacío , que debe compilarse en un no-op.
Bergi

14

¿El compilador está haciendo algo específico del sistema operativo al compilar esto?

Probablemente. En algún momento durante el proceso de compilación y vinculación, su código se convierte en un binario específico del sistema operativo y se vincula con las bibliotecas necesarias. Su programa debe guardarse en un formato que el sistema operativo espera para que el sistema operativo pueda cargar el programa y comenzar a ejecutarlo. Además, está llamando a la función de biblioteca estándar printf(), que en algún nivel se implementa en términos de los servicios que proporciona el sistema operativo.

Las bibliotecas proporcionan una interfaz, una capa de abstracción del sistema operativo y el hardware, y eso permite recompilar su programa para un sistema operativo diferente o un hardware diferente. Pero esa abstracción existe en el nivel fuente: una vez que el programa se compila y se vincula, se conecta a una implementación específica de esa interfaz que es específica de un sistema operativo determinado.


12

Hay varias razones, pero una muy importante es que el sistema operativo debe saber cómo leer la serie de bytes que componen su programa en la memoria, encontrar las bibliotecas que van con ese programa y cargarlas en la memoria, y luego comience a ejecutar su código de programa. Para hacer esto, los creadores del sistema operativo crean un formato particular para esa serie de bytes para que el código del sistema operativo sepa dónde buscar las diversas partes de la estructura de su programa. Debido a que los principales sistemas operativos tienen diferentes autores, estos formatos a menudo tienen poco que ver entre sí. En particular, el formato ejecutable de Windows tiene poco en común con el formato ELF que utilizan la mayoría de las variantes de Unix. Por lo tanto, todo este código de carga, vinculación dinámica y ejecución debe ser específico del sistema operativo.

A continuación, cada sistema operativo proporciona un conjunto diferente de bibliotecas para hablar con la capa de hardware. Estas son las API que mencionas, y generalmente son bibliotecas que presentan una interfaz más simple para el desarrollador mientras se traducen en llamadas más complejas y específicas a las profundidades del sistema operativo en sí, estas llamadas a menudo están indocumentadas o aseguradas. Esta capa a menudo es bastante gris, con las nuevas API de "SO" que se construyen parcial o totalmente en las API más antiguas. Por ejemplo, en Windows, muchas de las API más nuevas que Microsoft ha creado a lo largo de los años son esencialmente capas superiores a las API Win32 originales.

Un problema que no surge en su ejemplo, pero que es uno de los más grandes que enfrentan los desarrolladores es la interfaz con el administrador de ventanas, para presentar una GUI. Si el administrador de ventanas es parte del "SO" a veces depende de su punto de vista, así como del SO en sí, con la GUI en Windows integrada con el SO en un nivel más profundo, mientras que las GUI en Linux y OS X Más directamente separados. Esto es muy importante porque hoy en día lo que la gente suele llamar "El sistema operativo" es una bestia mucho más grande que lo que los libros de texto tienden a describir, ya que incluye muchos componentes de nivel de aplicación.

Finalmente, no es estrictamente un problema del sistema operativo, pero uno importante en la generación de archivos ejecutables es que diferentes máquinas tienen diferentes objetivos de lenguaje ensamblador, por lo que el código objeto generado real debe ser diferente. Esto no es estrictamente un problema de "sistema operativo", sino un problema de hardware, pero sí significa que necesitará diferentes compilaciones para diferentes plataformas de hardware.


2
Puede valer la pena tener en cuenta que los formatos ejecutables más simples se pueden cargar usando solo una pequeña cantidad de RAM (si la hay) más allá de la requerida para contener el código cargado, mientras que los formatos más complejos pueden requerir una huella RAM mucho mayor durante, y en algunos casos incluso después de cargar. MS-DOS cargaría archivos COM de hasta 63.75K simplemente leyendo bytes secuenciales a RAM comenzando en el desplazamiento 0x100 de un segmento arbitrario, cargando CX con la dirección final y saltando a eso. La compilación de un solo paso podría lograrse sin parches posteriores (útil con disquetes) por ...
supercat

1
... hacer que el compilador incluya con cada rutina una lista de todos los puntos de parche, cada uno de los cuales incluiría la dirección de la lista anterior, y poner la dirección de la última lista al final del código. El sistema operativo simplemente cargaría el código como bytes sin procesar, pero una pequeña rutina dentro del código podría aplicar todos los parches de dirección necesarios antes de ejecutar la parte principal del código.
supercat

9

De otra respuesta mía:

Considere las primeras máquinas DOS y cuál fue la verdadera contribución de Microsoft al mundo:

Autocad tuvo que escribir controladores para cada impresora en la que podían imprimir. También lo hizo el loto 1-2-3. De hecho, si quería que su software se imprimiera, tenía que escribir sus propios controladores. Si había 10 impresoras y 10 programas, entonces 100 piezas diferentes de esencialmente el mismo código tenían que escribirse por separado e independientemente.

Lo que Windows 3.1 intentó lograr (junto con GEM y muchas otras capas de abstracción) es hacer que el fabricante de la impresora escribiera un controlador para su impresora, y el programador escribió un controlador para la clase de impresoras de Windows.

Ahora, con 10 programas y 10 impresoras, solo se deben escribir 20 piezas de código, y dado que el lado del código de Microsoft era el mismo para todos, los ejemplos de MS significaron que tenía muy poco trabajo por hacer.

Ahora, un programa no se limitaba solo a las 10 impresoras que eligieron admitir, sino a todas las impresoras cuyos fabricantes proporcionaron controladores en Windows.

Por lo tanto, el sistema operativo proporciona servicios a las aplicaciones para que las aplicaciones no tengan que hacer un trabajo redundante.

Su programa C de ejemplo utiliza printf, que envía caracteres a stdout, un recurso específico del sistema operativo que mostrará los caracteres en una interfaz de usuario. El programa no necesita saber dónde está la interfaz de usuario: podría estar en DOS, podría estar en una ventana gráfica, podría canalizarse a otro programa y usarse como entrada para otro proceso.

Debido a que el sistema operativo proporciona estos recursos, los programadores pueden lograr mucho más con poco trabajo.

Sin embargo, incluso comenzar un programa es complicado. El sistema operativo espera que un archivo ejecutable tenga cierta información al principio que le indica al sistema operativo cómo debe iniciarse y, en algunos casos (entornos más avanzados como Android o iOS), qué recursos se requerirán que necesiten aprobación, ya que tocan recursos fuera del "sandbox": una medida de seguridad para ayudar a proteger a los usuarios y otras aplicaciones de los programas que se comportan mal.

Entonces, incluso si el código de máquina ejecutable es el mismo, y no se requieren recursos del sistema operativo, un programa compilado para Windows no se ejecutará en un sistema operativo OS X sin una capa de emulación o traducción adicional, incluso en el mismo hardware exacto.

Los primeros sistemas operativos de estilo DOS a menudo podían compartir programas, porque implementaban la misma API en hardware (BIOS) y el sistema operativo conectado al hardware para proporcionar servicios. Entonces, si escribió y compiló un programa COM , que es solo una imagen de memoria de una serie de instrucciones del procesador, podría ejecutarlo en CP / M, MS-DOS y varios otros sistemas operativos. De hecho, aún puede ejecutar programas COM en máquinas Windows modernas. Otros sistemas operativos no utilizan los mismos enlaces API de BIOS, por lo que los programas COM no se ejecutarán en ellos sin, nuevamente, una capa de emulación o traducción. Los programas EXE siguen una estructura que incluye mucho más que simples instrucciones de procesador, por lo que, junto con los problemas de API, no se ejecutará en una máquina que no entienda cómo cargarla en la memoria y ejecutarla.


7

En realidad, la respuesta real es que si cada sistema operativo entendiera el mismo diseño de archivo binario ejecutable, y solo se limitara a funciones estandarizadas (como en la biblioteca estándar C) que el sistema operativo proporcionó (qué sistemas operativos proporcionan), entonces su software lo haría , de hecho, se ejecuta en cualquier sistema operativo.

Por supuesto, la realidad es que ese no es el caso. Un EXEarchivo no tiene el mismo formato que un ELFarchivo, aunque ambos contienen código binario para la misma CPU. * Por lo tanto, cada sistema operativo debería ser capaz de interpretar todos los formatos de archivo, y simplemente no hicieron esto en el comenzando, y no había razón para que comenzaran a hacerlo más tarde (casi seguramente por razones comerciales más que técnicas).

Además, su programa probablemente necesita hacer cosas que la biblioteca C no define cómo hacer (incluso para cosas simples como enumerar el contenido de un directorio), y en esos casos cada sistema operativo proporciona sus propias funciones para lograr su tarea, lo que naturalmente significa que no habrá un mínimo común denominador para que uses (a menos que lo hagas tú mismo).

Entonces, en principio, es perfectamente posible. De hecho, WINE ejecuta ejecutables de Windows directamente en Linux.
Pero es un montón de trabajo y (generalmente) comercialmente injustificado.

* Nota: Hay mucho más en un archivo ejecutable que solo código binario. Hay un montón de información que le dice al sistema operativo de qué bibliotecas depende el archivo, cuánta memoria de pila necesita, qué funciones exporta a otras bibliotecas que pueden depender de él, dónde el sistema operativo puede encontrar información de depuración relevante, cómo " vuelva a ubicar "el archivo en la memoria si es necesario, cómo hacer que el manejo de excepciones funcione correctamente, etc., etc. de nuevo, podría haber un formato único para esto en el que todos estén de acuerdo, pero simplemente no lo hay.


Dato curioso: hay un formato binario posiz estandarizado, que se puede ejecutar en todos los sistemas operativos. Simplemente no se usa comúnmente.
Marcin

@ Marcin: Parece que no consideras que Windows es un sistema operativo. (¡¿O estás diciendo que Windows puede ejecutar binarios POSIX ?!) A los fines de mi respuesta, POSIX no es el tipo de estándar al que me refiero. La X en POSIX significa Unix. Nunca fue pensado para ser utilizado por ejemplo, Windows, a pesar de que Windows tiene un subsistema POSIX.
Mehrdad

1. Algo puede ejecutarse en múltiples SO sin ejecutarse en todos los SO; 2. Windows desde NT ha podido ejecutar binarios posix.
Marcin

1
@Marcin: (1) Como dije, la X en POSIX significa UNIX . No es un estándar que debía ser seguido por otros sistemas operativos, era solo un intento de alcanzar un denominador común entre los diversos Unix, lo cual es genial pero no tan sorprendente. El hecho de que haya múltiples versiones de sistemas operativos Unix es completamente irrelevante para el punto que he estado tratando de hacer con respecto a la compatibilidad en otros sistemas operativos que no sean Unix. (2) ¿Puede proporcionar una referencia para el # 2?
Mehrdad

1
@Mehrdad: Marcin tiene razón; Windows SUA (Subsistema para aplicaciones Unix) es compatible con POSIX
MSalters

5

El diagrama tiene la capa de "aplicación" (en su mayoría) separada de la capa de "sistema operativo" por las "bibliotecas", y eso implica que "aplicación" y "SO" no necesitan conocerse entre sí. Esa es una simplificación en el diagrama, pero no es del todo cierto.

El problema es que la "biblioteca" tiene tres partes: la implementación, la interfaz de la aplicación y la interfaz del sistema operativo. En principio, los dos primeros se pueden hacer "universales" en lo que respecta al sistema operativo (depende de dónde lo corte), pero la tercera parte, la interfaz con el sistema operativo, generalmente no. La interfaz con el sistema operativo dependerá necesariamente del sistema operativo, las API que proporciona, el mecanismo de empaquetado (por ejemplo, el formato de archivo utilizado por Windows DLL), etc.

Debido a que la "biblioteca" generalmente está disponible como un paquete único, significa que una vez que el programa elige una "biblioteca" para usar, se compromete con un sistema operativo específico. Esto sucede de una de dos maneras: a) el programador selecciona completamente por adelantado, y luego el enlace entre la biblioteca y la aplicación puede ser universal, pero la biblioteca en sí está vinculada al sistema operativo; o b) el programador configura las cosas para que la biblioteca se seleccione cuando ejecuta el programa, pero luego el mecanismo de enlace , entre el programa y la biblioteca, depende del sistema operativo (por ejemplo, el mecanismo DLL en Windows). Cada uno tiene sus ventajas y desventajas, pero de cualquier manera debe elegir con anticipación.

Ahora, esto no significa que sea imposible hacerlo, pero debes ser muy inteligente. Para superar el problema, tendría que seguir la ruta de elegir la biblioteca en tiempo de ejecución, y tendría que encontrar un mecanismo de enlace universal que no dependa del sistema operativo (por lo que es responsable de mantenerlo, mucho más trabajo). Algunas veces vale la pena.

No tiene que hacerlo, pero si va a hacer el esfuerzo para hacerlo, es muy probable que tampoco quiera estar vinculado a un procesador específico, por lo que escribirá una máquina virtual y compilará su programa a un formato de código neutral de procesador.

A estas alturas ya deberías haber notado a dónde voy. Las plataformas de lenguaje como Java hacen exactamente eso. El tiempo de ejecución de Java (biblioteca) define el enlace neutral del sistema operativo entre su programa de Java y la biblioteca (cómo el tiempo de ejecución de Java abre y ejecuta su programa), y proporciona una implementación específica para el sistema operativo actual. .NET hace lo mismo hasta cierto punto, excepto que Microsoft no proporciona una "biblioteca" (tiempo de ejecución) para nada que no sea Windows (pero otros sí, vea Mono). Y, en realidad, Flash también hace lo mismo, aunque tiene un alcance más limitado para el navegador.

Finalmente, hay formas de hacer lo mismo sin un mecanismo de enlace personalizado. Puede usar herramientas convencionales, pero difiere el paso de enlace a la biblioteca hasta que el usuario elija el sistema operativo. Eso es exactamente lo que sucede cuando distribuye el código fuente. El usuario toma su programa y lo vincula al procesador (compilarlo) y al sistema operativo (vincularlo) cuando el usuario está listo para ejecutarlo.

Todo depende de cómo corte las capas. Al final del día, siempre tiene un dispositivo informático hecho con hardware específico que ejecuta un código de máquina específico. Las capas están allí en gran medida como un marco conceptual.


3

El software no siempre es específico del sistema operativo. Tanto Java como el sistema de código p anterior (e incluso ScummVM) permiten el software que es portátil en todos los sistemas operativos. Infocom (fabricantes de Zork y la máquina Z ), también tenía una base de datos relacional basada en otra máquina virtual. Sin embargo, en algún nivel algo tiene que traducir incluso esas abstracciones en instrucciones reales para ser ejecutadas en una computadora.


3
Sin embargo, Java se ejecuta en una máquina virtual, que no es entre sistemas operativos. Tienes que usar un binario JVM diferente para cada sistema operativo
Izkata

3
@Izkata True, pero no recompila el software (solo la JVM). Además, vea mi última oración. Pero señalaré que Sun tenía un microprocesador que podía ejecutar directamente el código de bytes.
Elliott Frisch

3
Java es un sistema operativo, aunque generalmente no se lo considera uno. El software de Java es específico del sistema operativo Java, y existen emuladores del sistema operativo Java para la mayoría de los sistemas operativos "reales". Pero podría hacer lo mismo con cualquier sistema operativo host y de destino, como ejecutar el software de Windows en Linux usando WINE.
user253751

@immibis Sería más específico. Java Foundation Classes (JFC, la biblioteca estándar de Java) es un marco. Java en sí es un lenguaje. La JVM es similar a un sistema operativo: tiene "Máquina virtual" en su nombre y realiza funciones similares a un sistema operativo desde la perspectiva del código que se ejecuta en él.

1

Tu dices

el software producido usando lenguajes de programación para ciertos sistemas operativos solo funciona con ellos

Pero el programa que da como ejemplo funcionará en muchos sistemas operativos, e incluso en algunos entornos de metal desnudo.

Lo importante aquí es la distinción entre el código fuente y el binario compilado. El lenguaje de programación C está específicamente diseñado para ser independiente del sistema operativo en forma de fuente. Lo hace dejando la interpretación de cosas como "imprimir en la consola" hasta el implementador. Pero C puede cumplir con algo que es específico del sistema operativo (ver otras respuestas por razones). Por ejemplo, los formatos ejecutables PE o ELF.


66
Parece bastante claro que el OP pregunta por los binarios, no por el código fuente.
Caleb

0

Otras personas han cubierto bien los detalles técnicos, me gustaría mencionar una razón menos técnica, el lado UX / UI de las cosas:

Escribe una vez, siéntete incómodo en todas partes

Cada sistema operativo tiene sus propias API de interfaz de usuario y estándares de diseño. Es posible escribir una interfaz de usuario para un programa y ejecutarlo en múltiples sistemas operativos, sin embargo, hacerlo garantiza que el programa se sentirá fuera de lugar en todas partes. Hacer una buena interfaz de usuario requiere ajustar los detalles de cada plataforma compatible.

Muchos de estos son pequeños detalles, pero si se equivocan, frustrará a sus usuarios:

  • Confirme que los cuadros de diálogo tienen sus botones en un orden diferente en Windows y OSX; haga esto mal y los usuarios harán clic en el botón equivocado mediante la memoria muscular. Windows tiene "Ok", "Cancelar" en ese orden. OSX tiene el orden intercambiado y el texto del botón de hacerlo es una breve descripción de la acción a realizar: "Cancelar", "Mover a la Papelera".
  • El comportamiento "Volver" es diferente para iOS y Android. Las aplicaciones de iOS dibujan su propio botón de retroceso según sea necesario, generalmente en la esquina superior izquierda. Android tiene un botón dedicado en la esquina inferior izquierda o inferior derecha dependiendo de la rotación de la pantalla. Los puertos rápidos a Android se comportarán incorrectamente si se ignora el botón de retroceso del sistema operativo.
  • El desplazamiento de momento es diferente entre iOS, OSX y Android. Lamentablemente, si no está escribiendo código de interfaz de usuario nativo, es probable que tenga que escribir su propio comportamiento de desplazamiento.

Incluso cuando es técnicamente posible escribir una base de código de interfaz de usuario que se ejecute en todas partes, es mejor realizar ajustes para cada sistema operativo compatible.


-2

Una distinción importante en este punto es separar el compilador del enlazador. Lo más probable es que el compilador produzca más o menos el mismo resultado (las diferencias se deben principalmente a varios #if WINDOWSs). El vinculador, por otro lado, tiene que manejar todas las cosas específicas de la plataforma: vincular las bibliotecas, construir el archivo ejecutable, etc.

En otras palabras, el compilador se preocupa principalmente por la arquitectura de la CPU, porque está produciendo el código ejecutable real, y tiene que usar las instrucciones y los recursos de la CPU (tenga en cuenta que el código de bytes IL o JVM de .NET se consideraría un conjunto de instrucciones de una CPU virtual en esta vista). Es por eso que debe compilar código por separado para x86y ARM, por ejemplo.

El vinculador, por otro lado, tiene que tomar todos estos datos en bruto e instrucciones, y ponerlo en un formato que el cargador (en la actualidad, casi siempre sería el sistema operativo) pueda entender, así como vincular cualquier biblioteca estáticamente vinculada (que también incluye el código requerido para la vinculación dinámica, asignación de memoria, etc.).

En otras palabras, podría compilar el código solo una vez y ejecutarlo tanto en Linux como en Windows, pero debe vincularlo dos veces, produciendo dos ejecutables diferentes. Ahora, en la práctica, a menudo también tiene que hacer concesiones en el código (ahí es donde entran las directivas del (pre) compilador), por lo que incluso compilar una vez el enlace dos veces no se usa mucho. Sin mencionar que las personas tratan la compilación y la vinculación como un solo paso durante la compilación (al igual que a usted ya no le importan las partes del compilador).

El software de la era de DOS a menudo era más portátil binario, pero hay que entender que también se compiló no contra DOS o Unix, sino contra un cierto contrato que era común para la mayoría de las PC de estilo IBM, descargando lo que hoy son llamadas API a interrupciones de software. Esto no necesitaba un enlace estático, ya que solo tenía que establecer los registros necesarios, llamar, por ejemplo, int 13hpara funciones gráficas, y la CPU simplemente saltó a un puntero de memoria declarado en la tabla de interrupciones. Por supuesto, una vez más, la práctica fue mucho más complicada, porque para obtener el rendimiento de pedal al metal, tenía que escribir todos esos métodos usted mismo, pero eso básicamente equivalía a sortear el sistema operativo por completo. Y, por supuesto, hay algo que invariablemente necesita interacción con la API del sistema operativo: la finalización del programa. Pero aún así, si utilizó los formatos más simples disponibles (p. Ej.COMen DOS, que no tiene encabezado, solo instrucciones) y no quería salir, bueno, ¡suerte! Y, por supuesto, también podría manejar la terminación adecuada en el tiempo de ejecución, por lo que podría tener código para la terminación de Unix y la terminación de DOS en el mismo ejecutable, y detectar en el tiempo de ejecución cuál usar :)


esto parece limitarse a repetir los puntos explicados en esta y esta respuestas anteriores que fueron publicadas ayer
mosquito
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.