Comment les systèmes d'exploitation… fonctionnent-ils… sans système d'exploitation?

167

Je suis vraiment curieux en ce moment. Je suis un programmeur Python, et cette question m’a complètement échappé: vous écrivez un système d’exploitation. Comment le dirigez-vous? Il doit être exécuté d'une manière ou d'une autre, et cette manière est dans un autre OS?

Comment une application peut-elle s'exécuter sans être dans un système d'exploitation? Comment pouvez-vous dire à l'ordinateur d'exécuter, par exemple, C, et d'exécuter ces commandes à l'écran, s'il ne dispose pas d'un système d'exploitation à exécuter?

Est-ce que cela a à voir avec un noyau UNIX? Si oui, qu'est-ce qu'un noyau Unix, ou un noyau en général?

Je suis sûr que les OS sont plus compliqués que ça, mais comment ça marche?

Thor Correia
la source
14
Je suis à peu près sûr que c'est à quoi sert un BIOS - c'est un très petit système d'exploitation qui démarre le système d'exploitation plus grand.
Sevenseacat
64
Un système d'exploitation est pratique , mais vous n'en avez pas besoin pour exécuter des programmes sur un ordinateur.
Andres F.
10
Il est parfaitement possible d'écrire un logiciel sans système d'exploitation sans système d'exploitation. Beaucoup d'interprètes de Forth travaillaient traditionnellement sans système d'exploitation (ou on pourrait dire qu'ils étaient des systèmes d'exploitation). Ce n'est même pas si difficile. Si vous connaissez C, vous pourriez aimer écrire un tel programme (un petit jeu, peut-être) comme exercice d'apprentissage.
Max
44
Cette confusion est l’un des coûts des systèmes informatiques merveilleux, sûrs et très abstraits que nous utilisons de nos jours: les gens peuvent être de très bons programmeurs et des programmeurs compétents sans même connaître les bases du fonctionnement de l’ordinateur. À quel point voulez-vous aller? Pour des résultats très bas, mais toujours au-dessus de la physique, voir Comment les premiers microprocesseurs ont-ils été programmés? sur Electronics.SE.
dmckee
2
La programmation a été réalisée avant l'invention du concept OS actuel. De toute évidence, quelque chose à ce niveau est ce qui lance le système d'exploitation. Les systèmes d'exploitation sont démarrés. Cela est généralement mentionné au moins dans un programme CS 4 ans à un moment donné, car la plupart nécessitent un cours de théorie informatique des systèmes d'exploitation.
Rig

Réponses:

263

De nombreux sites Web passent par le processus de démarrage (tel que Comment les ordinateurs démarrent- ils ). En un mot, il s’agit d’un processus en plusieurs étapes qui construit progressivement le système jusqu’à ce qu’il puisse enfin démarrer les processus du système d’exploitation.

Cela commence par le firmware de la carte mère qui tente de mettre le processeur en marche. Il charge ensuite le BIOS, qui est comme un mini système d’exploitation qui permet à l’autre matériel de fonctionner correctement. Une fois que cela est fait, il cherche un périphérique de démarrage (disque, CD, etc.) et, une fois trouvé, il localise le MBR (Master Boot Record), le charge en mémoire et l'exécute. C'est ce petit morceau de code qui sait ensuite comment initialiser et démarrer le système d'exploitation (ou d'autres chargeurs de démarrage au fur et à mesure que les choses se compliquent). C'est à ce stade que des éléments comme le noyau seraient chargés et commenceraient à s'exécuter.

C'est assez incroyable que ça marche du tout!

Facture
la source
108
+1 pour la dernière phrase.
un CVn
39
Il y a une raison pour que ça s'appelle "booting"; le terme est l'abréviation de "bootstrapping", comme dans "se tirer par vos bootstraps".
KeithS
5
Il fut un temps où quelqu'un devait saisir ou basculer dans le code de bootstrap. Parfois, il s'agissait d'un simple saut à la première instruction d'un programme en ROM. D'autres fois, il s'agissait d'un code à lire à partir d'un périphérique et à sauter à la première instruction de programme dans les données lues. Les choses sont beaucoup plus simples maintenant.
BillThor
15
@ BillThor: Où par "beaucoup plus simple" vous entendez bien sûr "beaucoup plus compliqué". Ils sont simplement plus simples à utiliser .
Raphael Schweikert
173

Un système d'exploitation "nu-metal" ne fonctionne dans rien. Il exécute le jeu d'instructions complet sur la machine physique et a accès à l'ensemble de la mémoire physique, à tous les registres de périphérique et à toutes les instructions privilégiées, y compris celles contrôlant le matériel de prise en charge de la mémoire virtuelle.

(Si le système d'exploitation s'exécute sur une machine virtuelle, il peut penser qu'il se trouve dans la même situation que celle décrite ci-dessus. La différence est que certaines choses sont émulées ou gérées d'une autre manière par l'hyperviseur, c'est-à-dire le niveau d'exécution des machines virtuelles. .)

Quoi qu’il en soit, bien que le système d’exploitation puisse être implémenté en C, par exemple, il ne disposera pas de toutes les bibliothèques C normales. En particulier, il n'aura pas les bibliothèques normales 'stdio'. Au lieu de cela, il implémentera (par exemple) un pilote de périphérique de disque lui permettant de lire et d'écrire des blocs de disque. Il implémentera un système de fichiers au-dessus de la couche de bloc de disque et implémentera les appels système que les bibliothèques d'exécution d'une application utilisateur lancent (par exemple) pour créer, lire et écrire des fichiers ... et ainsi de suite.

Comment une application peut-elle s'exécuter sans être dans un système d'exploitation?

Il doit s'agir d'un type d'application particulier (par exemple, un système d'exploitation) capable d'interagir directement avec le matériel d'E / S, etc.

Comment pouvez-vous dire à l'ordinateur d'exécuter, par exemple, C, et d'exécuter ces commandes à l'écran, s'il ne dispose pas d'un système d'exploitation à exécuter?

Vous ne.

L'application (qui servait d'argument écrit en C) est compilée et liée sur un autre ordinateur pour donner une image en code natif. L'image est ensuite écrite sur le disque dur à un endroit où le BIOS peut la trouver. Le BIOS charge l’image en mémoire et exécute une instruction pour accéder au point d’entrée de l’application.

