Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

Introduction aux systèmes d'exploitation

Bonjour, Habr! Je souhaite vous présenter une série d'articles traduits d'une littérature qui, à mon avis, est intéressante — OSTEP. Dans ce matériau, nous examinons en profondeur le fonctionnement des systèmes d'exploitation de type Unix, notamment la gestion des processus, les différents planificateurs, la mémoire et d'autres composants similaires qui constituent un système d'exploitation moderne. Vous pouvez consulter l'original de tous les documents ici. ici. Je vous demande de prendre en compte que la traduction a été réalisée de manière non professionnelle (assez libre), mais j'espère que le sens général a été conservé.

Les travaux pratiques sur ce sujet peuvent être trouvés ici :
— original : pages.cs.wisc.edu/~remzi/OSTEP/Homework/homework.html
— original : github.com/remzi-arpacidusseau/ostep-code
— ma propre adaptation : github.com/bykvaadm/OS/tree/master/ostep

Vous pouvez également visiter ma chaîne sur Telegram =)

Fonctionnement du programme

Que se passe-t-il lorsque n'importe quel programme fonctionne ? Un programme lancé effectue une tâche simple — il exécute des instructions. Chaque seconde, des millions, voire des milliards d'instructions sont extraites de la mémoire vive par le processeur, qui les décode (par exemple, il reconnaît à quel type appartiennent ces instructions) et les exécute. Cela peut être l'addition de deux nombres, l'accès à la mémoire, la vérification d'une condition, le passage à une fonction, et ainsi de suite. Après l'exécution d'une instruction, le processeur passe à l'exécution de la suivante. Et ainsi, instruction après instruction, elles s'exécutent jusqu'à ce que le programme se termine.
Cet exemple est naturellement simplifié — en réalité, pour accélérer le fonctionnement du processeur, le matériel moderne permet d'exécuter les instructions hors séquence, de calculer les résultats possibles, d'exécuter les instructions en parallèle et d'autres astuces similaires.

Modèle de calcul de Von Neumann

La forme simplifiée de fonctionnement que nous avons décrite ressemble au modèle de calcul de Von Neumann. Von Neumann est l'un des pionniers des systèmes informatiques, et il est également l'un des auteurs de la théorie des jeux.Pendant l'exécution d'un programme, de nombreux autres événements se produisent, de nombreux autres processus et logiques externes fonctionnent, dont le principal objectif est de simplifier le démarrage, le fonctionnement et la maintenance du système.
Il existe un ensemble de logiciels responsables de la facilité de lancement des programmes (ou même permettant de faire fonctionner plusieurs programmes simultanément). Il permet aux programmes de partager la même mémoire et d'interagir avec divers appareils. Cet ensemble de logiciels est essentiellement ce que l'on appelle un système d'exploitation, et sa tâche principale est de s'assurer que le système fonctionne correctement et efficacement, tout en facilitant la gestion de celui-ci.

Système d'exploitation

Un système d'exploitation, abrégé en OS, est un ensemble de programmes interconnectés, destiné à gérer les ressources de l'ordinateur et à organiser l'interaction entre l'utilisateur et l'ordinateur..
Le système d'exploitation atteint son efficacité principalement grâce à la technique la plus importante : la virtualisation.. L'OS interagit avec les ressources physiques (processeur, mémoire, disque, etc.) et les transforme en une forme plus générale, offrant plus de fonctionnalités et étant plus simple à utiliser. Ainsi, pour une compréhension générale, on peut comparer très grossièrement un système d'exploitation à une machine virtuelle.
Pour permettre aux utilisateurs de donner des commandes au système d'exploitation et ainsi utiliser les capacités de la machine virtuelle (comme : lancer un programme, allouer de la mémoire, accéder à un fichier, etc.), le système d'exploitation fournit une interface appelée API (interface de programmation d'applications) à laquelle on peut faire des appels. Un système d'exploitation typique permet de réaliser des centaines d'appels système.
Enfin, puisque la virtualisation permet à de nombreux programmes de fonctionner (partageant ainsi le CPU) tout en accédant simultanément à leurs instructions et données (partageant ainsi la mémoire), ainsi qu'en accédant aux disques (partageant ainsi les périphériques d'entrée/sortie), le système d'exploitation est également appelé gestionnaire de ressources. Chaque processeur, disque et mémoire constitue une ressource du système et, par conséquent, l'une des fonctions du système d'exploitation est de gérer ces ressources de manière efficace, équitable ou, au contraire, selon la tâche pour laquelle ce système d'exploitation a été conçu.

Virtualisation du CPU

Considérons le programme suivant :
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

Il n'exécute pas d'actions particulières, en fait tout ce qu'il fait est d'appeler la fonction spin(), dont la tâche est de vérifier cycliquement le temps et de retourner après qu'une seconde se soit écoulée. Ainsi, il répète indéfiniment la chaîne que l'utilisateur a transmise en tant qu'argument.
Lançons ce programme et passons-lui le caractère “A” en argument. Le résultat n'est pas particulièrement intéressant : le système exécute simplement un programme qui affiche périodiquement le caractère “A”.
Essayons maintenant un scénario où plusieurs instances du même programme s'exécutent, mais qui affichent différentes lettres pour plus de clarté. Dans ce cas, le résultat sera quelque peu différent. Même si nous avons un seul processeur, le programme s'exécute simultanément. Comment cela est-il possible ? C'est que le système d'exploitation, avec l'aide des capacités matérielles, crée une illusion. L'illusion qu'il existe plusieurs processeurs virtuels dans le système, transformant un processeur physique en un nombre théoriquement infini, permettant ainsi aux programmes de sembler s'exécuter simultanément. Cette illusion est appelée Virtualisation du CPU.
Un tel tableau soulève de nombreuses questions, par exemple, lorsque plusieurs programmes souhaitent s'exécuter simultanément, lequel sera réellement exécuté ? Ce problème est géré par les « politiques » du système d'exploitation. Les politiques sont utilisées dans de nombreux domaines du système d'exploitation et répondent à ce type de questions, servant également de mécanismes de base que le système d'exploitation implémente. D'où le rôle du système d'exploitation en tant que gestionnaire de ressources.

