# Côté logiciel

La partie logicielle de l'entrée-sortie est une partie du système
d'exploitation qui a pour but de fournir une interface vers les
périphériques tout en assurant une indépendance par rapport au type de
périphérique.

Par exemple, l'accès à une clé USB doit être, du point de vue des
programmes, identique à l'accès sur un disque dur.

Cette partie du système d'exploitation est divisée en quatre couches,
que l'on va adresser en partant du plus proche du matériel vers le plus
haut niveau.

![](https://books.snowcode.ovh/uploads/images/gallery/2024-01/2024-01-05-14-26-50-screenshot.png)

### Le gestionnaire d'interruption

Le gestionnaire de périphérique (le pilote) qui lance l'opération
d'entrée-sortie, va attendre une interruption.

Lorsque l'interruption survient, **gestionnaire d'interruptions** va
avertir le gestionnaire de périphérique et celui-ci va terminer
l'opération voulue.

Lorsqu'une interruption survient, le gestionnaire d'interruptions va
sauvegarder le contexte du processus en cours, créer le contexte pour le
traitement de l'interruption, se place en état "disponible" pour traiter
les interruptions suivantes, exécuter la procédure de traitement (la
routine dont on a parlé dans la section précédente).

Une fois les interruptions gérées, le scheduler choisit le processus à
démarrer.

### Gestionnaire de périphérique (pilote ou driver)

![](https://books.snowcode.ovh/uploads/images/gallery/2024-01/2024-01-05-13-59-08-screenshot.png)

Le **gestionnaire de périphérique**, aussi appelé **pilote** ou
**driver**, dépends de la nature du périphérique, ainsi un gestionnaire
de disque fonctionne très différemment d'un gestionnaire de souris.

Le gestionnaire de périphérique, afin d'être utilisable, doit être
chargé au cœur du système, dans le noyau, en mode protégé. C'est pour
cela qu'un mauvais driver pour conduire à des plantages du système
d'exploitation.

Mais en même temps, ces gestionnaires de périphériques, écrits par les
fabricants, doivent pouvoir être inséré facilement dans le noyau
(kernel) pour être utilisé.

#### Fonctionnement

Le système appelle des fonctions internes (open, read, write,
initialize, etc) des pilotes afin de commander le périphérique. Les
pilotes vont ensuite vérifier si les paramètres reçus sont corrects et
valide et vont traduire certains paramètres en données physiques.

Par exemple, un pilote de disque dur va traduire un numéro de bloc en
cylindre, piste, secteur et tête.

Ensuite, le pilote vérifie si le périphérique est disponible (sinon il
attend), il vérifie également l'état du périphérique et le commande.

Pendant le travail du périphérique, le pilote s'endort, car il ne peut
plus rien faire. Une fois qu'il est réveillé par le gestionnaire
d'interruption, il vérifie que tout s'est bien passé et transmet les
informations.

### La couche logicielle indépendante

La couche de **logicielle indépendante** (aussi appelée **interface
standard**)

Cette couche fournit une interface uniforme pour les drivers (la fameuse
API dont on a parlé plus tôt), ainsi que du buffering, de la gestion
d'erreur, de l'allocation et libération des périphériques.

#### Interface standard

Grâce à cette couche, on s'assure que les drivers sont appelés de
manière uniforme.

![](https://books.snowcode.ovh/uploads/images/gallery/2024-01/2024-01-05-14-10-49-screenshot.png)

*A gauche, un système sans interface standard, à droite un système avec
une interface standard*

#### Uniformisation des noms

La désignation d'un périphérique doit être non ambiguë, sous Linux le
répertoire `/dev` contient des entrées pour chaque périphérique. Un
périphérique est donc traité au même titre qu'un fichier, il ne peut
donc pas en avoir deux avec le même nom.

Par exemple, un disque dur sera appelé `/dev/sda`, si on met un autre
disque dur, celui-ci sera appelé `/dev/sdb`.

#### Buffering

Afin d'améliorer les performances du système et d'éviter de faire
beaucoup d'accès inutilement, on garde des buffers (tampons) dans le
système d'exploitation et dans les contrôleurs. L'objectif étant de
placer en mémoire les informations qui sont souvent accédées.

#### Gestion d'erreurs

C'est aussi à cette couche que revient la gestion des erreurs.
L'utilisation de périphériques induit un certain nombre d'erreurs
(temporaire ou permanentes).

Il y a deux types d'erreurs, les **erreurs de programmation** qui
surviennent lorsque l'opération demandée est impossible (exemple,
écriture sur un périphérique d'entrée), dans quel cas on rapporte
l'erreur et on s'arrête.

Et les **erreurs d'entrées sorties** qui surviennent lorsqu'une
opération se termine de façon anormale (par exemple, on déconnecte le
disque, ou le périphérique est défectueux). Dans ce cas, le driver
décide de soit tenter à nouveau l'opération ou alors de rapporter
l'erreur.

#### Allocations et libérations des périphériques

Certains périphériques ne sont pas partageables, par exemple avec une
imprimante, il est impossible d'imprimer plusieurs choses en même temps.

Il revient alors au système d'exploitation de vérifier si le
périphérique est libre et s'il peut être alloué au processus.

La fonction `open` permet à un processus d'informer le système
d'exploitation qu'il souhaite utiliser un périphérique, le système
vérifie alors sa disponibilité.

### Couche d'entrée-sortie applicative

Cette partie de la gestion de l'entrée sortie est faite au niveau de
l'application, par exemple via les libraires systèmes liées aux
programmes.

C'est là que l'on va par exemple trouver les appels systèmes de `stdio`
tel qu'open, printf, read, write, etc.

Cette couche va également fournir un système de **spooling** qui permet
de créer une fille d'attente pour l'accès aux périphériques non
partageables. Par exemple via une liste de jobs d'impression pour une
imprimante.