wiki:smc5

TP12 : Entrées/sorties et accès aux périphériques

1. Objectifs

Les périphériques sont des ressources partagées par tous les threads en cours d'exécution : n'importe quel thread, de n'importe quel processus, s'exécutant dans n'importe quel cluster, peut effectuer un appel système nécessitant un accès au disque.

Dans une architecture manycore capable d'exécuter plusieurs centaines de threads en parallèle, le sous-système d'entrées/sorties, et plus précisément les structures de données du noyau permettant l'accès aux périphériques peuvent facilement devenir - après l'accès à la mémoire partagée - le principal goulot d'étranglement de la machine.

Le but de ce TP est de présenter les techniques de distribution utilisées par Almos-mkh pour minimiser les risques de contention lors de l'accès au sous-système d'entrées/sorties.

2. Principes Généraux

Pour chaque type de périphérique (contrôleur de disque, contrôleur réseau, contrôleur graphique, terminal alpha-numérique, etc.), il existe un - très - grand nombre de modèles de périphériques fournissant globalement le même service, mais ayant chacun leur propres commandes, leurs propre jeu de registres adressables, et leur propre mécanisme de signalisation.

Pour faciliter l'intégration des périphériques, et pour éviter de devoir modifier et/ou recompiler le code du noyau lors de l'ajout d'un nouveau périphérique, tous les systèmes d'exploitation définissent une représentation abstraite de chaque type de périphérique, permettant de masquer les différences entre différentes implémentations matérielles dans un pilote logiciel (appelé driver), généralement fourni par le fabricant.

Par ailleurs, pour améliorer la bande passante (c'est à dire le nombre d'opérations d'entrées sortie par seconde), les contrôleurs de périphériques sont souvent multi-canaux : un même contrôleur contient plusieurs jeux de registres indépendants, permettant à l'OS d'exécuter plusieurs opérations d'entrée/sortie en parallèle pour le compte de plusieurs clients. C'est presque toujours le cas des contrôleur de terminaux, c'est le cas des contrôleurs de disque respectant la norme AHCI, et c'est de plus en plus souvent le cas des contrôleurs réseau.

3. Implémentation du sous-système d'entrées-sorties

La principale abstraction définie par almos-mkh est la structure chdev, qui représente un canal d'un périphérique. Pour les terminaux texte, et pour les contrôleurs réseau, Almosmkh définit même un chdev par canal et par direction (direction RX pour la réception de donnée depuis l'extérieur / direction TX pour la transmission de données vers l'extérieur).

La structure chdev_t et les fonctions d'accès sont définis dans les fichiers chdev.h et chdev.c.

3.1) Typage des périphériques

Almost-mkh identifie un périphérique matériel par un double index (func,impl). L'index func défnit la fonctionnalité. Pour une fonctionnalité donnée, l'index impl définit une réalisation matérielle particulière.

  • Chaque valeur de func définit en pratique un ensemble de fonctions (appelé API noyau) utilisables par le noyau et correspondant aux différents services fournis par ce type de périphérique, indépendamment de son implémentation. Il y a donc une API différente pour chaque type fonctionnel.
  • Chaque valeur de impl définit une implémentation matérielle particulière, donc un pilote particulier, pour une même fonctionnalité définie par func.

3.2) Interface noyau / pilotes

Ll'interface entre les pilotes et le code noyau qui implémente - pour chaque type fonctionnel - l'API noyau, est standardisée et contient trois fonctions:

  1. la fonction func_driver_init() est appelée par le noyau pour initialiser les registres du périphériques et les éventuelles structures de données en mémoire utilisées par le driver.
  2. la fonction func_drived_cmd() permet au noyau de donner des ordres au périphérique sans connaître son implémentation.
  3. la fonction func_driver_isr() est exécutée lorsque le périphérique signale une fin de traitement, en activant une interruption matérielle. ISR signifie Interrupt Service Routine.

3.3 File d'attente et thread serveur