Il n'y a (généralement) pas de "C en cours d'exécution et d'exécution de commandes" dans l'application, sauf s'il s'agit d'un système d'exploitation complet. Et dans ce cas, il incombe au système d'exploitation de mettre en œuvre toute l'infrastructure requise pour y parvenir. Pas de magie. Juste beaucoup de code.

La réponse de Bill couvre l' amorçage qui est le processus dans lequel vous passez d'un ordinateur éteint à un ordinateur sur lequel le système d'exploitation normal est opérationnel. Cependant, il convient de noter que lorsque le BIOS termine ses tâches, il (généralement) donne le contrôle total du matériel au système d'exploitation principal et ne joue aucun autre rôle - jusqu'au prochain redémarrage du système. Le système d'exploitation principal ne fonctionne certainement pas "au sein" du BIOS au sens conventionnel.

Est-ce que cela a à voir avec un noyau UNIX? Si c'est le cas, qu'est-ce qu'un noyau Unix ou un noyau en général?

Oui.

Le noyau UNIX est le noyau du système d'exploitation UNIX. C’est la partie d’UNIX qui exécute tout le «métal nu» décrit ci-dessus.

L'idée d'un "noyau" est que vous essayez de séparer le logiciel système en éléments essentiels (nécessitant un accès à un périphérique physique, toute la mémoire, etc.) et des éléments non essentiels. Le noyau est constitué du noyau.

En réalité, la distinction entre noyau / noyau et non-noyau / non-cœur est plus compliquée que cela. Et il y a eu beaucoup de débats sur ce qui appartient vraiment au noyau et ce qui ne l’est pas. (Recherchez le micro-noyau par exemple.)

Stephen C
la source
6
Réponse phénoménale. Je vous donnerais plusieurs autres votes si possible.
Weberc2
7
Beaucoup de bonnes réponses ici, donc j'ajouterai ceci sous forme de commentaire, puisque personne ne l'a mentionné jusqu'à présent: l'une des fonctionnalités clés d'un système d'exploitation permet à plusieurs applications de s'exécuter "simultanément" du point de vue de l'utilisateur. La possibilité de planifier des processus et d'empêcher ceux-ci de modifier leur comportement est généralement une fonctionnalité que l'on ne trouve que dans un système d'exploitation et non dans un microprogramme ou dans le BIOS.
Sean Barbeau
2
The idea of a "kernel" is that you try to separate the system software into core stuffFacile à retenir en notant que le terme kernelvient de l'allemand Kern, qui signifie noyau / noyau.
deed02392
1
J'adore cette réponse ici car elle indique qu'il s'agit d'un code binaire compilé et lié ne fonctionnant pas C.
Travis Pessetto
3
"S'éloigner de cela a rendu les utilisateurs de PC moins intelligents." - Pas moins intelligent ... moins expert en informatique. Vous pouvez également dire que cela a augmenté le nombre d'utilisateurs de PC.
Stephen C
62

Au début, il n'y avait pas de courant dans la CPU.

Et l'homme a dit "qu'il y ait du courant", et la CPU a commencé à lire une adresse donnée en mémoire et à exécuter l'instruction qui y était présente. Puis le suivant et ainsi de suite jusqu'à la fin du pouvoir.

C'était le démarrage. Sa tâche consistait à charger un autre logiciel pour accéder à l'environnement où se trouvait le logiciel principal et à le charger.

Enfin, un écran convivial vous invite à vous connecter.

mouviciel
la source
58
Cette réponse devrait être déplacée vers christianity.stackexchange.com
Coomie
6
quelle est "une adresse donnée" d'où ça vient. Désolé pour jouer Charles Darwin ici.
Midhat
7
@Midhat - La première adresse à récupérer par la CPU est câblée à l'intérieur de celle-ci. D'habitude c'est 0.
mouviciel
17
... Et le 7ème jour, l'homme s'est reposé avec ses matchs
Canadien Luke
9
@mouviciel L'adresse en mémoire 0x7C00concerne toutes x86les architectures compatibles et doit d'abord être renseignée par le BIOS, qui charge généralement le premier secteur de tout périphérique amorçable qu'il préfère ... Bonne réponse cependant: -7
Tobias Kienzler
29

Désolé d'être en retard, mais je vais le décrire comme tel:

  • La carte mère est alimentée.

  • Les circuits de synchronisation démarrent et se stabilisent si nécessaire, en se basant uniquement sur leurs caractéristiques électriques. Certains appareils plus récents peuvent en fait utiliser un microprocesseur ou un séquenceur très limité.

    Il convient de noter que beaucoup de choses telles que "les circuits de synchronisation démarrent et se stabilisent si nécessaire" ne se produisent plus vraiment dans le matériel. Une part considérable de ce travail consiste en fait en un logiciel extrêmement spécialisé fonctionnant sur des sous-processeurs / séquenceurs très limités.

    - jkerian à 5h20 le 25 octobre

  • Le processeur et la RAM sont alimentés.

  • La CPU charge (en fonction de son câblage interne) les données du BIOS. Sur certaines machines, le BIOS peut être mis en miroir dans la RAM, puis exécuté à partir de là, mais cela est rare, l’IIRC.

    Lorsqu'ils sont activés, les CPU compatibles x86 démarrent à l'adresse 0xFFFFFFF0 dans l'espace d'adressage ...

    -Micheal Steil, 17 erreurs Microsoft commises dans le système de sécurité Xbox ( archive )

  • Le BIOS appelle les ports matériels et les adresses utilisées par la carte mère pour les E / S de disque et autres matériels. Il fait tourner les disques et fait fonctionner le reste de la RAM, entre autres.

  • Le code du BIOS (au moyen des paramètres CMOS, stockés dans le matériel) utilise des commandes IDE ou SATA de bas niveau pour lire le secteur de démarrage de chaque disque, dans un ordre spécifié par le CMOS ou un remplacement par un menu.

  • Le premier disque avec un secteur de démarrage fait exécuter son secteur de démarrage. Ce secteur de démarrage est l’Assemblée qui a pour instructions de charger plus de données à partir du disque, de charger une plus grande NTLDR, des étapes ultérieures GRUB, etc.

  • Enfin, le code machine du système d'exploitation est exécuté par le chargeur de démarrage, directement ou indirectement via le chargement en chaîne, chargeant un secteur de démarrage à partir d'un emplacement alternatif ou offset.

Vous obtenez alors une panique amicale du noyau, un pingouin étouffé ou votre disque s’arrête de bouger en raison d’un crash de la tête. =) Dans le scénario alternatif, votre noyau configure des tables de processus, des structures en mémoire et monte des disques, charge des pilotes, des modules et une interface graphique ou un ensemble de services (s’il se trouve sur un serveur). Ensuite, les programmes sont exécutés au fur et à mesure que leurs en-têtes sont lus, et leur assemblage est mis en mémoire et mappé en conséquence.

ζ--
la source
2
Il convient de noter que beaucoup de choses telles que "les circuits de synchronisation démarrent et se stabilisent si nécessaire" ne se produisent plus vraiment dans le matériel. Une quantité énorme de ce travail consiste en fait en des logiciels extrêmement spécialisés fonctionnant sur des sous-processeurs / séquenceurs très limités. - Un ingénieur sympathique du micrologiciel du voisinage
jkerian
@jkerian Cela vous dérange-t-il que j'ai cité votre commentaire dans mon message?
ζ--
heh, pas du tout.
jeudi
Le BIOS n'est pas un système d'exploitation. Le BIOS est un raccourci pour Basic Input / Output System, et c'est ce que fait le BIOS. Cela permet aux programmeurs d'utiliser des ressources de bas niveau avec les pilotes fournis par le fabricant. Lorsque le système d'exploitation entre en mode protégé (32 bits) ou long (64 bits), le BIOS n'est plus disponible et le système d'exploitation utilise ses propres pilotes qui remplacent essentiellement les fonctionnalités fournies par le BIOS à des niveaux "inférieurs". Les systèmes d'exploitation modernes, tels que Linux et Windows, utilisent le BIOS uniquement pour détecter les sections de RAM utilisables et pour charger leur propre chargeur plus évolué capable de charger les pilotes requis.
Hannes Karppila
1
@HannesKarppila Mis à jour; cela fait environ quatre ans maintenant et je ne suis plus actif sur ce site.
ζ--
15

Il y a beaucoup de bonnes réponses mais je voulais ajouter ceci: vous avez mentionné que vous venez d'un contexte Python. Python est un langage ninterpreted (ou "interpiled" ou autre, du moins dans les cas d'utilisation typiques de CPython). Cela signifie que d'autres logiciels (l'interpréteur Python) examinent le code source et l'exécutent d'une manière ou d'une autre. C’est un bon modèle qui permet de jolis langages de haut niveau bien abstraits du matériel actuel. L'inconvénient est que vous avez toujours besoin de ce logiciel d'interprétation en premier.

Un tel logiciel d'interprétation est généralement écrit dans un langage qui compile en code machine, par exemple C ou C ++. Le code machine est ce que le processeur peut gérer. Ce qu'un processeur peut faire, c'est lire quelques octets dans la mémoire et, en fonction des valeurs d'octet, lancer une opération spécifique. Donc une séquence d'octets est une commande pour charger des données de la mémoire dans un registre, une autre séquence pour ajouter deux valeurs, une autre pour stocker la valeur d'un registre dans la mémoire principale et bientôt (un registre est une zone de mémoire spéciale qui fait partie du processeur où cela peut fonctionner le mieux), la plupart de ces commandes sont assez faibles à ce niveau. Le code assembleur est lisible par l'homme pour ces instructions de code machine. Ce code machine correspond en gros à ce qui est stocké dans les fichiers .exe ou.com sous Windows ou à l’intérieur des fichiers binaires Linux / Unix.

Maintenant, si un ordinateur est démarré, il est stupide, mais il possède un câblage qui lira de telles instructions de code machine. Sur un PC, il s’agit généralement (actuellement) d’une puce EEPROM sur la carte mère contenant le BIOS (système d’inputs de base), ce système ne peut pas faire grand chose, il peut faciliter l’accès à certains matériels, puis effectuer une opération de clé: démarrez et copiez les premiers octets (c’est-à-dire l’enregistrement de démarrage principal, MBR) dans la mémoire, puis indiquez à la CPU "ici, voici votre programme", la CPU traitera alors ces octets en tant que code machine et l’exécutera. Il s’agit généralement d’un chargeur de système d’exploitation chargé de charger certains paramètres dans le noyau, puis de céder le contrôle à ce noyau, qui charge ensuite tous ses drivers pour accéder à tout le matériel, charger un programme de bureau ou shell ou autre et permettre à l’utilisateur de se connecter utiliser le système.

johannes
la source
6
"interpilé"? Je n'ai jamais entendu ce terme auparavant.
Bryan Oakley
3
Ce terme a été utilisé il y a environ 5 ans pour décrire les interprètes "modernes" dont la phase de compilation est distincte de l'exécution. Aucune idée si ce terme a survécu n'importe où ;-)
johannes
1
"ninterpreted"? Je n'ai jamais entendu ce terme auparavant.
Cole Johnson
12

Vous demandez "Comment une application peut-elle s'exécuter sans être dans un système d'exploitation". La réponse facile est "un OS n'est pas une application". Bien qu'un système d'exploitation puisse être créé avec les mêmes outils qu'une application et fabriqué à partir de la même matière première, ils ne sont pas la même chose. Un système d'exploitation ne doit pas nécessairement respecter les mêmes règles qu'une application.

OTOH, vous pouvez considérer le matériel et le micrologiciel réels comme le "système d'exploitation" dans lequel "l'application" du système d'exploitation s'exécute. Le matériel est un système d’exploitation très simple: il sait comment exécuter les instructions écrites dans le code machine et sait qu’au démarrage, il doit rechercher une adresse mémoire très spécifique pour sa première instruction. Ainsi, il démarre et exécute immédiatement cette toute première instruction, suivie de la seconde, etc.

Ainsi, le système d'exploitation est simplement un code machine qui existe à un emplacement connu et qui peut interagir directement avec le matériel.

Bryan Oakley
la source
1
+1 Je pense que c'est la meilleure réponse. En ce qui concerne les abstractions, je pense que vous êtes en train de le fixer aux bons niveaux.
Preet Sangha
6

