Pourquoi le logiciel d'exploitation est-il spécifique?

77

J'essaie de déterminer les détails techniques de la raison pour laquelle les logiciels produits à l'aide de langages de programmation pour certains systèmes d'exploitation ne fonctionnent qu'avec eux.

D'après ce que j'ai compris, les fichiers binaires sont spécifiques à certains processeurs en raison du langage machine spécifique qu'ils maîtrisent et des jeux d'instructions différents entre les différents processeurs. Mais d'où vient la spécificité du système d'exploitation? J'avais l'habitude de supposer que c'était des API fournies par le système d'exploitation, mais j'ai vu ce diagramme dans un livre: Diagramme

Systèmes d'exploitation - Composants internes et principes de conception 7ème édition - W. Stallings (Pearson, 2012)

Comme vous pouvez le constater, les API ne font pas partie du système d'exploitation.

Si par exemple je construis un programme simple en C en utilisant le code suivant:

#include<stdio.h>

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

}

Le compilateur fait-il quelque chose de spécifique à l'OS lors de la compilation?

utilisateur139929
la source
15
Imprimez-vous sur une fenêtre? ou une console? ou à la mémoire graphique? Comment mettez-vous les données là-bas? Le fait de regarder printf pour Apple] [+ serait très différent de celui d’un Mac OS 7 et très différent de celui de Mac OS X (s’en tenir à une «ligne» d’ordinateurs).
3
Parce que si vous écriviez ce code pour Mac OS 7, il apparaîtrait sous forme de texte dans une nouvelle fenêtre. Si vous le faisiez sur Apple] [+, cela écrirait directement sur un segment de mémoire. Sur Mac OS X, il écrit dans une console. Ainsi, trois méthodes différentes d’écriture traitent le code en fonction du matériel d’exécution géré par la couche bibliothèque.
2
@StevenBurnap yep - fr.wikipedia.org/wiki/Aztec_C
10
Votre fonction FFT fonctionnera sans problème sous Windows ou Linux (sur le même processeur), sans même recompiler. Mais alors, comment allez-vous afficher le résultat? Utiliser une API de système d'exploitation, bien sûr. ( printfde msvcr90.dll n'est pas la même chose que printfde libc.so.6)
user253751
9
Même si les API ne font "pas partie du système d'exploitation", elles restent différentes si vous passez d'un système d'exploitation à un autre. (Ce qui, bien sûr, soulève la question de savoir ce que signifie réellement l'expression "ne faisant pas partie du système d'exploitation", selon le diagramme.)
Theodoros Chatzigiannakis

Réponses:

78

Vous mentionnez comment, si le code est spécifique à une CPU, pourquoi doit-il l'être également à un système d'exploitation? C’est en fait une question plus intéressante que beaucoup des réponses proposées ici.

Modèle de sécurité du processeur

Le premier programme exécuté sur la plupart des architectures de CPU s'exécute dans ce qu'on appelle l'anneau interne ou anneau 0 . La manière dont une arche de processeur spécifique implémente des anneaux varie, mais il est évident que presque tous les processeurs modernes ont au moins deux modes de fonctionnement, l'un privilégié, qui exécute un code «bare metal» pouvant effectuer toutes les opérations légales que le processeur peut effectuer et l'autre. non approuvé et exécute un code protégé qui ne peut exécuter qu'un ensemble de capacités sécurisé défini. Certains processeurs ont cependant une granularité beaucoup plus élevée et pour pouvoir utiliser les VM de manière sécurisée, il faut au moins 1 ou 2 sonneries supplémentaires (souvent étiquetées avec des nombres négatifs), mais cela dépasse le cadre de cette réponse.

Où l'OS entre en jeu

Premiers systèmes d'exploitation à tâche unique

Dans les tout premiers systèmes DOS et autres systèmes basés sur des tâches uniques, tout le code était exécuté dans l'anneau interne. Chaque programme que vous exécutiez disposait alors du pouvoir absolu sur tout l'ordinateur et pouvait littéralement faire n'importe quoi s'il se comportait mal, y compris effacer toutes vos données ou même endommager le matériel. dans quelques cas extrêmes tels que la définition de modes d'affichage non valides sur de très vieux écrans, cela pourrait être dû à un code buggy sans aucune malveillance.

Ce code était en fait largement indépendant des systèmes d’exploitation, tant que vous disposiez d’un chargeur capable de charger le programme en mémoire (assez simple pour les premiers formats binaires) et que le code ne reposait sur aucun pilote, implémentant tous les accès matériels auxquels il était destiné. n’importe quel système d’exploitation tant qu’il est exécuté dans l’anneau 0. Remarque: un système d’exploitation très simple comme celui-ci est généralement appelé moniteur s’il est simplement utilisé pour exécuter d’autres programmes sans offrir aucune fonctionnalité supplémentaire.

Systèmes d'exploitation multitâches modernes

Des systèmes d’exploitation plus modernes, notamment UNIX , des versions de Windows commençant par NT et divers autres systèmes d’exploitation devenus obscurs, ont décidé d’améliorer cette situation. Les utilisateurs souhaitaient des fonctionnalités supplémentaires telles que le multitâche afin de pouvoir exécuter plusieurs applications à la fois et une protection, de sorte qu'un bogue ( code malveillant) dans une application ne peut plus causer de dommages illimités à la machine et aux données.

Cela a été fait en utilisant les anneaux mentionnés ci-dessus, le système d'exploitation prendrait la place unique dans l'anneau 0 et les applications s'exécuteraient dans les anneaux externes non fiables, ne pouvant effectuer qu'un ensemble restreint d'opérations autorisées par le système d'exploitation.

Cependant, cette utilité et cette protection accrues avaient un coût, les programmes devaient désormais travailler avec le système d'exploitation pour effectuer des tâches qu'ils n'étaient pas autorisés à effectuer eux-mêmes; ils ne pouvaient par exemple plus prendre le contrôle direct du disque dur en accédant à sa mémoire et en modifiant les paramètres de façon arbitraire. données, au lieu de cela, ils ont dû demander au système d’exécuter ces tâches pour pouvoir vérifier s’ils étaient autorisés à effectuer l’opération, sans modifier les fichiers qui ne leur appartenaient pas, cela vérifierait également la validité de l’opération et ne laisserait pas le matériel dans un état non défini.

Chaque système d’exploitation a choisi une implémentation différente pour ces protections, basée en partie sur l’architecture pour laquelle le système d’exploitation avait été conçue et basée en partie sur la conception et les principes du système d’exploitation en question. les fonctionnalités disponibles pour cela alors que Windows a été conçue pour être plus simple, pour fonctionner sur du matériel plus lent avec un seul utilisateur. La manière dont les programmes de l'espace utilisateur communiquent également avec le système d'exploitation est complètement différente sur X86 comme sur ARM ou MIPS, par exemple, obligeant un système d'exploitation multi-plateforme à prendre des décisions en fonction du besoin de travailler sur le matériel pour lequel il est ciblé.

Ces interactions spécifiques au système d'exploitation sont généralement appelées "appels système" et englobent la manière dont un programme d'espace utilisateur interagit complètement avec le matériel via le système d'exploitation. Elles diffèrent fondamentalement en fonction de la fonction du système d'exploitation. être spécifique à l'OS.

Le chargeur de programme

En plus des appels système, chaque système d' exploitation fournit une méthode différente pour charger un programme à partir du support de stockage secondaire , et dans la mémoire , pour être chargeable par un système d' exploitation spécifique , le programme doit contenir un en- tête spécial décrit à l'OS comment elle peut être chargé et couru.

Cet en-tête était assez simple pour écrire un chargeur pour un format différent était presque trivial. Cependant, avec les formats modernes tels que elf qui supporte des fonctionnalités avancées telles que la liaison dynamique et les déclarations faibles, il est maintenant presque impossible pour un système d'exploitation d'essayer de charger des fichiers binaires ce qui signifie que même s'il n'y avait pas d'incompatibilités entre les appels système, il est extrêmement difficile de placer un programme en mémoire de manière à ce qu'il puisse être exécuté.

Bibliothèques

Les programmes utilisent rarement les appels système directement, cependant, ils obtiennent presque exclusivement leurs fonctionnalités bien que les bibliothèques encapsulent les appels système dans un format légèrement plus convivial pour le langage de programmation, par exemple, C possède la bibliothèque standard C et la glibc sous Linux et les bibliothèques win32 sous Windows NT et supérieur, la plupart des autres langages de programmation ont également des bibliothèques similaires qui encapsulent les fonctionnalités du système de manière appropriée.

Ces bibliothèques peuvent même, dans une certaine mesure, résoudre les problèmes multi-plateformes décrits ci-dessus. Il existe une gamme de bibliothèques conçues pour fournir une plate-forme uniforme aux applications tout en gérant en interne les appels vers une large gamme de systèmes d’exploitation tels que SDL . les programmes ne peuvent pas être compatibles binaires, les programmes qui utilisent ces bibliothèques peuvent avoir une source commune entre les plates-formes, ce qui rend le portage aussi simple que la recompilation.

Exceptions à ce qui précède

Malgré tout ce que j'ai dit ici, il y a eu des tentatives pour surmonter les limitations de l'impossibilité d'exécuter des programmes sur plusieurs systèmes d'exploitation. Parmi les bons exemples, citons le projet Wine, qui a émulé avec succès le chargeur de programme win32, le format binaire et les bibliothèques système permettant aux programmes Windows de s'exécuter sous divers UNIX. Il existe également une couche de compatibilité permettant à plusieurs systèmes d’exploitation BSD UNIX d’exécuter un logiciel Linux et, bien entendu, au propre logiciel d’impression d’Apple permettant d’exécuter un ancien logiciel MacOS sous MacOS X.

Cependant, ces projets nécessitent des efforts de développement manuels considérables. En fonction de la différence entre les deux systèmes d’exploitation, la difficulté varie d’une cale d’impression assez petite à une émulation presque complète de l’autre système d’exploitation, qui est souvent plus complexe que l’écriture de tout un système d’exploitation. C’est donc une exception et non la règle.

Vality
la source
6
+1 "Pourquoi le logiciel d'exploitation est-il spécifique?" Parce que l'histoire.
Paul Draper
2
est le modèle de sécurité du processeur d'origine x86? pourquoi et quand le modèle a-t-il été inventé?
n611x007
8
@naxa Non, il a longtemps précédé le x86, il a d'abord été partiellement implémenté pour Multics en 1969, ce qui en fait le premier système d'exploitation avec des fonctionnalités utiles de partage du temps multi-utilisateurs nécessitant ce modèle dans l' ordinateur GE-645 . Toutefois, cette implémentation était incomplète et fiable. support logiciel, la première implémentation matérielle complète et sécurisée se trouvait dans son successeur, le Honeywell 6180 . Ceci était entièrement basé sur le matériel et permettait à Multics d’exécuter du code à partir de plusieurs utilisateurs sans possibilité d’interférence croisée.
Vality
@Vality En outre, IBM LPAR est ~ 1972.
Elliott Frisch
@ ElliottFrisch wow, c'est impressionnant. Je n'avais pas réalisé que c'était aussi tôt. Merci pour cette info.
Vality
48

Comme vous pouvez le constater, les API ne font pas partie du système d'exploitation.

Je pense que vous lisez trop dans le diagramme. Oui, un système d’exploitation spécifiera une interface binaire pour l’appel des fonctions du système d’exploitation. Il définira également un format de fichier pour les exécutables, mais fournira également une API, dans le sens de fournir un catalogue de fonctions pouvant être appelées par une application pour appeler des services OS.

Je pense que le diagramme tente simplement de souligner que les fonctions du système d'exploitation sont généralement appelées par un mécanisme différent de celui d'un simple appel à une bibliothèque. La plupart des systèmes d’exploitation courants utilisent des interruptions de processeur pour accéder aux fonctions du système d’exploitation. Les systèmes d'exploitation modernes ne permettent pas à un programme utilisateur d'accéder directement à un matériel. Si vous voulez écrire un caractère sur la console, vous devrez demander au système d'exploitation de le faire pour vous. L'appel système utilisé pour écrire sur la console varie d'un système d'exploitation à l'autre. Par conséquent, voici un exemple de la raison pour laquelle le logiciel est spécifique à un système d'exploitation.

printf est une fonction de la bibliothèque d'exécution C et, dans une implémentation typique, une fonction assez complexe. Si vous google, vous pouvez trouver la source de plusieurs versions en ligne. Voir cette page pour une visite guidée d'un . Au sol, il passe un ou plusieurs appels système et chacun de ces appels est spécifique au système d'exploitation hôte.

Charles E. Grant
la source
4
Et si tout le programme faisait, c’était d’ajouter deux nombres, sans entrée ni sortie. Ce programme serait-il toujours spécifique à l'OS?
Paul
2
Les systèmes d'exploitation sont destinés à placer la plupart des éléments spécifiques au matériel derrière / dans une couche d'abstraction. Cependant, le système d'exploitation lui-même (l'abstraction) peut différer d'une implémentation à l'autre. Il y a POSIX auquel certains systèmes d'exploitation (plus ou moins) adhèrent et peut-être d'autres, mais globalement, les systèmes d'exploitation diffèrent trop de par leur partie "visible" de l'abstraction. Comme dit précédemment: vous ne pouvez pas ouvrir / home / user sous Windows et vous ne pouvez pas accéder à HKEY_LOCAL_MACHINE \ ... sur un système * N * X. Vous pouvez écrire un logiciel virtuel ("émulation") à cette fin pour aider à rapprocher ces systèmes, mais ce sera toujours une "3ème partie" (à partir du système d'exploitation POV).
RobIII
16
@ Paul Oui. En particulier, la manière dont il est empaqueté dans un exécutable serait spécifique au système d'exploitation.
Cessez de nuire à Monica le
4
@ TimSeguine Je ne suis pas d'accord avec votre exemple de XP vs 7. Microsoft s'efforce de garantir que la même API existe dans 7 comme dans XP. Il est clair que le programme a été conçu pour fonctionner avec une API ou un contrat donné. Le nouveau système d'exploitation vient d'adhérer à la même API / au même contrat. Cependant, dans le cas de Windows, cette API est très largement propriétaire, raison pour laquelle aucun autre fournisseur de système d'exploitation ne la prend en charge. Même dans ce cas, il existe de nombreux exemples de programmes qui ne fonctionnent pas sur 7.
ArTs
3
@Paul: Un programme qui ne fait pas d'entrées / sorties est le programme vide , qui devrait compiler en no-op.
Bergi
14

Le compilateur fait-il quelque chose de spécifique à l'OS lors de la compilation?

Probablement. À un moment donné pendant le processus de compilation et de liaison, votre code est transformé en un binaire spécifique au système d'exploitation et lié à toutes les bibliothèques requises. Votre programme doit être enregistré dans un format que le système d'exploitation attend afin que le système d'exploitation puisse le charger et commencer à l'exécuter. De plus, vous appelez la fonction de bibliothèque standard printf(), qui est mise en œuvre à un certain niveau en termes de services fournis par le système d'exploitation.

Les bibliothèques fournissent une interface - une couche d’abstraction du système d’exploitation et du matériel - qui permet de recompiler votre programme pour un système d’exploitation ou un matériel différent. Mais cette abstraction existe au niveau source: une fois que le programme est compilé et lié, il est connecté à une implémentation spécifique de cette interface spécifique à un système d'exploitation donné.

Caleb
la source
12

Il y a un certain nombre de raisons, mais l'une des plus importantes est que le système d'exploitation doit savoir lire la série d'octets constituant votre programme en mémoire, rechercher les bibliothèques associées à ce programme et les charger en mémoire, et puis commencez à exécuter votre code de programme. Pour ce faire, les créateurs du système d'exploitation créent un format particulier pour cette série d'octets afin que le code du système d'exploitation sache où rechercher les différentes parties de la structure de votre programme. Parce que les principaux systèmes d'exploitation ont des auteurs différents, ces formats ont souvent peu à faire les uns avec les autres. En particulier, le format exécutable Windows a peu de points communs avec le format ELF utilisé par la plupart des variantes Unix. Donc, tout ce chargement, cette liaison dynamique et ce code doivent être spécifiques au système d'exploitation.

Ensuite, chaque système d'exploitation fournit un ensemble de bibliothèques différent pour communiquer avec la couche matérielle. Ce sont les API que vous mentionnez, et ce sont généralement des bibliothèques qui présentent une interface plus simple au développeur tout en la traduisant en appels plus complexes et plus spécifiques dans les profondeurs du système d’exploitation lui-même, ces appels étant souvent non documentés ou sécurisés. Cette couche est souvent assez grise, avec les API "OS" les plus récentes, construites partiellement ou entièrement sur des API plus anciennes. Par exemple, sous Windows, bon nombre des API les plus récentes créées par Microsoft au cours des années sont essentiellement des couches superposées aux API Win32 d’origine.

Un problème qui ne se pose pas dans votre exemple, mais c’est l’un des problèmes les plus importants auxquels les développeurs sont confrontés est l’interface avec le gestionnaire de fenêtres, qui permet de présenter une interface graphique. Que le gestionnaire de fenêtres fasse partie du "système d'exploitation" dépend parfois de votre point de vue, ainsi que du système d'exploitation lui-même, l'interface graphique de Windows étant intégrée au système d'exploitation à un niveau plus profond, tandis que les interfaces graphiques de Linux et OS X sont plus directement séparé. Ceci est très important car ce que les gens appellent généralement "le système d'exploitation" est une bête bien plus grosse que ce que les manuels ont tendance à décrire, car elle inclut de très nombreux composants au niveau de l'application.

Enfin, il n’est pas strictement un problème de système d’exploitation, mais un aspect important de la génération de fichiers exécutables réside dans le fait que différentes machines ont des cibles en langage assembleur différentes et que, par conséquent, le code objet généré doit être différent. Ce n'est pas à proprement parler un problème de "système d'exploitation" mais plutôt un problème de matériel, mais cela signifie que vous aurez besoin de versions différentes pour différentes plates-formes matérielles.

Gort le robot
la source
2
Il peut être intéressant de noter que des formats exécutables plus simples peuvent être chargés en utilisant seulement une petite quantité de RAM (le cas échéant) au-delà de celle requise pour contenir le code chargé, tandis que des formats plus complexes peuvent nécessiter une empreinte de mémoire vive beaucoup plus grande pendant, et dans certains cas même après le chargement. MS-DOS chargerait des fichiers COM jusqu'à 63,75 K en lisant simplement des octets séquentiels dans la RAM commençant à l'offset 0x100 d'un segment arbitraire, chargerait CX avec l'adresse de fin et passerait à celle-ci. La compilation en un seul passage pourrait être réalisée sans rétro-patch (utile avec les disquettes) en ...
Supercat
1
... faire en sorte que le compilateur inclue avec chaque routine une liste de tous les points de patch, chacun d'eux incluant l'adresse de la liste précédente, et en plaçant l'adresse de la dernière liste à la fin du code. Le système d'exploitation chargerait simplement le code sous forme d'octets bruts, mais une petite routine dans le code pourrait appliquer tous les correctifs d'adresse nécessaires avant d'exécuter la partie principale du code.
Supercat
9

Une autre de mes réponses :

Considérez les premières machines DOS et la véritable contribution de Microsoft au monde:

Autocad devait écrire des pilotes pour chaque imprimante sur laquelle il pouvait imprimer. De même que Lotus 1-2-3. En fait, si vous vouliez que votre logiciel soit imprimé, vous deviez écrire vos propres pilotes. S'il y avait 10 imprimantes et 10 programmes, il fallait alors écrire séparément et indépendamment 100 différents types de code.

Ce que Windows 3.1 a essayé d'accomplir (avec GEM et de nombreuses autres couches d'abstraction), c'est de permettre au fabricant d'imprimante d'écrire un pilote pour son imprimante et au programmeur d'écrire un pilote pour la classe d'imprimantes Windows.