Virtualisation de la mémoire

Examinons maintenant la mémoire. Le modèle physique de la mémoire dans les systèmes modernes est présenté comme un tableau d'octets. Pour lire la mémoire, il faut spécifier un adresse de cellule, afin d'y accéder. Pour écrire ou mettre à jour des données, il faut également spécifier les données et l'adresse de la cellule dans laquelle les enregistrer.
Les accès à la mémoire se produisent en permanence pendant l'exécution du programme. Le programme stocke dans la mémoire toute sa structure de données et y accède en exécutant diverses instructions. Les instructions sont également stockées dans la mémoire, c'est pourquoi l'accès à celle-ci se produit également à chaque demande pour la prochaine instruction.

Appel de malloc()

Considérons le programme suivant qui alloue de la mémoire en utilisant un appel malloc() (https://youtu.be/jnlKRnoT1m0):

Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

Le programme effectue plusieurs actions. Tout d'abord, il alloue une certaine quantité de mémoire (ligne 7), puis il affiche l'adresse de l'emplacement mémoire alloué (ligne 9), et enfin il écrit zéro dans le premier slot de la mémoire allouée. Ensuite, le programme entre dans une boucle où il incrémente la valeur stockée en mémoire à l'adresse de la variable "p". Il affiche également l'identifiant de son propre processus. L'identifiant de processus est unique pour chaque processus en cours d'exécution. Si nous exécutons plusieurs copies, nous allons constater un résultat intéressant : Dans le premier cas, si nous ne faisons rien d'autre que d'exécuter plusieurs copies, les adresses seront différentes. Mais cela ne cadre pas avec notre théorie ! En effet, puisque les distributions modernes incluent par défaut la fonction de randomisation de la mémoire. Si nous la désactivons, nous obtiendrons le résultat attendu : les adresses mémoire de deux programmes fonctionnant simultanément coïncideront.

Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

En fin de compte, il s'avère que deux programmes indépendants fonctionnent avec leurs propres espaces d'adresses privés, qui sont à leur tour mappés par le système d'exploitation dans la mémoire physique. Par conséquent, l'utilisation des adresses mémoire dans un même programme n'affectera en rien les autres, et chaque programme a l'impression de disposer de son propre espace de mémoire physique, entièrement à sa disposition. La réalité, cependant, est que la mémoire physique est une ressource partagée, dont la gestion est assurée par le système d'exploitation.

Cohérence

Un autre sujet important dans le cadre des systèmes d'exploitation est la cohérence. Ce terme est utilisé lorsqu'il s'agit de problèmes dans le système qui peuvent survenir lors de l'exécution de plusieurs opérations simultanément au sein d'un même programme. Des problèmes de cohérence peuvent se produire même dans le système d'exploitation lui-même. Dans les exemples précédents de virtualisation de la mémoire et du processeur, nous avons compris que le système d'exploitation gère de nombreuses choses en même temps — il exécute le premier processus, puis le second, et ainsi de suite. Il s'est avéré que ce comportement peut entraîner certains problèmes. Par exemple, les programmes multithread modernes rencontrent de telles difficultés.

Considérons le programme suivant :

Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

Le programme dans la fonction principale crée deux threads en utilisant l'appel Pthread_create(). Dans cet exemple, on peut penser au thread comme à une fonction exécutée dans un même espace mémoire, à côté d'autres fonctions, avec un nombre de fonctions lancées simultanément manifestement supérieur à un. Dans cet exemple, chaque thread démarre et exécute la fonction worker() qui, à son tour, incrémente simplement une variable.,.

Nous allons exécuter ce programme avec l'argument 1000. Comme vous l'avez probablement deviné, le résultat devrait être 2000, car chaque thread a incrémenté la variable 1000 fois. Cependant, ce n'est pas si simple. Essayons de lancer le programme avec un nombre de répétitions beaucoup plus élevé.

Systèmes d'exploitation : trois pièces faciles. Partie 1 : Introduction (traduction)

En entrant un nombre, par exemple 100000, nous nous attendons à voir en sortie le nombre 200000. Cependant, en exécutant le nombre 100000 plusieurs fois, nous ne verrons non seulement pas la bonne réponse, mais nous obtiendrons également des réponses incorrectes différentes. Le secret réside dans le fait que pour incrémenter un nombre, il faut trois opérations : extraire le nombre de la mémoire, incrémenter et ensuite écrire le nombre à nouveau. Comme toutes ces instructions ne sont pas effectuées de manière atomique (toutes en même temps), des choses étranges peuvent se produire. Ce problème est appelé en programmation race condition — condition de concurrence. Quand des forces inconnues à des moments inconnus peuvent influencer l'exécution de certaines de vos opérations.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster