Présentation de macrO.Scope

Salutations camarades !

Qui dit mois de décembre dit nouveau projet[référence nécessaire], je vous propose donc aujourd’hui une courte explication de ma dernière lubie : macrO.Scope. Il s’agit d’un projet visant à développer un nouveau système d’exploitation dont l’architecture a été totalement repensée pour mettre au cœur du système un protocole de communication assez générique : le protocole NARP (quel nom mélodieux !). Commençons donc par là.

Présentation de NARP

NARP a vocation à être utilisé dans toutes les situations où des composants d’une architecture informatique ont vocation à communiquer. Cela va de l’interaction entre les programmes et le noyau aux communication en réseau, en passant par la communication inter-processus.

L’entité de base de NARP est la connexion à un service : c’est un canal de communication bi-directionnel sur lequel on échange des messages dans les deux sens. Le contenu est asymétrique : un des deux côtés est le serveur, qui répond aux requêtes de l’autre côté qui est le client et qui est à l’origine de la connexion. Les messages communiqués sur un canal NARP s’adapte à l’application : en particulier des applications standard comme la messagerie instantanée peuvent s’implémenter très simplement.

Cependant, ce qui fait la force de NARP, c’est sa capacité de récursion : en effet, une partie du protocole concerne des messages permettant d’identifier par un nom un service NARP, et de demander une connexion dessus. Ainsi, un canal NARP peut servir à multiplexer plusieurs canaux de communication : les communications sont imbriquées, d’où récursion.

Pour plus de détails sur le protocole, consulter la page de référence du projet.

Architecture de macrO.Scope

Mettre NARP au centre de l’OS, ça signifie qu’un grand nombre de tâches disposant habituellement d’une API spécifique sont toutes contenues dans des communications au format spécifié par le protocole. En particulier, les actions suivantes :

  • Accès aux périphériques
  • Accès aux informations sur les processus et la mémoire ; lancement de processus
  • Accès aux systèmes de fichiers
  • Accès au réseau
  • Communication inter-processus
  • Interaction homme-machine

Pour permettre cela de façon propre, le noyau sera divisé en trois niveaux :

  • Niveau 0 : ce niveau contient les fonctionnalités élémentaires au fonctionnement du noyau, en particulier l’initialisation et la gestion de la mémoire
  • Niveau 1 : ce niveau contient la partie la plus importante du noyau, à savoir les mécanismes de communication et de synchronisation entre les processus (c’est ici que NARP intervient).
  • Niveau 2 : ce niveau contient une assemblée de processus noyau, qui fonctionne de façon assez isolée et communiquent uniquement à travers les fonctions fournies par le niveau 2. Ces processus fournissent tous les services utiles à la machine : pilotes de périphériques, systèmes de fichiers, couche réseau, …

Le concept de processus est au cœur du système : un processus correspond à un seul fil d’exécution (thread), ainsi que d’un espace mémoire qui lui est réservé. La seule façon pour deux processus de communiquer est de passer par les fonctionnalités du niveau 1, qui implémente la synchronisation de façon unifiée. Chaque processus noyau dispose d’un tas réservé pour l’allocation mémoire, ce qui a plusieurs avantages. En particulier, plusieurs processus ne sont pas obligés de réserver l’accès à un tas mémoire centralisé pour réaliser l’allocation : chaque tas est utilisé par exactement un seul thread. Un autre avantage est que chaque tas sera individuellement plus petit, permettant des opérations d’allocation et de libération de mémoire plus rapides. Finalement, les fuites mémoire dans des processus de niveau 2 sont plus contenues, puisque lorsque l’un de ces processus est tué, tout son tas est libéré inconditionnellement. On peut également faire une analyse plus fine pour savoir quels sont les modules noyau qui utilisent le plus de mémoire.

Il y a aussi un inconvénient à cette architecture : le niveau 1 étant au centre de toute l’attention, celui-ci recevra constamment plusieurs appels en même temps, il est donc particulièrement important de veiller à avoir une implémentation performante.

Feuille de route pour l’implémentation

La réalisation de ce projet requiert une quantité considérable de travail, et c’est pourquoi il est important de s’organiser le mieux possible.

La première étape consistera en la réalisation d’un niveau 0 complet et au design le meilleur possible, afin de fournir une base solide à la suite des travaux. Cette phase est en cours, vous en saurez plus bientôt !

Ensuite, on pourra développer un niveau 1 assez élémentaire, et des applications de niveau 2 de base, afin d’avoir quelque chose à montrer qui ne soit pas totalement inepte. Les premières choses à implémenter seront : clavier, écran, système de fichiers virtuel, shell noyau.

On pourra ensuite implémenter le mode utilisateur et passer le shell comme processus utilisateur. L’objectif est qu’il soit très facile de recompiler un processus noyau comme application utilisateur si celui-ci ne dépend pas des fonctions spécifiques de l’espace noyau (en particulier, accès au matériel). Réciproquement, il devrait être assez facile de prendre un composant de l’espace utilisateur pour le faire tourner comme processus noyau à la place. C’est ce qui avait été fait dans Windows NT pour la couche graphique : celle-ci était implémentée en mode utilisateur dans les versions NT 3.x, mais a été déplacée dans le noyau pour des raisons de performances à partir de la version NT 4.

La suite du développement se fera de manière plus souple et plus organique, avançant sur plusieurs fronts en même temps.

État actuel

Comme précisé plus haut, le développement du niveau 0 est en cours, mais à un niveau encore embryonnaire : ce matin encore, je m’évertuais à réaliser un exception handler qui ne fasse pas tout planter, tâche pour laquelle je me suis retrouvé violemment confronté à des optimisations de GCC qui détruisait des données utiles, à cause d’un mauvais interfaçage C-assembleur… Eh oui : le compilateur a le droit de modifier les arguments passés à une fonction si ça l’arrange (en particulier pour des optimisations du type tail-rec), ne comptez pas retrouver vos données indemnes après ça ! Il s’agit d’un bug connu dans ce tutoriel, qui me sert en partie de référence pour certaines parties.

Dans un prochain billet, je vous parlerai du gestionnaire de mémoire physique, qui est le prochain morceau à écrire. En attendant, vous pouvez déjà vous renseigner sur le sujet avec cette excellente page !

À bientôt pour de nouvelles aventures !