Maintenant, avec 10 programmes et 10 imprimantes, il ne reste plus que 20 morceaux de code à écrire. Etant donné que la partie microsoft du code était identique pour tout le monde, les exemples fournis par MS signifiaient que vous aviez très peu de travail à faire.

Désormais, un programme ne se limite pas aux 10 imprimantes qu’ils ont choisi de prendre en charge, mais à toutes les imprimantes dont les fabricants fournissent des pilotes pour Windows.

Le système d'exploitation fournit donc des services aux applications afin que celles-ci n'aient pas à faire un travail redondant.

Votre exemple de programme en C utilise printf, qui envoie des caractères à stdout, une ressource spécifique au système d'exploitation qui affiche les caractères sur une interface utilisateur. Le programme n'a pas besoin de savoir où se trouve l'interface utilisateur - cela pourrait être sous DOS, dans une fenêtre graphique, il pourrait être redirigé vers un autre programme et utilisé comme entrée dans un autre processus.

Parce que le système d'exploitation fournit ces ressources, les programmeurs peuvent accomplir beaucoup plus avec peu de travail.

Cependant, même démarrer un programme est compliqué. Le système d’exploitation s'attend à ce que le fichier exécutable contienne au début certaines informations lui indiquant comment le démarrer, et dans certains cas (environnements plus avancés tels que Android ou iOS), quelles ressources seront nécessaires et nécessiteront une approbation, car elles touchent des ressources extérieures au système. "sandbox" - une mesure de sécurité destinée à protéger les utilisateurs et les autres applications contre les programmes malveillants.