Pour répondre à votre question, vous devez connaître l’aspect du code natif (pour le processeur) et son interprétation par celui-ci.

En général, le processus de compilation consiste à traduire les éléments que vous écrivez en C, Pascal ou même en Python (avec pypy) et C # en éléments compris par le CPU, c.-à-d. et ebx "," call function foo "," comparez eax à 10 ". Ces instructions, exécutées une à une, font ce que vous vouliez faire avec votre code.

Pensez maintenant à ceci: vous n'avez pas vraiment besoin d'un système d'exploitation pour exécuter ce code natif! Tout ce dont vous avez besoin est de charger ce code en mémoire et d'indiquer au processeur qu'il est là et que vous voulez qu'il soit exécuté. Ne vous inquiétez pas trop de cela, cependant. C’est le travail qui devrait préoccuper le BIOS: il charge votre code (un et un secteur seulement), juste après le démarrage du processeur, sous l’adresse physique 0x7C00. Ensuite, la CPU commence à exécuter ce secteur (512 B) de votre code. Et vous pouvez faire ce que vous imaginez! Sans, bien sûr, aucun support de l'OS. C'est parce que VOUS êtes le système d'exploitation. Cool hein? Pas de bibliothèque standard, pas de boost, pas de python, pas de programmes, pas de pilotes! Vous devez tout écrire par vous-même.

Et comment communiquez-vous avec le matériel? Eh bien, vous avez deux choix:

  1. Vous restez à l'intérieur du "Mode réel" - mode d'exécution du processeur avec seulement 1 Mo de mémoire (et même moins), pas de fonctionnalités avancées du processeur comme les extensions de processeur, la protection de la mémoire, le multitâche; Code exécutable 16 bits, mode d'adressage ancien ... Mais avec certaines routines fournies par le BIOS, notamment la sortie d'écran simple, la prise en charge du clavier, les E / S de disque et la gestion de l'alimentation. En un mot, vous êtes de retour à l’époque des processeurs MS-DOS et 16 bits.
  2. Vous allez dans "Mode protégé" avec toutes les fonctionnalités de votre CPU, toute la mémoire que vous avez installée et ainsi de suite. Mais en mode protégé, vous ÊTES complètement seul et vous devez tout faire par vous-même (et vous communiquez avec le matériel en utilisant des instructions "in" et "out" pour entrer / sortir des données sur des ports d'E / S et en utilisant des interruptions. Et la mémoire mappée I / O). Dois-je dire chaque système d'exploitation depuis Windows 95 et la toute première Linux choisit cette option?

Maintenant vous demandez ce qu'est le noyau. En bref, le noyau est tout ce que vous ne voyez pas et ne faites pas l'expérience directe. Il gère, avec les pilotes, tout, de votre clavier à presque tous les éléments matériels de votre PC. Vous communiquez avec lui par shell graphique ou terminal. Ou par des fonctions dans votre code, maintenant exécutées, heureusement, avec le support du système d'exploitation.

Pour une meilleure compréhension, je peux vous donner un conseil: essayez d’écrire votre propre système d’exploitation. Même si ça va écrire "Hello world" sur l'écran.

Mateusz K.
la source
3

Le fonctionnement d’un système d’exploitation dépend énormément de certaines différences. Pour être utile, un système doit avoir un comportement prévisible au démarrage, tel que "démarrer l'exécution à l'adresse X". Pour les systèmes dont la mémoire non volatile (telle que la mémoire flash) est mappée dans leur espace programme, cela est assez facile car vous devez simplement vous assurer que le code de démarrage est bien placé dans l’espace programme du processeur. Ceci est extrêmement courant pour les microcontrôleurs. Certains systèmes doivent extraire leurs programmes de démarrage d'un autre emplacement avant de les exécuter. Certaines opérations seront câblées (ou presque) dans ces systèmes. Certains processeurs récupèrent leur code de démarrage via i2c à partir d’une autre puce,

Les systèmes utilisant la famille de processeurs x86 utilisent généralement un processus de démarrage en plusieurs étapes assez complexe en raison de son évolution et de problèmes de compatibilité ascendante. Le système exécute un micrologiciel (appelé BIOS - Système d’entrée / sortie de base ou similaire) qui se trouve dans une mémoire non volatile de la carte mère. Parfois, tout ou partie de ce microprogramme est copié (déplacé) dans la RAM pour le rendre plus rapide à exécuter. Ce code a été écrit en sachant quel matériel serait présent et utilisable pour le démarrage.

Le microprogramme de démarrage est généralement écrit avec des hypothèses sur le matériel qui sera présent sur le système. Il y a des années, sur une machine 286, on pourrait probablement supposer qu'il y aurait un contrôleur de lecteur de disquette à l'adresse d'E / S X et que le secteur 0 serait chargé dans un certain emplacement de mémoire s'il recevait un certain jeu de commandes (et le code du secteur 0). sait comment utiliser les propres fonctions du BIOS pour charger plus de code, et finalement assez de code pour être un système d'exploitation est chargé). Sur un microcontrôleur, on peut supposer qu’un port série fonctionnant avec certains paramètres doit attendre des commandes (pour mettre à jour un microprogramme plus complexe) pendant une durée X avant de poursuivre le processus de démarrage.

Le processus de démarrage exact d’un système donné n’est pas aussi important pour vous que de savoir qu’il diffère d’un système à l’autre, mais aussi qu’ils ont tous des choses en commun. Souvent, dans le code de démarrage (démarrage), lorsqu'une opération d'E / S doit être effectuée, les périphériques d'E / S sont interrogés au lieu de compter sur des interruptions. En effet, les interruptions sont complexes, utilisez la RAM de pile (qui peut ne pas être entièrement configurée pour le moment) et vous n'avez pas à craindre de bloquer d'autres opérations lorsque vous êtes la seule.

Lors du premier chargement, le noyau du système d'exploitation (le noyau est la partie principale de la plupart des systèmes d'exploitation) agira initialement beaucoup comme le microprogramme. Il devra soit être programmé en fonction de la connaissance du matériel présent, soit configurer celle-ci, configurer une certaine RAM en tant que pile, effectuer divers tests, configurer diverses structures de données, éventuellement détecter et monter un système de fichiers, puis lancer probablement un programme plus performant. comme les programmes que vous avez l'habitude d'écrire (un programme qui repose sur la présence d'un système d'exploitation).

