Depuis 2003, plusieurs ordonnanceurs se sont succédé dans le noyau Linux 2.6. Le dernier en date (apparu dans Linux 2.6.24) est le « CFS – Complete Fair Scheduler » autrement dit « l’ordonnanceur vraiment équitable ». Celui-ci garantit que le temps CPU disponible est réparti de manière équitable entre les différentes tâches. Ceci ne concerne naturellement que les tâches ordonnancées en temps-partagé, celles s’exécutant en temps-réel disposent de tout le temps-processeur qu’elles désirent (voir toutefois cet article…)
Pour améliorer le temps de réponse des applications interactives (notamment l’interface utilisateur graphique), ce scheduler propose un partage du temps CPU entre les groupes de processus. Supposons que nous ayons un premier groupe de dix tâches réalisant des calculs intensifs (la compilation d’un projet important comme le noyau Linux). Le temps processeur sera alors réparti en dix tranches de 10 % (en ignorant les quelques pourcents indispensables pour faire fonctionner les autres processus du système). Si nous ajoutons à présent un second groupe de trois processus de calcul, la répartition se fera ainsi :
- 5 % pour chacun des trois processus du premier groupe qui totalise donc 50% ;
- 16,6 % pour chacun des trois processus du second groupe, qui reçoit les 50% restants.
Ce mécanisme, très pratique, est malheureusement assez compliqué à mettre en œuvre, et doit être explicitement activé lors de la création des tâches à placer dans les groupes.
Dans le noyau 2.6.38 – publié récemment comme je l’avais signalé dans un précédent article – est apparu un nouvel élément de configuration nommé CONFIG_SCHED_AUTOGROUP
. Celui-ci est accessible dans le menu « General Setup » de l’interface de configuration du kernel (make menuconfig
, make xconfig
, ou make gconfig
) sous la dénomination « Automatic process group scheduling » .
Avec l’auto-groupement des tâches, l’ordonnanceur prend automatiquement en considération le terminal de contrôle du processus. Le temps CPU est réparti équitablement entre les terminaux, quelque soit le nombre de tâches sur chaque terminal.
Pour tester cette fonctionnalité, nous allons utiliser le petit programme suivant, qui lance en parallèle le nombre de processus demandé sur sa ligne de commande. Chaque processus consomme, dans une boucle active, tout le temps CPU qui lui est offert :
boucles-actives.c : #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char * argv[]) { int nb; if ((argc != 2) || (sscanf(argv[1], "%d", & nb) != 1)) { fprintf(stderr, "usage: %s n", argv[0]); exit(1); } while (nb > 0) { if (fork() == 0) while (1) ; nb --; } return 0; }
Ouvrons un terminal et lançons un premier jeu de processus. Nous observons que la charge CPU monte en flèche.
Basculons alors sur un autre bureau virtuel et ouvrons un second terminal. Première surprise : la fluidité de l’interface graphique. Les applications de l’environnement graphique (Gnome sur ce poste) n’ont pas de terminaux de contrôle et appartiennent donc à un groupe différent de celui des processus en boucle. Le temps processeur dont elles ont besoin est donc réservé et nous n’observons plus de saccades dans la gestion de la souris par exemple comme ce pouvait être le cas précédement. Ce point a d’ailleurs été longuement commenté suite à un message enthousiaste de Linus Torvalds en novembre dernier lorsqu’il s’est aperçu que son interface graphique conservait toute sa réactivité, même durant une compilation largement parallélisée du noyau.
Ouvrons un second terminal et lançons une deuxième série de processus en boucle :
Puis en basculant sur un troisième terminal, observons les répartitions de temps processeur (sur cette machine, le processeur dispose de deux cœurs, aussi la somme des temps CPU est de 200%) :
$ ps axu USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 2876 1332 ? Ss Mar29 0:01 /sbin/init root 2 0.0 0.0 0 0 ? S Mar29 0:00 [kthreadd] [...] cpb 7413 10.1 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7414 10.3 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7415 10.0 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7416 10.3 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7417 10.3 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7418 10.3 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7419 10.3 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7420 10.1 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7421 10.1 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7422 10.1 0.0 1864 48 pts/0 R 08:24 0:07 ./boucles-actives 10 cpb 7425 23.4 0.0 1864 48 pts/1 R 08:24 0:15 ./boucles-actives 4 cpb 7426 24.0 0.0 1864 48 pts/1 R 08:24 0:16 ./boucles-actives 4 cpb 7427 23.7 0.0 1864 48 pts/1 R 08:24 0:16 ./boucles-actives 4 cpb 7428 23.4 0.0 1864 48 pts/1 R 08:24 0:15 ./boucles-actives 4 cpb 7456 0.0 0.0 4764 988 pts/2 R+ 08:25 0:00 ps axu $
Nous voyons bien que les dix processus issus de la commande ‘boucles-actives 10
» disposent chacun de 10 % du temps CPU, tandis que ceux lancés par la commande « boucles-actives 4
» profitent d’environ 25 % chacun. Pour voir les groupes de processus, nous pouvons utiliser la commande suivante :
$ ps ax -O pgrp PID PGRP S TTY TIME COMMAND 1 1 S ? 00:00:01 /sbin/init 2 0 S ? 00:00:00 [kthreadd] [...] 7413 7412 R pts/0 00:00:15 ./boucles-actives 10 7414 7412 R pts/0 00:00:14 ./boucles-actives 10 7415 7412 R pts/0 00:00:14 ./boucles-actives 10 7416 7412 R pts/0 00:00:14 ./boucles-actives 10 7417 7412 R pts/0 00:00:14 ./boucles-actives 10 7418 7412 R pts/0 00:00:14 ./boucles-actives 10 7419 7412 R pts/0 00:00:14 ./boucles-actives 10 7420 7412 R pts/0 00:00:14 ./boucles-actives 10 7421 7412 R pts/0 00:00:14 ./boucles-actives 10 7422 7412 R pts/0 00:00:14 ./boucles-actives 10 7425 7424 R pts/1 00:00:32 ./boucles-actives 4 7426 7424 R pts/1 00:00:34 ./boucles-actives 4 7427 7424 R pts/1 00:00:33 ./boucles-actives 4 7428 7424 R pts/1 00:00:34 ./boucles-actives 4 7477 7477 R pts/2 00:00:00 ps ax -O pgrp $
Le numéro de groupe est indiqué en seconde colonne, le groupe 7412 contient les dix processus ayant chacun 10 % du temps CPU et le groupe 7424 est constitué des quatre processus avec 25 % de temps CPU chacun. La nouveauté du noyau 2.6.38 est d’organiser automatiquement ces groupes en se basant sur le terminal de contrôle (pts/0
dans un cas et pts/1
dans l’autre).
En conclusion, je ne peux que saluer cette nouveauté du noyau 2.6.38. Elle est particulièrement utile pour conserver une bonne réactivité à un système sur lequel de nombreuses tâches de calcul s’exécutent. L’emploi du terminal de contrôle des processus limite toutefois la portée de cette innovation, car la plupart des utilisateurs de Linux exécutent des applications purement graphiques (navigateurs, players vidéo, etc.) dans des environnements (Gnome, Kde, Xfce…) où la notion de terminal est absente. Ces processus sont présentés avec un point d’interrogation dans la colonne TTY
de ps
. Les principaux bénéficiaires seront plutôt les utilisateurs qui lancent régulièrement des tâches consommatrices de CPU (compilation, traitement vidéo, calculs…) depuis des terminaux shell.