Ainsi, même si le code machine exécutable est identique et qu’aucune ressource système n’est requise, un programme compilé pour Windows ne fonctionnera pas sur un système d’exploitation OS X sans émulation ou couche de traduction supplémentaire, même sur le même matériel.

Les premiers systèmes d'exploitation de type DOS pouvaient souvent partager des programmes, car ils implémentaient la même API dans le matériel (BIOS) et le système d'exploitation connecté au matériel pour fournir des services. Ainsi, si vous avez écrit et compilé un programme COM (qui n'est qu'une image mémoire d'une série d'instructions de processeur), vous pouvez l'exécuter sur CP / M, MS-DOS et plusieurs autres systèmes d'exploitation. En fait, vous pouvez toujours exécuter des programmes COM sur des machines Windows modernes. D'autres systèmes d'exploitation n'utilisent pas les mêmes points d'ancrage de l'API du BIOS. Par conséquent, les programmes COM ne s'exécutent pas sur eux sans, encore une fois, une couche d'émulation ou de traduction. Les programmes EXE suivent une structure qui comprend beaucoup plus que de simples instructions de processeur. Par conséquent, les problèmes d’API ne s’exécutent pas sur une machine qui ne comprend pas comment le charger en mémoire et l’exécuter.

Adam Davis
la source
7