Par définition un canal de périphérique ne peut traiter qu'une seule opération d'entrée/sortie à la fois. Il faut donc séquencialiser les requêtes concurrentes visant un même chdev. Almos-mkh attache donc à chaque chdev une file d'attente permettant à chaque thread client d'enregistrer sa requête puis de se bloquer, et de relâcher le processeur, en attendant que sa requête soit traitée. Ce n'est généralement pas le thread client qui exécute l'opération d'entrée/sortie. C'est un autre thread server, de type DEV, attaché au chdev, qui est chargé d'exécuter séquenciellement les requêtes d'entrée/sortie enregistrées dans la file d'attente, et de réveiller chaque thread client, quand le transfert de données demandé par le client est terminé.

En pratique, la file d'attente est implémentée comme une XLIST trans-clusters, enracinée dans la structure chdev_t, et permettant de chaîner entre elles les threads clients. Les paramètres de la requête (type de la requête, adresse du tampon mémoire source ou destination, nombre d'octets ou de blocs à transférer, etc) sont enregistrés dans le descripteur du thread client.

Lorsque la file d'attente est vide, le thread server se bloque et relâche le processeur. La politique d'ordonnancement des threads prévoit qu'un thread de type DEV a une priorité plus élevée qu'un thread de type USR, mais elle prévoit également qu'un thread DEV n'est pas exécutable lorsque la file d'attente est vide.

3.4) Placement des chdev

Les périphériques externes sont généralement connectés sur un bus externe (type PCI-express ou Hypertransport) qui est connecté au bus sytème (ou au micro-réseau dans le cas d'une architecture manycore), par un bridge. Il faut donc nécessairement passer par ce bridge, localisé dans un cluster particulier (appelé cluster_io) pour accéder aux registres adressables de tous les périphériques.

Pour minimiser les risques de contention, Almos-mkh ne place pas les descripteurs de chdev (et les threads server associés) dans le cluster_io, mais il les distribue, le plus uniformément possible, sur tous les clusters de l'architecture.

Par conséquent, dans le cas général, une opération d'entrée/sortie met en jeu trois clusters:

  1. le cluster client, qui contient le descripteur du thread client (et donc les paramètres de la commande),
  2. le cluster device, qui contient le périphérique (et donc le segment des registres adressages),
  3. le cluster server, qui contient le descripteur de chdev et le thread server,

3.5) structure du descripteur de chdev

La structure chdev_t contient (entre autres) les informations suivantes:

  • func : type fonctionnel
  • impl : type d'implementation
  • channel : index du canal
  • is_rx : direction (RX/TX)
  • base : adresse de base du segment contenant les registres
  • cmd : pointeur sur la fonction func_driver_cmd()
  • isr : pointeur sur la fonction func_driver_isr()
  • server : pointeur local sur le thread server associé
  • wait_root : racine de la liste the threads clients en attente de traitement

4. Questions

Pour répondre aux questions ci-dessous, vous devez commencer par lire la documentation décrivant le sous-système d'entrées/sorties, que vous trouverez ici.

L'API du contrôleur de disque générique (IOC pour Input/Outptut Controler) est définie ici

L'API du terminal texte générique (TXT pour Terminal teXTe) est définie ici ici

Trois contrôleurs de disque (et donc trois pilotes) ont été intégrés dans Almos-mkh : IOC_BDV, IOC_HBA, IOC_RDK. Le code du pilote IOC_BDV utilisé dans TSAR est défini dans les fichiers soclib_bdv.h et soclib_bdv.c.