Le code OS est généralement écrit dans un mélange de C et d’assemblage. Le tout premier code pour le noyau du système d'exploitation est probablement toujours en cours d'assemblage et effectue des tâches telles que la configuration de la pile, sur laquelle le code C s'appuie, puis appelle une fonction C. D'autres assemblages écrits à la main y figureront également, car certaines opérations qu'un système d'exploitation doit effectuer ne sont souvent pas exprimables en C (comme le changement de contexte / l'échange de piles). Des indicateurs spéciaux doivent souvent être transmis au compilateur C pour lui dire de ne pas s'appuyer sur les bibliothèques standard utilisées par la plupart des programmes C et de ne pas s'attendre à ce qu'il y ait uneint main(int argc, char *argv[])dans le programme. De plus, des options spéciales de l'éditeur de liens que la plupart des programmeurs d'applications n'utilisent jamais doivent être utilisées. Celles-ci peuvent donner l’impression que le programme du noyau sera chargé à une certaine adresse ou qu’il semblerait qu’il existe des variables externes à certains emplacements, même si ces variables n’ont jamais été déclarées dans un code C (cela est utile pour les entrées / sorties mappées en mémoire). autres emplacements de mémoire spéciaux).

Toute l'opération semble magique au début, mais une fois que vous avez bien compris et compris certaines de ses facettes, la magie devient simplement un ensemble de programmes nécessitant beaucoup plus de planification et de connaissances système à mettre en œuvre. Le débogage, cependant, prend de la magie.

nategoose
la source
3

Pour comprendre le fonctionnement des systèmes d'exploitation, il peut être utile de les diviser en deux catégories: celles qui fournissent simplement des services aux applications à la demande et celles qui utilisent des fonctionnalités matérielles dans le processeur pour empêcher les applications de faire ce qu'elles ne devraient pas. MS-DOS était du style précédent; toutes les versions de Windows depuis la version 3.0 ont été ce dernier style (du moins lorsqu’on utilise quelque chose de plus puissant qu'un 8086).

Le PC IBM original exécutant PC-DOS ou MS-DOS aurait été un exemple de l'ancien style de "système d'exploitation". Si une application souhaitait afficher un caractère à l'écran, il y aurait eu plusieurs façons de le faire. Il pourrait appeler la routine qui demanderait à MS-DOS de l'envoyer à la "sortie standard". Si tel était le cas, MS-DOS vérifierait si la sortie était redirigée et sinon, il appellerait une routine stockée dans la ROM (dans un ensemble de routines IBM appelé système d'entrée / sortie de base) qui afficherait un caractère position du curseur et déplacez le curseur ("write teletype"). Cette routine BIOS stockerait alors une paire d'octets quelque part dans la plage 0xB800: 0 à 0xB800: 3999; Le matériel de l'adaptateur graphique couleur récupère de manière répétée des paires d'octets dans cette plage, utiliser le premier octet de chaque paire pour sélectionner une forme de caractère et le second pour sélectionner les couleurs de premier plan et d’arrière-plan. Les octets sont extraits et traités en signaux rouge, vert et bleu, dans une séquence qui donne un affichage lisible du texte.

Les programmes du PC IBM peuvent afficher du texte à l’aide de la routine DOS "sortie standard", de la routine "Écriture télétype" du BIOS ou de l’enregistrement direct dans la mémoire. De nombreux programmes ayant besoin d'afficher beaucoup de texte ont rapidement opté pour cette dernière solution, qui pouvait être littéralement des centaines de fois plus rapide que les routines DOS. Ce n'était pas parce que les routines DOS et BIOS étaient exceptionnellement inefficaces; à moins que l’écran ne soit blanc, il ne peut être écrit qu’à certaines heures. La routine du BIOS pour sortir un caractère a été conçue pour pouvoir être appelée à tout moment. chaque requête devait donc recommencer à attendre le bon moment pour effectuer une opération d'écriture. En revanche, un code d'application qui savait ce qu'il devait faire pouvait s'organiser en fonction des possibilités disponibles pour écrire l'affichage.

Un point clé ici est que, si le DOS et le BIOS offraient un moyen d’afficher du texte sur l’affichage, il n’y avait rien de particulièrement "magique" à propos de telles capacités. Une application qui souhaitait écrire du texte sur l’affichage pouvait le faire tout aussi efficacement, du moins si le matériel d’affichage fonctionnait comme prévu (si l’utilisateur avait installé une carte graphique monochrome similaire au CGA mais avec sa mémoire de caractères) situé à 0xB000: 0000-0xB000: 3999), le BIOS produirait automatiquement les caractères à cet emplacement; une application programmée pour fonctionner avec le MDA ou le CGA pourrait également le faire, mais une application programmée uniquement pour le CGA serait totalement inutile sur le MDA).

Sur les nouveaux systèmes, les choses sont un peu différentes. Les processeurs ont divers modes de "privilège". Ils commencent dans le mode le plus privilégié, où le code est autorisé à faire tout ce qu'il veut. Ils peuvent ensuite basculer vers un mode restreint, dans lequel seules les plages de mémoire sélectionnées ou les fonctionnalités d'E / S sont disponibles. Le code ne peut pas passer directement du mode restreint au mode privilégié, mais le processeur a défini des points d’entrée en mode privilégié, et le code en mode restreint peut demander au processeur de commencer à exécuter du code à l’un de ces points en mode privilégié. De plus, il existe des points d'entrée en mode privilégié associés à un certain nombre d'opérations qui seraient interdits en mode restreint. Supposons, par exemple, que quelqu'un veuille exécuter plusieurs applications MS-DOS simultanément, chacune ayant son propre écran. Si les applications pouvaient écrire directement sur le contrôleur d'affichage à l'adresse 0xB800: 0, il n'y aurait aucun moyen d'empêcher une application d'écraser l'écran d'une autre application. D'autre part, un système d'exploitation peut exécuter l'application en mode restreint et intercepter tout accès à la mémoire d'affichage. si elle découvrait qu'une application supposée être dans le "fond" essayait d'écrire 0xB800: 160, elle pourrait stocker les données dans une mémoire qu'elle avait réservée en tant que mémoire tampon d'écran d'arrière-plan. Si cette application est ensuite basculée au premier plan, le tampon peut alors être copié sur l'écran réel. un système d'exploitation peut exécuter l'application en mode restreint et intercepter tous les accès à la mémoire d'affichage; si elle découvrait qu'une application supposée être dans le "fond" essayait d'écrire 0xB800: 160, elle pourrait stocker les données dans une mémoire qu'elle avait réservée en tant que mémoire tampon d'écran d'arrière-plan. Si cette application est ensuite basculée au premier plan, le tampon peut alors être copié sur l'écran réel. un système d'exploitation peut exécuter l'application en mode restreint et intercepter tous les accès à la mémoire d'affichage; si elle découvrait qu'une application supposée être dans le "fond" essayait d'écrire 0xB800: 160, elle pourrait stocker les données dans une mémoire qu'elle avait réservée en tant que mémoire tampon d'écran d'arrière-plan. Si cette application est ensuite basculée au premier plan, le tampon peut alors être copié sur l'écran réel.