En fait, la vraie réponse est que si tous les OS ne comprend la même mise en page de fichier binaire exécutable, et vous ne vous limite aux fonctions normalisées (comme dans la bibliothèque standard C) que le système d' exploitation fourni (quels systèmes d' exploitation ne fournissent), votre logiciel serait , en fait, sur n'importe quel OS.

Bien sûr, la réalité est que ce n'est pas le cas. Un EXEfichier n'a pas le même format qu'un ELFfichier, même si les deux contiennent du code binaire pour le même processeur. * Chaque système d'exploitation doit donc pouvoir interpréter tous les formats de fichier, ce qu'ils n'ont tout simplement pas fait dans la début, et il n'y avait aucune raison pour qu'ils le fassent plus tard (presque certainement pour des raisons commerciales plutôt que techniques).

De plus, votre programme doit probablement faire des choses que la bibliothèque C ne définit pas (même pour des choses simples comme lister le contenu d’un répertoire), et dans ce cas, chaque système d’exploitation fournit ses propres fonctions permettant d’atteindre votre objectif. tâche, ce qui signifie naturellement qu’il n’y aura pas de plus petit dénominateur commun à utiliser (à moins que vous ne définissiez ce dénominateur vous-même).

Donc, en principe, c'est parfaitement possible. En fait, WINE exécute les exécutables Windows directement sur Linux.
Mais c'est une tonne de travail et (généralement) injustifiée sur le plan commercial.

