Systemes d'exploitation

OSRoles

PagePrincipale :: DerniersChangements :: ParametresUtilisateur :: Vous êtes ec2-44-192-93-109.compute-1.amazonaws.com
<< Architecture et fonctionnement d'un micro-ordinateur ChapitreArchitecture Exercices supplémentaires? >>

Rôles d'un système d'exploitation


Démarrage


Le problème du démarrage de l’ordinateur n’est pas abordé dans le livre. À y réfléchir : accéder à un programme depuis un périphérique demande que les gestionnaires d'interruption? soient chargés. Mais pour charger ces gestionnaires et les vecteurs d'interruption? associés, il faut un programme ! C'est le dilemme de la poule et l'œuf.
En réalité, le processeur est pré-câblé pour aller chercher une instruction à une adresse fixe de la mémoire. Cette adresse correspond à de la ROM? qui contient une sorte de système d'exploitation très minimaliste, le BIOS? (Basic Input/Output System) sur les architectures PC. Ce logiciel parcours les périphériques selon un ordre et un protocole prédéterminé, et démarre le premier logiciel correspondant à ce protocole. Ce logiciel est alors soit le système d’exploitation lui-même, soit un petit logiciel appelé chargeur de système? ou OS loader dont le rôle est de proposer à l’utilisateur la liste des systèmes d’exploitations disponibles au démarrage.

Les appels système


Nous présentons ici une expérience qui se fait en Linux et qui montre que même les programmes les plus anodins ont besoin d'appeler des fonctions du noyau. Pour la reproduire, il faut compiler l'exemple de façon statique, et imposer qu'il n'établisse aucun lien dynamique avec les bibliothèques système, faute de quoi un certain nombre d'appels liées à la création de zones de mémoire partagée et de chargement de fichiers apparaissent. Cela implique qu'il doit être compilé avec quelques options de compilation peu habituelles : -static -static-libgcc.

Le programme exemple.c, d'une extrême simplicité, ne fait rien en apparence.
        #include 

        int main(int argc, char** argv) {
	
         /* printf("Message\n"); */
         return 0;
        }

On le compile avec les options citées précédemment, puis on fait appel à la commande Linux, strace, qui affiche les appels système qui sont effectués lors d'une exécution.
        $ gcc  –static –static-libgcc exemple.c
        $ strace ./a.out > /tmp/strace.txt
        execve("./a.out", ["./a.out"], [/* 29 vars */]) = 0
        uname({sys="Linux", node="nolay", ...}) = 0
        brk(0)                                  = 0x80ad000
        brk(0x80ce000)                          = 0x80ce000
        exit_group(0)                          = ?

Cet affichage montre que a.out fait très exactement 5 appels systèmes (execve, uname, brk – 2 fois – et exit_group) lors de son exécution, ce qui peut sembler étrange pour un programme qui est censé ne rien faire ! En réalité, les fonctions mises en exergue sont liées à l'allocation de la mémoire et la mise en place des structures nécessaires pour l'exécution d'un processus (cf. section 6.5 du chapitre 2 du livre) à travers les appels à execve, (qui copie l'exécutable en mémoire), et brk qui alloue de la mémoire. uname récupère les informations sur la configuration du noyau, et exit_group termine le processus en cours et libère la mémoire utilisée.
En retirant les commentaires dans programme source précédent, et en le recompilant, la trace d'exécution devient :
        execve("./a.out", ["./a.out"], [/* 29 vars */]) = 0
        uname({sys="Linux", node="nolay", ...}) = 0
        brk(0)                                  = 0x80ad000
        brk(0x80ce000)                          = 0x80ce000
        stat64(1, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
        old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, 
                 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd8000
        write(1, "Message\n", 8)                = 8
        munmap(0xb7fd8000, 4096)                = 0
        exit_group(0)                           = ?

Certains appels sont les mêmes que dans le cas précédent. La seule instruction printf déclenche quatre appels système. fstat64 vérifie l'état du fichier dont le descripteur est 1 (et qui correspond à la sortie standard) puis le processus alloue la mémoire nécessaire (old_mmap) pour l'échange de données avec le système. Ensuite on écrit "Message\n" d'une longueur de 8 caractères dans le descripteur de fichier 1 (write). À la fin de l'exécution, la mémoire partagée est dissociée du processus (munmap).
Cet exemple illustre l'utilisation des appels système et le fait qu'ils soient encapsulés dans des fonctions courantes. Chaque appel système donne, en plus, lieu à des vérifications de validité des paramètres, puis par l'appel à un trap, qui fait basculer le processeur en mode noyau. Observer les appels système à ce niveau de détail nécessite, en revanche, d'analyser le code source du système d'exploitation lui-même.

Il n'y a pas de commentaire sur cette page. [Afficher commentaires/formulaire]