Retrouvez cet article dans : Linux Magazine 85
L’objectif de cette nouvelle rubrique est de vous informer sur les grandes lignes de l’actualité du noyau Linux : le dernier sorti, mais aussi la version en préparation et le décryptage de leurs principales nouveautés.
Nous approfondirons aussi souvent que possible le sujet par quelques brèves expliquant une partie spécifique du noyau, une nouveauté méritant attention...
Bienvenue et bonne lecture !
L’actualité Linux est un sujet très riche et très intéressant, mais long à suivre et difficile à comprendre. Ceux qui ont fait l’expérience de parcourir la source d’information officielle le savent. Cette source est la Linux Kernel Mailing List (ou LKML pour les geeks qui se respectent), c’est-à-dire une liste de diffusion de mails, composée principalement de rapports de bugs, de bouts de codes les corrigeant (patchs), de propositions d’ajout de fonctionnalités et des discussions parfois enflammées s’ensuivant. Le niveau technique est élevé et le trafic important : aux alentours de 8000 mails par mois. Je vous propose donc ici de faire une synthèse des nouveautés majeures du noyau stable actuel, la série 2.6.16.x et un tour d’horizon de ce qui se profile pour le 2.6.17 (qui sera probablement sorti à l’heure où vous lirez ces lignes).
Série 2.6.16 (depuis le 19 mars)
Cette mouture se distingue par l’ajout d’une nouvelle architecture supportée : les processeurs CELL d’IBM. Celui-ci, reposant sur un cœur principal secondé par plusieurs unités de calcul secondaires, équipera par exemple la console Playstation 3.
Du mouvement côté systèmes de fichiers ! On note tout d’abord l’apparition d’un petit nouveau : OCFS (Oracle Clustering File System) est un système de fichiers permettant des accès partagés (plusieurs clients accèdent aux mêmes données physiques). Il est dans ce sens similaire au bien connu NFS, mais a un avantage qui le place plutôt en comparaison avec des systèmes de fichiers comme GFS (Global FileSystem). Il n’y a pas de relation client/serveur central, mais des nœuds accédant tous aux données sur le même support de stockage physique (avec, bien entendu, gestion des accès concurrents). Ceci ne met pas à l’abri d’un crash disque ou du système qui l’héberge, mais permet d’éviter les problèmes pouvant survenir sur la couche serveur des systèmes comme NFS. La différence avec NFS ne s’arrête pas là. Alors que NFS peut être vu comme une couche d’export de données prenant place au-dessus d’un système de fichiers dit « local » (ext2/3, reiser...), OCFS est un système de fichiers local, très proche de ext3 dans sa structure. Ainsi, pour l’utiliser, vous devrez lui réserver une partition dédiée et formatée via les outils spécifiques (ocfsformat...).
Ensuite, le support des droits étendus Access Control List (ACL) POSIX pour les montages CIFS (système de partage de fichiers Microsoft) fait son apparition. Les transferts NFS sont, quant à eux, optimisés, gérant maintenant des blocs de données jusqu’à 1Mo (contre 64K auparavant). Utile avec un serveur NFS adapté (Solaris par exemple).
Ce noyau est le premier à implémenter les « high resolution timers », de nouvelles structures de découpage du temps. Jusqu’alors, il ne pouvait (par défaut) gérer des intervalles inférieurs à 4ms (lié à la fréquence par défaut de l’interruption système, 250Hz), ce qui n’est pas gênant dans la majorité des cas, mais peut le devenir pour des applications temps réel et multimédias exigeantes ou pour certains pilotes.
La nouvelle API système hrtimer, facilement extensible, pourra autoriser de meilleures résolutions et expose des valeurs en nanosecondes. La fonction nanosleep, les itimers et les posix-timers l’exploitent dès à présent, même si elle utilise encore pour l’instant l’interruption système standard (fréquence configurable par les options CONFIG_HZ). De nouveaux pilotes matériels ont été inclus, en particulier pour eagle (modems ADSL USB Sagem et Comtrend), pour les appareils photo numériques Nikon-CoolPix2000 et Olympus MAUSB-10.
On peut noter une nette amélioration de la gestion de l’énergie sur les périphériques SATA, supportant désormais d’être mis en veille ou arrêtés. Vous souhaitez savoir en un coup d’œil si une option est activée dans votre noyau ? Une nouvelle option (KCONFIG_PROC) permet d’afficher l’équivalent du .config, la configuration étant sauvegardée directement dans l’image du noyau :
$ gzip -cd /proc/configfs.gz
Enfin, cette version marque la fin du support de la série 2.9 de GCC. Il faut désormais GCC >= 3.2 pour compiler correctement Linux.
Série 2.6.17
La série 2.6.17 introduit de nouveaux appels système : splice() et tee(). Sans entrer dans des notions qui pourraient être complexes à comprendre, voici l’explication grossière : ces fonctions permettent de transférer des données d’une source vers une/des destination(s) via un tampon noyau et se distinguent d’une copie classique par le fait que les données ne transitent jamais par la mémoire classique (il n’y a pas de copie en espace utilisateur).
C’est donc au niveau des performances que réside tout leur intérêt ; dans leur principe, elles sont directement issues des fameux pipes Unix. splice transmet des données d’un tampon vers un descripteur de fichiers, tandis que tee transfère un buffer vers un ou plusieurs autre(s).
La fonction sendfile(), qui existe depuis longtemps et est également présente sur d’autres OS Unix, a un but similaire, mais fonctionne différemment et est limitée aux fichiers. splice() et tee(), quant à elles, peuvent manipuler des fichiers, des streams ou des sockets, à peu près n’importe quel flot de données donc.
Un transfert via sendfile() pouvant être vu comme un cas particulier de splice(), ce sera désormais un appel à cette dernière qui sera effectué (pour le 2.6.18 ?), représentant la première utilisation concrète de ces nouveaux appels « zero-copy movement ».
Reste ensuite aux applications pouvant en tirer partie à les employer ; pour reprendre un exemple concret donné par Linus, une puce d’encodage vidéo pourrait envoyer son flux simultanément et directement sur le disque pour sauvegarde et sur le réseau pour faire du streaming. D’une manière générale, tout transfert de données auquel un processus utilisateur n’a pas besoin d’accéder peut remplir ces conditions.
Une nouvelle architecture supportée fait son apparition : le Niagara, processeur multi-cœur, à l’instar du CELL. Le Niagara est le dernier-né de chez SUN, c’est une architecture de type SPARC. Il a en plus le bon goût d’être open source.
Si vous utilisez Linux sur des archis de type ARM, n’hésitez pas à consulter les évolutions du noyau 2.6.17 dans ce domaine, elles sont nombreuses (http://www.kernelnewbies.org/LinuxChanges).
Concernant la famille x86, une nouveauté de taille a été implémentée, nommée « SMP alternatives ». Concrètement, cette technique permet au noyau de prendre en charge l’ajout ou le retrait de CPU : tôt lors du boot, le code du noyau se modifie dynamiquement en chargeant depuis un fichier binaire (au format ELF) les fonctionnalités permettant la gestion du SMP.
Ces bouts de code contenant des instructions adaptées sont insérés en mémoire à l’endroit précis qui doit contenir le code UP/SMP si au moins deux CPU sont détectés. Conceptuellement, c’est un patchage automatique de l’image binaire d’un code en cours d’exécution par lui-même.
Le premier bénéfice des « SMP alternatives » est que les distributions Linux n’auront plus à livrer un noyau distinct pour systèmes mono et multiprocesseurs et qu’il n’y aura plus besoin de recompiler le noyau d’un serveur après lui avoir ajouté un deuxième CPU. En se projetant plus loin, on peut également voir l’intérêt de cette fonctionnalité combinée au support du « CPU hotplug » : un processeur ajouté à chaud serait immédiatement pris en compte et utilisé par le système, sans interrompre les processus en cours !
Ceci vaut également pour les environnements virtualisés ; certains (dont Xen) permettant d’affecter à chaud les ressources matérielles aux instances de systèmes virtuels, on peut imaginer de répartir une instance Linux momentanément très chargée sur un ou plusieurs processeurs dédiés : le noyau de l’instance virtualisée les utiliserait alors immédiatement (passage en direct du mode UP au mode SMP) et reviendrait en mode UP, toujours sans interruption, quand l’administrateur du système de virtualisation le déciderait.
Un sujet à suivre donc ! En restant dans le domaine des processeurs, ce concept d’auto-adaptation du code en fonction du contexte pourrait également amener à ne fournir qu’un seul noyau pour plusieurs déclinaisons de CPU : votre distribution, qui livre actuellement un noyau générique fonctionnant sur une grande partie de la famille de votre processeur, vous permet ainsi de l’installer sur une grande variété de systèmes, au prix que les optimisations spécifiques au CPU que vous possédez ne seront pas utilisées. Les alternatives offriraient le choix au noyau de charger les fonctionnalités adaptées à votre processeur ! Pour se la jouer philosophique, seul le futur nous dira ce que nous pouvons attendre des implémentations supplémentaires de « code auto-adaptatif ».
Comme d’habitude, de nouveaux périphériques sont gérés, en particulier les cartes WIFI à base de puces Broadcom BCM43xx. Les possesseurs de nombre de portables PC (Acer, Dell ..) et Mac (Airport Extreme) sauront apprécier cette nouvelle à sa juste valeur.
En outre, une pile SoftMac générique fait son apparition (en attendant Devicescape ?). Les puces graphiques ATI RS350 (circuit vidéo intégré à la carte mère) sont maintenant supportées ; le nouveau pilote Radeon a, quant à lui, été amélioré.
Ceci devrait profiter aux anciennes cartes PCI comme aux nouveaux matériels de type r300, sous condition que le driver côté Xorg évolue.
A noter que l’ancien, déjà marqué obsolète, est désormais retiré du noyau officiel. Les détenteurs de contrôleurs RAID SATA Promise FastTrak TX4300 (puces PDC40719) pourront maintenant l’exploiter sous Linux pour la première fois. Un bond en avant est également fait pour le support de cartes de type DVB (acquisition vidéo numérique).
La partie systèmes de fichiers n’est pas en reste : le RAID5 logiciel permet le reshape, c’est-à-dire l’ajout de volumes, « à chaud » et sans bloquer les opérations de lecture/écriture en cours, y compris lors du sync. A noter que pour en bénéficier, il faudra mettre à jour mdadm.
Pour savoir ce qui se passe sur vos systèmes de fichiers distants et locaux, deux outils de statistiques détaillées font leur apparition : le premier est l’entrée /proc/self/mountstats.
Pour l’instant, il semble qu’il n’y ait que le client NFS qui ait implémenté des statistiques vraiment complètes.
Le second permet de voir en direct ce qui se passe sur vos disques locaux ; il nécessite debugfs (et l’option BLK_DEV_IO_TRACE est à activer dans le noyau) et les outils utilisateur blktrace. Je pense qu’ici un exemple vaut mieux qu’une longue explication.
$ git clone git://brick.kernel.dk/data/git/blktrace.git $ cd blktrace $ make
Passons root, puis activons debugfs si besoin :
# make install # mkdir /debug # mount -t debugfs none /debug/
On lance l’outil btrace sur la partition souhaitée :
# btrace /dev/hda
S’affiche alors en direct la trace de l’activité sur la partition choisie, ce qui ressemble à :
3,0 0 226 15.004000000 0 UT R [swapper] 1 3,0 0 227 15.004000000 7 U R [kblockd/0] 1 3,0 0 241 15.104000000 4004 D WB 112292344+8 [konqueror]
Interrompons la trace avec [CTRL+C] :
CPU0 (3,0): Reads Queued: 0, 0KiB Writes Queued: 55, 304KiB Read Dispatches: 0, 0KiB Write Dispatches: 34, 220KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 0, , 0KiB Writes Completed: 34, 220KiB Read Merges: 0 Write Merges: 21 Read depth: 0 Write depth: 1 IO unplugs: 12 Timer unplugs: 4 Throughput (R/W): 0KiB/s / 14KiB/s Events (3,0): 242 entries Skips: 0 forward (0 - 0.0%)
Les options de l’outil sont nombreuses (cf. README) et très utiles. Côté réseau, Netfilter gère maintenant le connection tracking et la translation d’adresses (NAT) pour les protocoles H323 (utilisé par beaucoup de logiciels de VoIP), H.245 tunnelling et RTP/RTCP.
Ce récapitulatif des deux derniers noyaux est loin d’être exhaustif. Je ne peux que vous inviter à compléter cette lecture par les sites traitant de l’actualité du noyau, tels que L.W.N (www.lwn.net), kernelnewbies (www.kernelnewbies.org), et aussi kerneltrap (www.kerneltrap.org).
Focus sur la pile WIFI Devicescape
La nouvelle a fait beaucoup de bruit : la société Devicescape, auteur d’une pile complète pour périphériques de type 802.11.x, jusqu’alors propriétaire, a décidé de l’offrir dans sa dernière version à la communauté sous licence GPL. Elle intègre les protections MAC, le WEP et le WPA, permet de faire de la QoS, telle que définie par la spécification 802.11 (voix, vidéo, « au mieux » et « en arrière-plan » ). Elle peut notamment piloter les puces Atheros, Broadcom et Intel IXP4xx, matériels dont l’installation n’est pas toujours possible (ou les fonctionnalités incomplètes) actuellement sous Linux. Alors, c’est pour quand dans les noyaux officiels ?
Le débat sur l’état du support du WiFi dans le noyau Linux n’est pas nouveau, l’idée d’intégrer une pile complète pouvant factoriser toutes les fonctionnalités pouvant être requises pour tous les types de chipsets a été l’objet d’un long troll sur la LKML début décembre 2005. Dès le début Jiri Benc, développeur chez SUSE plaide pour l’intégration de celle de Devicescape. Jeff Garzi, l’ancien développeur en charge du WiFi, s’y oppose, préférant une évolution progressive du code existant déjà dans le kernel.
Il se retrouvera progressivement isolé dans sa position. L’intégration dans le noyau officiel ne sera pas immédiate, pour plusieurs raisons : premièrement, il existe déjà des couches WIFI plus ou moins complètes dans le noyau, comme SoftMAC, utilisée pour les puces BroadCom et Prism54.
Les développeurs ne veulent pas multiplier le code redondant. Deuxièmement, il faut l’adapter aux conventions de codage de Linux pour qu’elle puisse être incluse (abandon de l’utilisation de /proc par la pile, déplacement de code en espace utilisateur). De plus, il faudrait la rendre plus modulaire, afin de ne pouvoir charger que les parties utiles à un matériel donné. John Linville, récemment nommé « mainteneur WIFI » a créé une nouvelle branche de test dans lequel il inclut la pile Devicescape.
Un bon signe pour qu’elle soit officiellement intégrée. Mais si celle-ci fournit les principales fonctions requises pour faire fonctionner beaucoup de chipsets (en particulier ceux déléguant des tâches à l’OS et au driver au lieu de les gérer en hardware, cas des chips SoftMAC), il reste toujours la couche qui fait la jonction pile<->matériel à développer spécifiquement pour chaque puce.
Pour tester la branche Linville (les puces de type rt2X00 et bcm43XX utiliseront alors Devicescape) :
$ git clone git://git.kernel.org/pub/scm\ /linux/kernel/git/linville\ /wireless-dev.git
Attention, près de 120Mo seront téléchargés !
$ cd wireless-dev
...et effectuer les classiques :
make [menu/old/g/x]config, make bzImage modules modules_install.
(en choisissant les options relatives à Devicescape et aux chipsets concernés). Il va de soi que tout ceci est hautement expérimental.
Les appels système et l’instruction SYSENTER
Les appels système dans Linux jusqu’à la version 2.5.53 (décembre 2002) utilisaient le chemin des interruptions logicielles.
Lorsqu’un processus en espace utilisateur souhaitait demander un service au noyau, il faisait appel à la libc, cette dernière levait l’interruption 0x80 pour provoquer le passage en ring 0 du processeur (i. e. « mode tout puissant » ou encore « mode noyau » ;) et donner la main au noyau (fonction system_call) pour que celui-ci récupère via les registres généraux le numéro de l’appel système à effectuer, ainsi que ses paramètres.
Lorsque le noyau avait fini son travail, il utilisait l’instruction IRET pour rendre la main au processus en mode utilisateur.
Regardons plus précisément ce qui se passe lors de la levée d’une interruption. Une lecture de l’IDT (Interrupt Descriptor Table) se produit en mémoire pour récupérer l’entrée qui correspond à l’interruption levée.
Elle identifie alors l’entrée dans la GDT (Global Descriptor Table) correspondant au segment de code. Pour le cas des appels système, cette lecture est en fait accélérée par le cache du processeur, puisque c’est toujours la même ligne d’interruption qui est utilisée.
Cependant, des cycles de lecture dans le cache et des vérifications d’autorisation d’accès sont nécessaires à chaque fois qu’un passage en mode noyau via l’interruption 0x80 se produit.
Pour accélérer ce chemin critique, une idée fut que le CPU puisse être hardcodé pour toujours aboutir à un point unique dans le segment de code noyau lors d’un appel système.
Nous évitons ainsi les lectures en mémoire. C’est ce que propose l’instruction SYSENTER depuis le Pentium 2 (et assimilés). L’utilisation de cette instruction pour le passage en mode noyau depuis un appel système entraîne de réelles performances.
Voyons à présent comment s’utilise cette instruction et comment elle est mise en œuvre dans Linux depuis la version 2.5.53 (option configurable du noyau). SYSENTER s’appuie sur des registres spécifiques (MSR, « Model-Specific Register ») pour son fonctionnement : SYSENTER_CS_MSR, SYSENTER_EIP_MSR et SYSENTER_ESP_MSR.
Dans le cas de Linux, ces registres sont chargés lors de l’initialisation du noyau. Le premier contient le sélecteur de segment du code noyau, le deuxième contient l’offset dans le segment de code amenant à la routine de gestion des appels système et le dernier pointe sur la pile à utiliser.
Lorsque l’instruction SYSENTER est exécutée, le processeur effectue les étapes suivantes :
- 1 Il charge les registres
CSetEIPavec les valeurs que nous lui avons fournies. - 2 Il charge le registre
SS(Stack Segment) grâce à la valeur deSYSENTER_CS_MSRauquel il ajoute 8 (ce qui correspond à l’entrée « Kernel Data Segment » de la GDT). - 3 Il charge le registre
ESPavec la valeur que nous lui avons fournie. - 4 Il passe en ring 0.
- 5 Il exécute la fonction noyau correspondante (l’adresse est alors dans
EIP).
__kernel_vsyscall: pushl %ecx pushl %edx pushl %ebp movl %esp, %ebp sysenter
Pour utiliser cette instruction, Linux mappe à l’avant-dernière page de l’espace d’adressage d’un nouveau processus (0xffffe000 sur 32 bits), un binaire ELF contenant le code suivant :
Lors d’un appel système, la libc, après avoir chargé les registres généraux avec le numéro du service noyau et ses paramètres, va effectuer un appel à l’endroit du code précédent (au lieu de lever l’interruption 0x80). Le processeur va donc sauvegarder certains registres sur la pile utilisateur du processus courant (nous allons voir pourquoi), puis exécuter SYSENTER. Le contrôle est alors donné à la fonction noyau chargée de gérer les appels système.
Elle s’occupe en premier lieu de charger dans le registre ESP (contenant jusque-là, la valeur prédéfinie dans SYSENTER_ESP_MSR) l’adresse de la pile noyau associée au processus s’exécutant. Ensuite, elle exécute le service demandé (via un appel dans la syscall table).
Lorsque le service se termine et que le noyau n’a pas d’autre travail en attente (gestionnaire de signal à exécuter par exemple), il est temps de repasser en mode utilisateur.
Pour cela l’instruction SYSEXIT est nécessaire. Elle commence par charger CS avec la valeur de SYSENTER_CS_MSR plus 16 (correspondant à l’entrée « User Code Segment » de la GDT). SS est ensuite chargée par le même type d’opération.
Elle continue en chargeant EIP et ESP avec les valeurs contenues respectivement dans les registres EDX et ECX, pour finalement passer en ring 3 et exécuter le code à l’adresse donnée par EIP.
Quelques explications supplémentaires s’imposent. ECX et EDX sont chargés avant le passage en mode utilisateur par le noyau de façon à ce que lors du SYSEXIT, le flux d’exécution soit bien détourné au début du code suivant (se trouvant un peu après __kernel_vsyscall) :
popl %ebp popl %edx popl %ecx ret
Les valeurs initiales des registres ECX et EDX (pouvant contenir certains paramètres de l’appel système exécuté) sont donc rétablies.
Vous vous demandez certainement pourquoi EBP (pouvant contenir aussi un paramètre) est conservé alors qu’il n’est pas utilisé par SYSEXIT.
En fait, le noyau s’en sert pour conserver l’adresse de la base de la pile noyau associée au processus s’exécutant.
A cet endroit, se trouve une structure d’informations importantes sur ce processus. Il est donc nécessaire de sauvegarder la valeur de EBP avant que le noyau s’amuse avec ;)
Au final, après le RET la libc reprend la main pour ensuite continuer l’exécution du processus.
Pour les lecteurs souhaitant plus d’informations sur ce que fait le noyau après SYSENTER, je vous conseille de regarder dans les sources du noyau le fichier arch/i386/kernel/entry.S à partir de sysenter_entry.