* Remarque: Un fichier exécutable est bien plus qu'un simple code binaire. Une tonne d'informations indique au système d'exploitation les bibliothèques dont dépend le fichier, la quantité de mémoire de pile dont il a besoin, les fonctions qu'il exporte vers d' autres bibliothèques qui peuvent en dépendre, le système d'exploitation peut trouver des informations de débogage pertinentes, " re-localiser » le fichier dans la mémoire si nécessaire, comment faire correctement le travail de gestion des exceptions, etc. , etc .... à nouveau, il pourrait être un format unique pour ce que tout le monde est d' accord, mais il n'y a tout simplement pas.

Mehrdad
la source
Anecdote: il existe un format binaire posiz standardisé, utilisable sur plusieurs systèmes d'exploitation. Ce n'est tout simplement pas couramment utilisé.
Marcin
@Marcin: On dirait que vous ne considérez pas Windows comme un système d'exploitation. (Ou êtes-vous en train de dire que Windows peut exécuter les fichiers binaires POSIX?!) Pour les besoins de ma réponse, POSIX n'est pas le type de standard auquel je fais référence. Le X dans POSIX signifie Unix. Par exemple, Windows n’a jamais été destiné à l’utiliser, même si Windows possède un sous-système POSIX.
Mehrdad
1. Quelque chose peut fonctionner sur plusieurs systèmes d'exploitation sans utiliser tous les systèmes d'exploitation; 2. Windows depuis NT a été capable d’exécuter des binaires posix.
Marcin
1
@Marcin: (1) Comme je l'ai dit, le X dans POSIX signifie UNIX . Ce n'est pas une norme qui devait être suivie par d'autres systèmes d'exploitation, c'était simplement une tentative d'atteindre un dénominateur commun entre les différents Unix, ce qui est génial mais pas étonnant. Le fait qu'il existe plusieurs types de systèmes d'exploitation Unix est totalement hors de propos, ce que j'essayais de faire valoir en ce qui concerne la compatibilité entre systèmes d'exploitation autres qu'Unix. (2) Pouvez-vous fournir une référence pour # 2?
Mehrdad
1
@ Mehrdad: Marcin a raison; Windows SUA (sous-système pour les applications Unix) est compatible POSIX
MSalters
5