Les éléments clés à noter sont (1) bien qu'il soit souvent pratique de disposer d'un ensemble standard de routines pour effectuer divers services standard tels que l'affichage de texte, elles ne font rien qu'une application qui s'exécutait en "mode privilégié" ne pourrait pas faire. s'il était correctement programmé pour traiter le matériel installé; (2) Bien que leur système d'exploitation empêche la plupart des applications fonctionnant aujourd'hui d'exécuter directement de telles E / S, un programme qui démarre en mode privilégié peut faire ce qu'il veut et peut définir les règles qu'il souhaite pour le mode restreint. programmes.

supercat
la source
2

Comme Stephen C. l’a dit, il ne s’agit pas seulement de démarrer le système d’exploitation, mais également de savoir comment il fonctionne, qui interagit avec le matériel et les logiciels qui s’y trouvent.

J'ajouterai simplement à sa réponse que vous voudrez peut-être jeter un coup d'œil à "Les éléments des systèmes informatiques" . C'est un livre et des outils qui expliquent comment un ordinateur, un système d'exploitation et des compilateurs interagissent. L’unique particularité de ce logiciel est de vous donner les outils nécessaires pour développer très rapidement votre propre système d’exploitation dans un environnement simulé, en ignorant les nombreux détails nécessaires à un système réel, afin que vous puissiez saisir les concepts . Il fait un excellent travail de vous laisser voir la forêt au lieu des arbres.

Si vous souhaitez en savoir plus sur la manière dont le système d'exploitation interagit avec le matériel, consultez Minix .

Gilad Naor
la source
1

Vous écrivez un système d'exploitation. Il doit être exécuté d'une manière ou d'une autre, et cette manière est dans un autre OS?

Votre application est en cours d'exécution dans un système d'exploitation. Ce système d'exploitation fournit des services à votre application, tels que l'ouverture d'un fichier et l'écriture d'octets. Ces services sont généralement fournis via des appels système.

Le système d'exploitation est en cours d'exécution dans le matériel. Le matériel fournit des services au système d’exploitation, notamment la définition du débit en bauds d’un port série et l’écriture d’octets. Ces services sont généralement fournis via des registres mappés en mémoire ou des ports d'E / S.


Pour donner un exemple très simplifié de la façon dont cela fonctionne:

Votre application demande au système d'exploitation d'écrire quelque chose dans un fichier. Pour votre application, le système d'exploitation fournit des concepts tels que les fichiers et les répertoires.

Sur le matériel, ces concepts n'existent pas. Le matériel fournit des concepts tels que des disques divisés en blocs fixes de 512 octets. Le système d'exploitation décide des blocs à utiliser pour votre fichier et de certains autres blocs pour les métadonnées, tels que le nom du fichier, sa taille et son emplacement sur le disque. Il indique ensuite au matériel: écrivez ces 512 octets dans le secteur portant ce numéro sur le disque portant ce numéro; écrivez ces 512 autres octets dans le secteur portant ce numéro différent sur le disque portant le même numéro; etc.

La façon dont le système d'exploitation demande au matériel de le faire varie beaucoup. L'une des fonctions d'un système d'exploitation est de protéger les applications de ces différences. Pour l'exemple de disque, sur un type de matériel, le système d'exploitation doit écrire le numéro de disque et de secteur sur un port d'E / S, puis écrire les octets un par un sur un port d'E / S séparé. Sur un autre type de matériel, le système d'exploitation devrait copier les 512 octets d'un secteur dans une zone de mémoire, écrire l'emplacement de cette zone de mémoire dans un emplacement de mémoire spécial et écrire le numéro de disque et de secteur sur un autre. emplacement mémoire spécial.