Deux contrôleurs de terminaux texte (et donc deux pilotes) ont été intégrés dans Almos-mkh : TXT_TTY, TXT_MTY, Le code du pilote TXT_TTY utilisé dans TSAR est défini dans les fichiers soclib_tty.h et soclib_tty.c.

  1. quels sont les 5 classes de périphériques (c'est à dire les 5 types fonctionnels) actuellement supportées par Almos-mkh ?
  2. Les descripteurs de device (structure chdev_t) contenant toutes les informations permettant l'accès à un périphérique sont distribués de façon régulière dans les clusters, mais cette distribution dépend évidemment du nombre de clusters. Comment le noyau localise-t-il le cluster contenant un chdev particulier ? Pourquoi a-t-on préféré avoir un descripteur par canal plutôt qu'un descripteur par périphérique ?
  3. Quels sont les trois fonctions qui doivent être définies par tout pilote de périphérique, quel que soit son type fonctionnel, pour être utilisés dans Almos-mkh ? Que fait chacune de ces trois fonctions ?
  4. La plupart des opérations d'entrée/sortie sont des transferts de données qui utilisent des arguments (type de l'opération demandée, adresse du tampon mémoire source ou destination, nombre d'octets à transférer, etc.). Comment le noyau transmet-il ces arguments au pilote ? Comment le pilote signale-t-il au noyau une éventuelle erreur ((adresse mémoire illégale par exemple) ?
  5. Dans le cas le plus général, une opération d'entrée/sortie fait intervenir deux threads (client et serveur) et trois clusters (client, serveur, device). Décrivez le scénario d'un accès au disque pour traiter un miss sur cache de fichier, détecté par un thread client. Dites ce que font les deux threads logiciels, et l'automate du contrôleur de disque, depuis l'appel de la fonction dev_ioc_read() par le thread client, en précisant quels accès mémoire - locaux ou distants - effectuent ces trois acteurs. La réponse à cette question se trouve dans les fichiers chdev.c, dev_ioc.c, et soclib_bdv.c.
  6. Pour signaler la terminaison d'une opération d'entrée/sortie, la plupart des périphériques utilisent une IRQ (Interrupt Request), qui entraîne l'exécution d'une ISR (Interrupt Service Routine) sur le coeur qui reçoit cette IRQ. En principe cette ISR peut être exécutée sur n'importe coeur de l'architecture. Quel est la politique d'Almos-mkh concernant le choix du coeur qui recevra l'interruption ? Quelle technique est utilisée par Almos-MKH pour acheminer l'interruption vers le coeur choisi ? Ce routage des interruptions est-il statique (c'est à dire défini une fois pour toutes, pour chaque interruption de chaque périphérique, lors de l'initialization du système) ou dynamique (c'est à dire défini pour chaque opération d'entrée/sortie, en fonction du placement du thread client) ?
  7. Dans le cas du type TXT (terminal texte), les pilotes de périphériques doivent définir une quatrième fonction txt_driver_aux(), utilisée exclusivement pour lancer les opérations de type TXT_SYNC_WRITE. Cette fonction n'utilise pas la structure txt_command_t enregistrée dans le descripteur du thread client. Pour quelle raison Almos-mkh n'utilise-t-il pas la fonction txt_driver_cmd() et la méthode standard de passage des arguments ?
  8. Quelle est la politique générale de Almos-mkh concernant l'allocation d'un terminal texte à un processus utilisateur ? Puisqu'un même terminal peut être partagé par plusieurs processus, deux processus peuvent exécuter de façon concurrente l'appel système getc() pour acquérir un character saisi au clavier. Comment Almos-mkh détermine-t-il vers quel processus doit être aiguillé le caractère entrant ?
  9. L'appel système printf() permet à une application d'afficher une chaîne de caractères sur l'écran alpha-numérique du terminal texte qui lui est attaché. Donnez la suite complète des appels de fonctions entre la fonction printf(), exécutée en mode utilisateur par le thread client, et l'appel de la fonction txt_driver_cmd(), exécutée en mode kernel par le thread server. Pour chaque fonction intermédiaire dans cette chaîne d'appels, vous préciserez ce que fait cette fonction, et vous donnerez la signification des arguments.
Last modified 4 years ago Last modified on Jan 25, 2020, 6:27:45 PM