Le diagramme comporte la couche "application" (la plupart du temps) séparée de la couche "système d'exploitation" par les "bibliothèques", ce qui implique que "application" et "système d'exploitation" n'ont pas besoin de se connaître. C'est une simplification dans le diagramme, mais ce n'est pas tout à fait vrai.

Le problème est que la "bibliothèque" a en réalité trois parties: l'implémentation, l'interface vers l'application et l'interface vers le système d'exploitation. En principe, les deux premiers peuvent être rendus "universels" en ce qui concerne le système d'exploitation (cela dépend de l'endroit où vous le découpez), mais la troisième partie - l'interface avec le système d'exploitation - ne le peut généralement pas. L’interface avec le système d’exploitation dépendra nécessairement du système d’exploitation, des API qu’il fournit, du mécanisme de conditionnement (par exemple, le format de fichier utilisé par la DLL Windows), etc.

Étant donné que la "bibliothèque" est généralement mise à disposition sous forme de package unique, cela signifie qu'une fois que le programme sélectionne une "bibliothèque" à utiliser, il s'engage dans un système d'exploitation spécifique. Cela se produit de deux manières: a) le programmeur choisit complètement à l'avance, puis la liaison entre la bibliothèque et l'application peut être universelle, mais la bibliothèque elle-même est liée au système d'exploitation; ou b) le programmeur configure les choses de manière à ce que la bibliothèque soit sélectionnée lors de l'exécution du programme, mais le mécanisme de liaison lui - même, entre le programme et la bibliothèque, dépend du système d'exploitation (par exemple, le mécanisme DLL sous Windows). Chacune a ses avantages et ses inconvénients, mais de toute façon, vous devez faire un choix à l’avance.