Le matériel haut de gamme d'aujourd'hui est extrêmement compliqué. Les manuels contenant tous les détails de la programmation sont des verrous de milliers de pages; Par exemple, le dernier manuel du processeur Intel comprend sept volumes, avec un total de plus de 4000 pages, et ce, uniquement pour le processeur. La plupart des autres composants exposent des blocs de mémoire ou des ports d'E / S, que le système d'exploitation peut demander à la CPU de mapper sur des adresses situées dans son espace d'adressage. Plusieurs de ces composants exposent encore plus d'éléments derrière quelques ports d'E / S ou adresses mémoire; à titre d'exemple, le RTC (Real Time Clock, le composant qui conserve l'heure de l'ordinateur lorsqu'il est éteint) expose quelques centaines d'octets de mémoire derrière une paire de ports d'E / S. Il s'agit d'un composant très simple qui remonte à le PC / AT d'origine. Des choses comme les disques durs ont des processeurs entièrement séparés, auquel le système d'exploitation communique via des commandes standardisées. Les GPU sont encore plus compliqués.

Plusieurs personnes dans les commentaires ci-dessus ont suggéré l'Arduino. Je suis d’accord avec eux, c’est beaucoup plus simple à comprendre: l’ATmega328, qui fait tout sur l’Arduino Uno sauf que le connecteur USB est exposé comme un port série, a un manuel de quelques centaines de pages seulement. Sur l’Arduino, vous exécutez directement sur le matériel, sans système d’exploitation entre les deux; juste quelques petites routines de bibliothèque que vous n’aurez pas à utiliser si vous ne le souhaitez pas.

CesarB
la source
1

Exemples exécutables

Techniquement, un programme qui fonctionne sans système d'exploitation est un système d'exploitation. Voyons maintenant comment créer et exécuter des systèmes d’exploitation minuscules Hello World.

Le code de tous les exemples ci-dessous est présent sur ce dépôt GitHub .

Secteur de démarrage

Sur x86, la chose la plus simple et la plus simple à faire est de créer un secteur de démarrage principal (MBR) , qui est un type de secteur de démarrage , puis de l’installer sur un disque.

Ici, nous en créons un avec un seul printfappel:

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Résultat:

entrez la description de l'image ici

Testé sur Ubuntu 18.04, QEMU 2.11.1.

main.img contient les éléments suivants:

  • \364en octal == 0xf4en hex: l'encodage d'une hltinstruction, qui indique à la CPU de ne plus fonctionner.

    Par conséquent, notre programme ne fera rien: démarrez et arrêtez seulement.

    Nous utilisons octal car les \xnombres hexadécimaux ne sont pas spécifiés par POSIX.

    Nous pourrions obtenir cet encodage facilement avec:

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    mais l' 0xf4encodage est bien sûr également documenté dans le manuel d'Intel.

  • %509sproduire 509 espaces. Nécessaire pour remplir le fichier jusqu'à l'octet 510.

  • \125\252en octal == 0x55suivi de 0xaa: octets magiques requis par le matériel. Ils doivent être les octets 511 et 512.

    S'il n'est pas présent, le matériel ne le traitera pas comme un disque amorçable.

Notez que même sans rien faire, quelques caractères sont déjà imprimés à l'écran. Celles-ci sont imprimées par le firmware et servent à identifier le système.

Exécuter sur du vrai matériel

Les émulateurs sont amusants, mais le matériel est la vraie affaire.

Notez cependant que ceci est dangereux et que vous pourriez effacer votre disque par erreur: ne le faites que sur de vieilles machines qui ne contiennent pas de données critiques! Ou encore mieux, des cartes de développement telles que Raspberry Pi, voir l'exemple ARM ci-dessous.

Pour un ordinateur portable typique, vous devez faire quelque chose comme:

  • Gravez l'image sur une clé USB (détruira vos données!):

    sudo dd if=main.img of=/dev/sdX
    
  • branchez la clé USB sur un ordinateur

  • allume ça

  • dites-lui de démarrer à partir de l'USB.

    Cela signifie que le micrologiciel sélectionne USB avant le disque dur.

    Si ce n'est pas le comportement par défaut de votre machine, continuez à appuyer sur Entrée, F12, ESC ou toute autre clé bizarre après la mise sous tension jusqu'à ce que vous obteniez un menu de démarrage dans lequel vous pouvez choisir de démarrer à partir de l'USB.

    Il est souvent possible de configurer l'ordre de recherche dans ces menus.

Par exemple, sur mon ancien Lenovo Thinkpad T430, UEFI BIOS 1.16, je peux voir:

Bonjour le monde

Maintenant que nous avons créé un programme minimal, passons à un monde de salut.

La question évidente est: comment faire IO? Quelques options:

  • demandez au firmware, par exemple BIOS ou UEFI, de le faire si pour nous
  • VGA: région mémoire spéciale qui est imprimée à l'écran si elle est écrite. Peut être utilisé en mode protégé.
  • écrivez un pilote et parlez directement au matériel d'affichage. C'est la "bonne" façon de le faire: plus puissant, mais plus complexe.
  • port série . C'est un protocole normalisé très simple qui envoie et récupère les caractères d'un terminal hôte.

    Source .

    Il n’est malheureusement pas exposé sur la plupart des ordinateurs portables modernes, mais c’est le moyen le plus courant d’utiliser des cartes de développement, voir les exemples ARM ci-dessous.

    C'est vraiment dommage, car de telles interfaces sont vraiment utiles pour déboguer le noyau Linux par exemple .

  • utiliser les fonctionnalités de débogage des puces. ARM appelle leur semi-hôte par exemple. Sur du matériel réel, cela nécessite un support matériel et logiciel supplémentaire, mais sur les émulateurs, cela peut être une option pratique et gratuite. Exemple .

Ici, nous allons faire un exemple de BIOS car il est plus simple sous x86. Mais notez que ce n'est pas la méthode la plus robuste.

main.S

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Assemblez et faites le lien avec:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

Résultat:

entrez la description de l'image ici

Testé sur: Lenovo Thinkpad T430, UEFI BIOS 1.16. Disque généré sur un hôte Ubuntu 18.04.

Outre les instructions de montage standard de l’utilisateur, nous avons:

  • .code16: indique à GAS de générer du code 16 bits

  • cli: désactiver les interruptions logicielles. Ceux-ci pourraient faire redémarrer le processeur après lahlt

  • int $0x10: fait un appel du BIOS. C'est ce qui imprime les caractères un par un.

Les drapeaux de liens importants sont:

  • --oformat binary: sortie du code d'assemblage binaire brut, ne le faîtes pas dans un fichier ELF, comme c'est le cas pour les exécutables utilisateur standard.

Utilisez C au lieu d'assemblage

Puisque C est compilé en assemblage, l’utilisation de C sans la bibliothèque standard est assez simple, il vous faut en gros:

  • un script d'éditeur de liens pour mettre les choses en mémoire au bon endroit
  • drapeaux qui indiquent à GCC de ne pas utiliser la bibliothèque standard
  • un point d'entrée d'assemblage minuscule qui définit l'état C requis pour mainnotamment:
    • la pile
    • zéro sur BSS

TODO: lien donc quelques exemples x86 sur GitHub. Voici un bras que j'ai créé .

Cela devient plus amusant si vous souhaitez utiliser la bibliothèque standard, car nous n'avons pas le noyau Linux, qui implémente une grande partie des fonctionnalités de la bibliothèque standard C via POSIX .

Quelques possibilités, sans passer par un système d'exploitation complet tel que Linux, incluent:

  • Newlib

    Exemple détaillé sur: https://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931

    Dans Newlib, vous devez implémenter vous-même les appels système, mais vous obtenez un système très minimal et il est très facile de les implémenter.

    Par exemple, vous pouvez rediriger printfvers les systèmes UART ou ARM, ou mettre exit()en œuvre avec semi-hébergement .

  • systèmes d'exploitation embarqués tels que FreeRTOS et Zephyr .

    De tels systèmes d'exploitation vous permettent généralement de désactiver la planification préemptive, vous donnant ainsi un contrôle total sur l'exécution du programme.

    Ils peuvent être vus comme une sorte de Newlib pré-implémenté.

BRAS

Dans ARM, les idées générales sont les mêmes. J'ai téléchargé:

Pour le Raspberry Pi, https://github.com/dwelch67/raspberrypi ressemble au didacticiel le plus populaire disponible à ce jour.

Certaines différences par rapport à x86 incluent:

  • IO se fait en écrivant aux adresses magiques directement, il n'y a pas inet outinstructions.

    Ceci est appelé IO mappé en mémoire .

  • pour du matériel réel, comme le Raspberry Pi, vous pouvez ajouter le micrologiciel (BIOS) vous-même à l'image du disque.

    C’est une bonne chose, car cela rend la mise à jour de ce micrologiciel plus transparente.

Micrologiciel

En réalité, votre secteur de démarrage n'est pas le premier logiciel qui s'exécute sur le processeur du système.

Ce qui fonctionne réellement en premier est le soi-disant micrologiciel , qui est un logiciel:

  • fabriqué par les fabricants de matériel
  • source généralement fermée mais probablement à base de C
  • stocké dans la mémoire en lecture seule, et donc plus difficile / impossible à modifier sans le consentement du vendeur.

Les firmwares bien connus incluent:

  • BIOS : ancien micrologiciel tout-présent x86. SeaBIOS est l'implémentation open source par défaut utilisée par QEMU.
  • UEFI : successeur du BIOS, mieux standardisé, mais plus performant et incroyablement gonflé.
  • Coreboot : la noble tentative d'open source cross arch arch

Le firmware fait des choses comme:

  • boucle sur chaque disque dur, clé USB, réseau, etc. jusqu'à ce que vous trouviez quelque chose de bootable.

    Quand nous courons QEMU, -hdadit que main.imgest un disque dur connecté au matériel, et

    hda est le premier à être essayé, et il est utilisé.

  • chargez les 512 premiers octets dans l’adresse de la mémoire RAM 0x7c00, placez-y le RIP de la CPU et laissez-le fonctionner

  • afficher des éléments tels que le menu de démarrage ou les appels d'impression du BIOS sur l'écran

Le micrologiciel offre une fonctionnalité semblable à celle du système d'exploitation dont dépend la plupart des systèmes d'exploitation. Par exemple, un sous-ensemble Python a été porté pour fonctionner sur le BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

On peut faire valoir que les firmwares sont indiscernables des systèmes d'exploitation et que les micrologiciels sont la seule "vraie" programmation en métal nu que l'on puisse faire.

Comme le dit ce développeur CoreOS :

La partie difficile

Lorsque vous mettez un PC sous tension, les puces composant le chipset (northbridge, southbridge et SuperIO) ne sont pas encore correctement initialisées. Même si la ROM du BIOS est aussi éloignée du processeur que possible, elle est accessible par le processeur, car elle doit l'être, sinon le processeur n'aurait aucune instruction à exécuter. Cela ne signifie pas que la ROM du BIOS est complètement mappée, généralement pas. Mais juste assez est mis en correspondance pour lancer le processus de démarrage. Tous les autres appareils, oubliez ça.

Lorsque vous exécutez Coreboot sous QEMU, vous pouvez expérimenter avec les couches supérieures de Coreboot et avec les charges utiles, mais QEMU offre peu d'occasions d'expérimenter le code de démarrage de bas niveau. D'une part, la RAM fonctionne bien dès le début.

Etat initial post-BIOS

Comme beaucoup de choses dans le matériel, la normalisation est faible et l’une des choses que vous ne devriez pas compter est l'état initial des registres lorsque votre code commence à s'exécuter après le BIOS.

Alors faites-vous une faveur et utilisez un code d'initialisation comme celui-ci: https://stackoverflow.com/a/32509555/895245

Les registres aiment %dset %esont des effets secondaires importants, vous devriez donc les mettre à zéro même si vous ne les utilisez pas explicitement.

Notez que certains émulateurs sont plus agréables qu'un matériel réel et vous offrent un bon état initial. Ensuite, lorsque vous utilisez du matériel réel, tout se brise.

GNU GRUB Multiboot

Les secteurs de démarrage sont simples, mais ils ne sont pas très pratiques:

  • vous ne pouvez avoir qu'un seul système d'exploitation par disque
  • le code de chargement doit être très petit et tenir dans 512 octets. Cela pourrait être résolu avec l' appel BIOS int 0x13 .
  • vous devez faire vous-même beaucoup de démarrage, comme passer en mode protégé

C’est pour ces raisons que GNU GRUB a créé un format de fichier plus pratique appelé multiboot.

Exemple de travail minimal: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Je l’utilise également sur mon dépôt d’exemples GitHub pour pouvoir exécuter facilement tous les exemples sur du matériel réel sans graver la clé USB un million de fois. Sur QEMU cela ressemble à ceci:

entrez la description de l'image ici

Si vous préparez votre système d'exploitation en tant que fichier à démarrage multiple, GRUB est alors en mesure de le trouver dans un système de fichiers classique.

C’est ce que font la plupart des distributions, en mettant les images du SE sous /boot .

Les fichiers à démarrage multiple sont essentiellement un fichier ELF avec un en-tête spécial. Ils sont spécifiés par GRUB à l’ adresse suivante : https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Vous pouvez transformer un fichier à démarrage multiple en un disque amorçable avec grub-mkrescue.

El Torito

Format pouvant être gravé sur CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Il est également possible de produire une image hybride qui fonctionne sur ISO ou USB. Ceci peut être fait avec grub-mkrescue( exemple ), et est aussi fait par le noyau Linux en make isoimageutilisant isohybrid.

Ressources

Ciro Santilli 改造 心心
la source