Cela ne veut pas dire que c'est impossible, mais il faut être très intelligent. Pour résoudre le problème, vous devez choisir la bibliothèque au moment de l'exécution et vous devez créer un mécanisme de liaison universel qui ne dépend pas du système d'exploitation (vous devez donc le maintenir, beaucoup plus de travail). Parfois, ça vaut le coup.

Vous n'avez pas à le faire, mais si vous allez faire l'effort de le faire, il y a de bonnes chances que vous ne vouliez pas être lié à un processeur spécifique non plus, alors vous allez écrire une machine virtuelle et vous allez compiler votre programme au format de code neutre du processeur.

A présent, vous devriez avoir remarqué où je vais. Les plateformes de langage comme Java font exactement cela. Le runtime Java (bibliothèque) définit la liaison indépendante du système d'exploitation entre votre programme Java et la bibliothèque (comment le runtime Java ouvre et exécute votre programme) et fournit une implémentation spécifique au système d'exploitation actuel. .NET fait la même chose dans une certaine mesure, sauf que Microsoft ne fournit pas de "bibliothèque" (runtime) pour autre chose que Windows (mais les autres le font - voir Mono). Et, en réalité, Flash fait la même chose, bien que sa portée soit plus limitée que celle du navigateur.

Enfin, il existe des moyens de faire la même chose sans mécanisme de liaison personnalisé. Vous pouvez utiliser des outils classiques, mais reporter l'étape de liaison à la bibliothèque jusqu'à ce que l'utilisateur sélectionne le système d'exploitation. C'est exactement ce qui se passe lorsque vous distribuez le code source. L'utilisateur prend votre programme et le lie au processeur (le compile) et au système d'exploitation (le lie) lorsqu'il est prêt à l'exécuter.

Tout dépend de la manière dont vous coupez les calques. À la fin de la journée, vous avez toujours un périphérique informatique fabriqué avec un matériel spécifique exécutant un code machine spécifique. Les couches constituent principalement un cadre conceptuel.

Euro Micelli
la source
3

Le logiciel n'est pas toujours spécifique au système d'exploitation. Les deux Java et version antérieure du système p-code (et même ScummVM) permettent un logiciel qui est portable sur les systèmes d' exploitation. Infocom (fabricants de Zork et de Z-machine ) disposait également d'une base de données relationnelle basée sur une autre machine virtuelle. Cependant, à un certain niveau, quelque chose doit traduire même ces abstractions en instructions à exécuter sur un ordinateur.

Elliott Frisch
la source
3
Java s'exécute sur une machine virtuelle, ce qui n'est pas un système d'exploitation croisé. Vous devez utiliser un binaire JVM différent pour chaque système d'exploitation
Izkata
3
@ Izkata True, mais vous ne recompilez pas le logiciel (uniquement la machine virtuelle Java). Voir aussi ma dernière phrase. Mais je ferai remarquer que Sun disposait d'un microprocesseur capable d'exécuter directement le code octet.
Elliott Frisch
3
Java est un système d'exploitation, bien qu'il ne soit généralement pas considéré comme tel. Le logiciel Java est spécifique au système d'exploitation Java et il existe des émulateurs Java pour la plupart des systèmes d'exploitation "réels". Mais vous pouvez faire la même chose avec n'importe quel hôte et système d'exploitation cible, comme exécuter un logiciel Windows sous Linux avec WINE.
user253751
@immibis je serais plus précis. Java Foundation Classes (JFC, la bibliothèque standard de Java) est un framework. Java lui-même est un langage. La JVM ressemble à un système d'exploitation: elle porte le nom "machine virtuelle" et remplit des fonctions similaires à celles d'un système d'exploitation du point de vue du code qui y est exécuté.
1

Vous dites

les logiciels produits à l'aide de langages de programmation pour certains systèmes d'exploitation ne fonctionnent qu'avec eux

Mais le programme que vous donnez à titre d'exemple fonctionnera sous de nombreux systèmes d'exploitation, et même dans certains environnements sans système d'exploitation.

La chose importante ici est la distinction entre le code source et le binaire compilé. Le langage de programmation C est spécifiquement conçu pour être indépendant du système d’exploitation sous forme de source. Pour ce faire, il laisse l'interprétation de choses comme "imprimer sur la console" jusqu'à l'implémenteur. Mais C peut être conforme à quelque chose qui est spécifique à l'OS (voir les autres réponses pour les raisons). Par exemple, les formats exécutables PE ou ELF.

Dan
la source
6
Il semble bien évident que l'OP pose des questions sur les fichiers binaires et non sur le code source.
Caleb
0

D'autres personnes ont bien couvert les détails techniques, je voudrais mentionner une raison moins technique, le côté UX / UI:

Ecrivez une fois, sentez-vous maladroit partout

Chaque système d'exploitation possède ses propres API d'interface utilisateur et normes de conception. Il est possible d'écrire une interface utilisateur pour un programme et de le faire fonctionner sur plusieurs systèmes d'exploitation. Cependant, cela garantit presque que le programme ne se sentira pas à sa place partout. Faire une bonne interface utilisateur nécessite de peaufiner les détails pour chaque plate-forme prise en charge.

Beaucoup d’entre eux sont de petits détails, mais ne vous y trompez pas et vous allez frustrer vos utilisateurs:

  • Les boîtes de dialogue de confirmation ont leurs boutons dans un ordre différent sous Windows et OSX; comprenez-le mal et les utilisateurs cliqueront sur le mauvais bouton avec la mémoire musculaire. Windows a "Ok", "Annuler" dans cet ordre. La commande est permutée sur OSX et le texte du bouton Do-it est une courte description de l'action à exécuter: "Annuler", "Déplacer vers la corbeille".
  • Le comportement de "retour" est différent pour iOS et Android. Les applications iOS dessinent leur propre bouton de retour selon les besoins, généralement en haut à gauche. Android a un bouton dédié en bas à gauche ou en bas à droite en fonction de la rotation de l'écran. Les ports rapides vers Android se comporteront de manière incorrecte si le bouton Précédent du système d'exploitation est ignoré.
  • Le défilement Momentum est différent entre iOS, OSX et Android. Malheureusement, si vous n'écrivez pas de code d'interface utilisateur natif, vous devrez probablement écrire votre propre comportement de défilement.

Même s'il est techniquement possible d'écrire une base de code d'interface utilisateur qui fonctionne partout, il est préférable d'effectuer des réglages pour chaque système d'exploitation pris en charge.

Nick Pinney
la source
-2

Une distinction importante à ce stade consiste à séparer le compilateur de l'éditeur de liens. Il est probable que le compilateur produise plus ou moins le même résultat (les différences sont principalement dues à divers #if WINDOWSs). L'éditeur de liens, quant à lui, doit gérer tout ce qui est spécifique à la plate-forme - relier les bibliothèques, créer le fichier exécutable, etc.

En d'autres termes, le compilateur s'intéresse principalement à l'architecture du processeur, car il produit le code exécutable réel et doit utiliser les instructions et les ressources du processeur (notez que le bytecode de l'IL ou de la JVM de .NET serait considéré comme un jeu d'instructions d'un processeur virtuel. dans cette vue). C'est pourquoi vous devez compiler le code séparément pour x86et ARM, par exemple.

L'éditeur de liens, d'un autre côté, doit prendre toutes ces données brutes et ces instructions et les mettre dans un format que le chargeur (de nos jours, ce serait presque toujours le système d'exploitation) peut comprendre, ainsi que lier toutes les bibliothèques liées de manière statique. (qui inclut également le code requis pour la liaison dynamique, l’allocation de mémoire, etc.).

En d'autres termes, vous pourriez être en mesure de compiler le code une seule fois et de le faire fonctionner à la fois sous Linux et Windows - mais vous devez le lier deux fois, en produisant deux exécutables différents. Maintenant, dans la pratique, vous devez aussi souvent tenir compte du code (c'est là que les directives du (pré) compilateur entrent), de sorte que même la compilation d'un lien à deux fois n'est pas très utilisée. Sans compter que les gens traitent la compilation et la liaison en une seule étape lors de la construction (tout comme vous ne vous souciez plus des parties du compilateur lui-même).

Les logiciels de l'époque DOS étaient souvent plus portables-binaires, mais vous devez comprendre qu'ils ont également été compilés non pas pour DOS ou Unix, mais pour un certain contrat commun à la plupart des PC de style IBM - décharger ce que sont aujourd'hui les appels d'API à interruptions logicielles. Cela ne nécessitait pas de liaison statique, car il suffisait de définir les registres nécessaires, par exemple int 13hpour appeler des fonctions graphiques, et la CPU venait de sauter à un pointeur de mémoire déclaré dans la table d'interruption. Bien sûr, encore une fois, la pratique était beaucoup plus délicate, car pour obtenir des performances allant du pédalier au métal, il fallait écrire toutes ces méthodes vous-même, mais cela revenait à contourner le système d'exploitation. Et bien sûr, quelque chose nécessite invariablement une interaction avec l'API du système d'exploitation: la terminaison du programme. Néanmoins, si vous utilisiez les formats les plus simples disponibles (par exemple,COMsous DOS, qui n’a pas d’en-tête, juste des instructions) et ne veut pas sortir, eh bien, chanceux! Et bien sûr, vous pouvez également gérer la terminaison appropriée dans le runtime, de sorte que vous puissiez avoir le code pour la terminaison Unix et la terminaison DOS dans le même exécutable, et détecter au moment de l'exécution lequel utiliser :)

Luaan
la source
cela semble répéter simplement des points expliqué dans ce et ce réponses avant qui ont été publiés hier
moucheron