État des lieux
Processus de PID 1
Sur un système UNIX, le tout premier processus (PID 1) a un rôle particulier :
- c’est le seul qui est lancé par le noyau ; il doit donc assurer toute l’initialisation du système, le lancement des différents services et des systèmes permettant la connexion des utilisateurs ;
- dans la hiérarchie des processus tournant sur le système, lorsqu’un processus devient orphelin, la norme et l’usage veut qu’il soit attaché au processus de PID 1. Dans ce cas lorsque le processus se termine, c’est au PID 1 qu’il revient de lire le code de retour du processus zombie, avant que le système puisse libérer certaines ressources associées.
L’évolution de l’informatique fait que le système évolue au fil du temps (ajout ou suppression de périphériques, lancement ou arrêt de services). Nous avons aussi des plantages de services ou des mises à jour.
SysVinit
Sous GNU/Linux, c’est généralement SysVinit, le système d’initialisation hérité d’UNIX Système V qui assure ce rôle, remplacé par Upstart dans plusieurs distributions.
Hélas, SysVinit reste très basique, peu performant et sujet à de nombreux problèmes. Il n’est pas très adapté à toutes les évolutions modernes que connaît Linux.
Changement dynamique du matériel et du logiciel
Pour l’instant, ces modifications du système sont réalisées par des scripts lancés dynamiquement, généralement par root
ou par un autre service (udev
) ; mais si un système moderne était chargé de maintenir le système opérationnel, il devrait aussi s’occuper de ces modifications.
Le fichier « /etc/inittab »
permet historiquement d’assurer une forme basique de suivi des services et de définir plusieurs modes de fonctionnement ; mais dans les distributions actuelles, il est terriblement en perte de vitesse, toute la charge fonctionnelle étant déplacée dans les scripts SysVinit.
Performances
Lennart Poettering constate que le système d’initialisation SysV est vieux et lent. Pour améliorer les choses, il faudrait :
- démarrer moins de choses ;
- démarrer les choses en parallèle lorsque c’est possible.
Pour démarrer moins de choses, une solution éprouvée est de retarder le démarrage de certains services jusqu’au moment où ils sont effectivement nécessaires (démarrage à la demande). Par exemple, bluetoothd
est inutile tant qu’aucun périphérique Bluetooth n’est présent et qu’aucune application ne s’adresse à lui via D-Bus. Pareillement, CUPS ne sert que pour configurer une imprimante ou pour imprimer, actions qui surviennent rarement dans les premières minutes de démarrage d’un système. Même SSH pourrait n’être démarré qu’au moment où arrive la première connexion entrante.
Pour démarrer les services en parallèle, il est nécessaire de tout lancer en même temps, et non de démarrer les services un à un comme le fait SysVinit.
Quasiment tous les systèmes qui tentent de paralléliser le démarrage de plusieurs services se servent d’un arbre de dépendances permettant d’imposer le lancement des services indispensables au démarrage d’un service donné. Avahi nécessite D-Bus, donc le système démarre D-Bus et attend qu’il soit prêt avant de lancer Avahi.
Paralléliser le lancement des services sur sockets (AF_UNIX)
A ← B (B est dépendant de A)
Cette approche des dépendances est coûteuse en temps, et contournable. Lorsqu’une dépendance existe, le second service (B) n’a pas réellement besoin que l’initialisation du premier service (A) soit terminée pour commencer à démarrer (charger son code en mémoire vive, lire son fichier de configuration…).
En poussant le raisonnement plus loin, la seule chose nécessaire pour ne pas provoquer d’erreurs lors du lancement du second service (B) est que l’interface du premier (A) soit en place et capable de temporiser les requêtes reçues. Le second service (B) pourra alors s’y connecter sans recevoir d’erreur. Le premier service (A) traitera les requêtes lorsqu’il sera prêt.
Dans le cas de services répondant sur des sockets UNIX (AF_UNIX, présents comme des fichiers de type « s » sur le disque), la simple existence du socket est suffisante. Le noyau s’assure que les données reçues sont mises en attente ou que l’émetteur est bloqué tant qu’elles ne sont pas lues. Les services utilisateurs du démon de journalisation d’événements syslog ont besoin de se connecter à un socket appelé « /dev/log »
, ceux de CUPS utilisent « /var/run/cups/cups.sock »
, etc. Si les sockets correspondants existent, alors il est possible de lancer toutes les dépendances, et la synchronisation se fera toute seule au fur et à mesure que les services seront prêts et répondront aux requêtes reçues.
Sur un système UNIX où les entrées‐sorties sont assimilées à des fichiers, il est très facile de créer ces sockets avant même de lancer le service associé.
Supprimer les dépendances explicites entre les services
Si la création des sockets est indépendante du lancement des services, le problème de dépendances disparaît. Dans une première phase, nous pourrions créer tous les sockets nécessaires au système, et dans un second temps, lancer tous les services en parallèle.
Prenons l’exemple de Syslog. Dès l’instant où « /dev/log »
est créé, il est possible de lancer des services qui vont envoyer leurs événements dans « /dev/log »
. Ces messages seront mis en attente tant que Syslog ne sera pas prêt. Dès qu’il aura démarré, Syslog lira tous les messages reçus et assurera leur traitement.
Démarrer moins de choses
Ce principe permet également de faire du démarrage à la demande. Si personne ne s’adresse à un service, il est inutile de le lancer. À l’instar d’inetd (et de son remplaçant xinetd) ; nous pouvons attendre qu’un service ait reçu une demande de connexion pour le lancer. Pour les postes de travail qui impriment trois pages par semaine, CUPS est un bon candidat. Tant qu’aucune impression n’est lancée, aucune donnée n’arrive et le service CUPS n’a pas de raison d’être démarré.
Un système plus résistant
Nous trouvons un nouvel avantage à ce système de démarrage : une certaine forme de résistance aux crashs d’un service. Si votre service syslog
se plante lamentablement, les données qui arrivent dans « /dev/log »
sont mises en attente par le noyau. Lorsque syslog
est relancé, aucun message n’est perdu (si ce n’est ceux qu’il a déjà lus et perdus lors de son crash), même si le temps de démarrage est important.
De la même façon, en cas de redémarrage d’un service, par exemple suite à une mise à jour, aucun message n’est perdu.
Services réseau (AF_INET, AF_INET6 et D-Bus)
Sur un système moderne, les services ne se limitent pas à un socket, ils reçoivent des connexions via le réseau IP ou via D-Bus. Est‐ce que ce mécanisme est aussi adaptable à ce type d’interface ? La réponse est oui.
D-Bus permet un mode « bus-activation » où un service est démarré par la réception d’un message lui étant destiné.
Concernant le réseau IP, c’est exactement ce que propose inetd
et ses successeurs depuis des années (en mode « wait »
). Le socket réseau est créé par inetd
. À la réception de données, le service est lancé déjà connecté au socket. La contrainte est que le service doit être capable de fonctionner sur un socket réseau déjà créé.
Résumé et Interlude
Par ce mécanisme, il est possible de démarrer tous les services en parallèle, sans aucun mécanisme de synchronisation. Il permet également de démarrer les services à la demande et non plus systématiquement.
Ce système n’est pas nouveau, nous avons vu qu’inetd
présentait déjà des aspects similaires. Apple a développé tous les points ci‐dessus dans son système Mac OS X, sous le nom de launchd. C’est cela qui permet à Mac OS X de démarrer très rapidement.
Les systèmes de fichiers
Le démarrage du système ne se limite pas au démarrage des services. La préparation des systèmes de fichiers est également une opération qui peut prendre beaucoup de temps. Il faut attendre que tous les périphériques listés dans « /etc/fstab »
soient créés, que chaque système de fichiers soit vérifié avec fsck, que le système soit monté et éventuellement que les quotas soient contrôlés.
Pour accélérer cela, Harald Hoyer a eu l’idée d’utiliser le système autofs
en mode direct. De la même façon que pour les sockets, autofs
permet de monter un pseudo système de fichiers à la place du système réel, et de ne basculer sur le vrai système qu’au moment où un accès est fait. Comme pour la création de sockets, le montage du pseudo système de fichiers est quasiment instantané.
Si un accès est fait alors que le système de fichiers n’est pas prêt (fsck
en cours, par exemple), alors le noyau bloque la requête (et celui qui l’a faite) jusqu’à ce que le système de fichiers soit disponible.
Pour la racine (« / »
), cela ne présente pas d’intérêt ; mais pour les grosses volumétries qui peuvent être montées sur « /home »
, ou pour des partitions de données qui ne sont pas nécessaires au démarrage du système, cela présente un gain important.
L’intégration de autofs
dans un système de démarrage peut faire peur à plus d’une personne. L’expérience montre que les processus bloqués par des fichiers qui ne sont pas disponibles peuvent être tués proprement, et qu’en cas d’erreur lors de la vérification du système de fichiers, il est possible de retourner une erreur au processus réclamant le fichier, de la même façon que cela se produit lorsqu’une partition tombe en erreur.
Gains fonctionnels
Avoir un petit PID utilisable
Les scripts sont rapides à coder, mais lents à exécuter. Tout le système de démarrage SysVinit est basé sur des scripts qui lancent des commandes externes. D’après Lennart cette approche est condamnée à être lente. Sur son système, les scripts dans « /etc/init.d/ »
appellent grep
au moins 77 fois, awk
l’est 92 fois, cut
, 23 fois, et sed
, 74 fois. À chaque appel, un processus doit être démarré, les bibliothèques résolues, la localisation initialisée, etc.. Après une manipulation de quelques octets, les données doivent être retournées à l’appelant via un descripteur de fichier et le processus est détruit. En outre, les scripts sont très sensibles à l’environnement et peu adaptés à la gestion des erreurs et des exceptions.
Tous ces scripts, ne faisant pour beaucoup que des tâches triviales, et contenant beaucoup de code dupliqué, auraient plutôt leur place dans le processus d’initialisation ou dans l’un de ses sous‐programmes. Tout ré‐écrire en C n’est pas pertinent ; mais le système devrait être capable de gérer bien plus de choses par lui‐même, sans faire appel à des scripts.
Une métrique intéressante est le PID du premier processus utilisable une fois le système démarré. Avec les noyaux actuels, cela donne une bonne idée du nombre de processus lancés. Amorcer le système, lancer un terminal, faire « echo $$ »
: sous Linux, le PID est 1823, sous Mac OS X c’est 154.
Suivre les processus
Une grosse part du mécanisme qui démarre les services devrait être la supervision : surveiller les services, pouvoir les identifier, redémarrer ceux qui plantent, collecter les traces.
Il devrait être capable de couper complètement un service. Cela semble facile mais est en réalité très difficile. Sous UNIX, un processus qui fait un double fork (le programme se relance lui‐même en tâche de fond) sort complètement de la supervision du processus qui l’a lancé. Prenons par exemple un script CGI lancé par un serveur Web. Lorsqu’il fait un double fork, il devient orphelin et est rattaché au processus de PID 1. L’arrêt du serveur Web ne le concerne pas, et aucun script système n’est en mesure de le retrouver automatiquement pour le couper.
Pour résoudre ce problème, le noyau Linux implémente un mécanisme appelé cgroups
(control groups) qui, comme son nom l’indique, permet de créer un groupe et d’y placer des processus. Tout processus créé au sein d’un cgroup
y reste, résolvant ainsi notre problème de CGI fou. Les cgroups
présentent un autre avantage (c’était leur objectif initial) : il est possible de placer certaines restrictions (processeur, mémoire, entrées‐sorties) afin de limiter la consommation d’un service dans sa globalité.
Contrôler le contexte du service
Un bon superviseur devrait également être capable de placer le service dans un environnement adapté et sécurisé. Jetez un coup d’œil aux scripts dans « /etc/init.d/ »
, tout le code dupliqué concerne le traitement des options, le changement d’utilisateur et de groupe, le réglage des paramètres de sécurité, la création d’un environnement avec une racine isolée (à l’aide de chroot
), la création d’un fichier contenant le PID, retrouver le processus pour lui envoyer un signal. Le même code, légèrement différent, se répète de script en script. Tout cela gagnerait à être normalisé et mutualisé.
Le système devrait permettre de régler les limites de ressources, la priorité, l’utilisateur et le groupe. Cela ne s’arrête pas là, le noyau Linux permet aussi de définir les capacités, les règles d’ordonnancement et d’entrées‐sorties, l’affinité avec les processeurs, supprimer l’accès en écriture à certaines partitions, et aussi tous les paramètres liés aux cgroups
.
Enfin, sous UNIX, tout processus a une sortie standard et une sortie d’erreur, qui sont la première source d’information en cas d’anomalie. Aujourd’hui, sur la majorité des distributions, ces données sont difficiles à récupérer, le système de démarrage devrait au contraire les conserver précieusement ; au moins en les redirigeant vers le service de journalisation des événements.
Second interlude : Upstart et les autres systèmes
Upstart est une grosse avancée par rapport à SysVinit. Il s’occupe du démarrage et de l’arrêt des services en fonction d’événements. Il gère tout un arbre de dépendances pour déterminer quoi démarrer et dans quel ordre, afin d’arriver à une situation cible. Malheureusement, cette gestion de dépendances demande de coder manuellement toutes les contraintes d’un service.
Cela a plusieurs effets pervers :
- en cas d’anomalie, bien malin est celui qui retrouvera ce qui s’est produit et dans quel ordre ;
- le travail du système de démarrage a considérablement augmenté, puisqu’avant de démarrer quelque chose, il doit avoir constitué au moins une partie du graphe de dépendances ;
- il ne permet pas de réduire le démarrage aux services strictement nécessaires. Si un service (A) peut être utilisé par un autre (B), alors il doit être marqué comme dépendant et démarré avant le second (A ← B).
Lennart admet que certains de ces points sont en cours de correction, mais il s’agit de contournements du principe de fonctionnement d’Upstart.
En dehors de SysVinit, Upstart et launchd (Mac OS X), un autre système mérite notre intérêt, c’est Solaris SMF. Il gère les dépendances entre les services, mais est très lié à Solaris et très complexe dans son fonctionnement (par exemple par son utilisation excessive de XML).
Systemd
L’implémentation
Ce qui a été décrit ci‐dessus est implémenté dans un système appelé systemd
qui est en cours d’adoption par plusieurs distributions GNU/Linux.
Le fonctionnement de systemd
est basé sur des unités (units) qui ont un nom et un type. Le nom correspond au nom du fichier décrivant l’unité.
Les types sont :
- service : les plus simples correspondent à des services. Peuvent être démarrés, coupés, redémarrés, reconfigurés (start/stop/restart/reload), comme on le fait habituellement avec les scripts dans
/etc/init.d/
;
- socket : correspond à une socket, que ce soit de type UNIX, de type Internet, de type fichier ; chaque socket a un service correspondant ;
- mount : un système de fichiers ; ces unités viennent en plus du fichier
/etc/fstab
qui est utilisé directement ;
- swap : pour les partitions d’échange ;
- automount : un système de fichiers monté à la demande ;
- target : une macro‐unité qui permet de grouper plusieurs unités, par exemple
multi-user.target
ou bluetooth.target
;
- snapshot : comme target, ce sont des unités qui peuvent être utilisées pour sauver l’état actuel des services et les restaurer par la suite, par exemple avant de passer en veille ;
- device : un périphérique dans le système Linux ; peut être une source de dépendance via
udev
;
- timer : activations à la sauce
cron
, en fonction de la date ;
- path : sert pour l’activation basée sur la présence ou la création de fichiers ou répertoires.
Toutes ces unités peuvent avoir des dépendances (Requires) et des conflits (Conflicts) déclarés de façon explicite.
Les bonus
- Pour chaque service, il est possible de régler de nombreux paramètres : limites, répertoire racine (chroot), umask, priorité, paramètre « OOM killer » (out of memory), les classes et les priorités d’ordonnancement et d’entrées‐sorties, l’affinité processeur, l’utilisateur, les groupes, les répertoires accessibles et les modes d’accès, les capacités, le répertoire temporaire, le réglage des
cgroups
…
- Les flux
stdout
et stderr
peuvent être redirigés vers le service de journalisation, vers « /dev/kmsg »
, vers un terminal ou vers un fichier.
- Intégration de SELinux, des « TCP wrappers », des modules d’authentification PAM.
- Chaque service a son propre
cgroup
.
- Chaque connexion via un terminal est aussi placée dans son propre
cgroup
, ce qui permet de fermer complètement une session et d’isoler un peu mieux les utilisateurs (processeur, mémoire).
- La syntaxe des fichiers de configuration suit celle bien connue des fichiers « .desktop ».
- Le système est compatible avec les anciens scripts SysVinit. Les informations LSB et
chkconfig
(de Red Hat) ainsi que celles de Debian et d’OpenSUSE sont utilisées lorsqu’elles sont disponibles. Sinon, l’ordre de démarrage dans « /etc/rc.d/ »
est utilisé. systemd est également capable de lire le fichier PID pour retrouver un service. Ces données permettent d’émuler une configuration classique SysVinit, facilitant la transition.
- Le fichier
« /etc/fstab »
est également lu comme s’il s’agissait d’un fichier de configuration de systemd
.
- Les fichiers natifs
systemd
masquent ces émulations, permettant de faire des paquets compatibles avec les deux systèmes, le nouveau masquant l’ancien si nécessaire.
- Il existe un système de modèles pour éviter de dupliquer une configuration (par exemple, pour les 6
getty
des terminaux virtuels habituellement lancés).
systemd
fourni une couche de compatibilité avec « /dev/initctl »
permettant d’utiliser les vieux binaires, tels que shutdown
ou reboot
. Les ordres sont transmis à systemd
via D-Bus. Il prend en charge également kexec
qui permet de relancer le noyau Linux.
- Il gère
utmp
et wtmp
, les journaux système enregistrant les connexions.
- Chaque processus lancé est tracé par son heure de démarrage, de fin, son PID et son code de retour ; permettant d’analyser les anomalies.
systemd
est capable de se ré‐exécuter lui‐même, afin de faire une mise à jour, ou de passer d’un processus provenant d’un « RAM‐disque » d’amorçage (initrd
) au système final. Tout l’état courant est migré d’un processus à l’autre.
- La mise en place des systèmes de fichiers virtuels est directement prise en charge par
systemd
(/proc
, /sys
, /dev
), binfmt_misc
et HugeTBLfs
sont gérés via autofs
, ce qui évite d’avoir à les configurer en root
ou à charger le module correspondant.
- De nombreuses petites fonctions sont prises en charge directement sans passer par le lancement d’un script : définir le nom de la machine, sauver / restaurer les données aléatoires, initialiser la localisation, nettoyer les répertoires temporaires, configurer la console, le clavier, charger des modules. Cela a pour conséquence d’unifier ces fichiers de configuration sur toutes les distributions utilisant systemd.
- Il existe un mode de démarrage interactif qui permet de confirmer chaque lancement de service en passant au noyau le paramètre
« systemd.confirm_spawn=1 »
.
systemd
gère le readahead
qui permet d’optimiser les lectures disque au démarrage en pré‐chargeant les blocs qui vont être nécessaires par la suite (en se basant sur un enregistrement d’un démarrage précédent).
L’administration
L’administration se fait par l’intermédiaire de quelques commandes dédiées et quelques fichiers de configuration qui sont documentés via une vingtaine de pages de manuel. Un certain nombre de pages liées à l’utilisation de systemd
sont également modifiées.
Définition d’un service
Lorsque l’on regarde les scripts d’initialisation d’un service contenu dans le dossier « /etc/init.d »
, on constate que la plupart des scripts se ressemblent. L’information utile (programme à lancer, description du service, dépendances, niveaux de lancement) est complètement perdue dans le bruit généré par le code de gestion (création d’un fichier PID, s’assurer que le programme ne tourne pas déjà, gestion des cas d’utilisation…).
Systemd intègre toutes ces notions redondantes et permet à l’administrateur de définir un service de façon simple.
[Unit]
Description=ABRT Automated Bug Reporting Tool
After=syslog.target
[Service]
Type=dbus
BusName=com.redhat.abrt
ExecStart=/usr/sbin/abrtd -d -s
[Install]
WantedBy=multi-user.target
Ici, est défini le service ABRT. Comme on le voit, la définition est assez simple :
- le service doit être lancé après syslog ;
- il s’agit d’un service D-Bus répondant au nom « com.redhat.abrt » ;
- la commande à exécuter est :
« /usr/sbin/abrtd -d -s »
;
- le service doit être exécuté lorsqu’on choisi un environnement cible multi‐utilisateur (le mode de fonctionnement habituel).
Regardons un autre exemple ressemblant un peu plus à ce que l’on a l’habitude de voir avec les scripts SysVinit :
[Unit]
Description=Command Scheduler
After=syslog.target
[Service]
EnvironmentFile=/etc/sysconfig/crond
ExecStart=/usr/sbin/crond -n $CRONDARGS
[Install]
WantedBy=multi-user.target
Il est toujours possible de définir un fichier contenant les paramètres à passer au service :
$ cat /etc/sysconfig/crond
## Settings for the CRON daemon.
## CRONDARGS= : any extra command-line startup arguments for crond
CRONDARGS=
Les fichiers de définition peuvent être trouvés dans les dossiers « /etc/systemd/ »
et « /lib/systemd/ »
. Le premier correspond à la configuration faite par l’administrateur, et le second aux fichiers livrés par la distribution.
Gestion des services
Avec systemd
, la gestion des services se fait maintenant par l’utilitaire systemctl
.
Grâce à ce dernier, il est possible de lister toutes les unités présentes sur le système. Les commandes suivantes sont exécutées sur une distribution Fedora 15 :
$ systemctl list-units
NIT LOAD ACTIVE SUB JOB DESCRIPTION
dev-hugepages.automount loaded active waiting Huge Pages File System Automount Point
dev-mqueue.automount loaded active waiting POSIX Message Queue File System Automount Point
proc-sys...misc.automount loaded active running Arbitrary Executable File Formats File System Automount Point
sys-kern...ebug.automount loaded active waiting Debug File System Automount Point
sys-kern...rity.automount loaded active waiting Security File System Automount Point
sys-devi...d-card0.device loaded active plugged N10/ICH 7 Family High Definition Audio Controller
sys-devi...et-eth0.device loaded active plugged BCM4313 802.11b/g LP-PHY
sys-devi...net-em2.device loaded active plugged RTL8101E/RTL8102E PCI Express Fast Ethernet controller
sys-devi...da-sda1.device loaded active plugged Hitachi_HTS725025A9A364
-.mount loaded active mounted /
boot.mount loaded active mounted /boot
home-spack-.gvfs.mount loaded active mounted /home/spack/.gvfs
home.mount loaded active mounted /home
media.mount loaded active mounted Media Directory
mcelog.service loaded active running Machine Check Exception Logging Daemon
mdmonitor.service loaded active running LSB: Start and stop the MD software RAID monitor
netfs.service loaded active exited LSB: Mount and unmount network filesystems.
NetworkManager.service loaded active running Network Manager
nfslock.service loaded active running LSB: Start up the NFS file locking sevice
ntpd.service loaded active running Network Time Service
ntpdate.service loaded failed failed Set time via NTP
udev.service loaded active running udev Kernel Device Manager
dbus.socket loaded active running D-Bus System Message Bus Socket
syslog.socket loaded active running Syslog Socket
systemd-initctl.socket loaded active listening /dev/initctl Compatibility Named Pipe
systemd-logger.socket loaded active running Stdio Syslog Bridge Socket
systemd-shutdownd.socket loaded active listening Delayed Shutdown Socket
udev.socket loaded active running udev Kernel Device Manager Socket
sockets.target loaded active active Sockets
sound.target loaded active active Sound Card
swap.target loaded active active Swap
sysinit.target loaded active active System Initialization
syslog.target loaded active active Syslog
time-sync.target loaded active active System Time Synchronized
systemd-...ead-done.timer loaded active elapsed Stop Read-Ahead Data Collection 10s After Completed Startup
systemd-...es-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
L’affichage a été réduit mais présente les différents éléments pris en compte par systemd
. On retrouve donc les différentes unités précédemment listées.
Vous constaterez que le service « ntpdate.service »
a échoué. Pour en savoir plus, nous pouvons utiliser la commande systemctl
suivie de l’argument « status »
et du nom de notre service :
$ systemctl status ntpdate.service
ntpdate.service - Set time via NTP
Loaded: loaded (/lib/systemd/system/ntpdate.service)
Active: failed since Mon, 01 Aug 2011 16:01:49 +0200; 1h 58min ago
Process: 741 ExecStart=/etc/init.d/ntpdate start (code=exited, status=1/FAILURE)
CGroup: name=systemd:/system/ntpdate.service
On voit alors la commande exécutée et le code de retour renvoyé par le service : 1. Un coup d’œil au fichier « /var/log/messages »
nous en dira plus :
# grep ntpdate /var/log/messages
Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 0.fedora.pool.ntp.org: Name or service not known (-2)
Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 1.fedora.pool.ntp.org: Name or service not known (-2)
Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 2.fedora.pool.ntp.org: Name or service not known (-2)
Aug 1 16:01:49 spiro ntpdate[1654]: Can't find host 3.fedora.pool.ntp.org: Name or service not known (-2)
Aug 1 16:01:49 spiro ntpdate[1654]: no servers can be used, exiting
Aug 1 16:01:49 spiro systemd[1]: ntpdate.service: main process exited, code=exited, status=1
Aug 1 16:01:49 spiro systemd[1]: Unit ntpdate.service entered failed state.
À l’aide de systemctl
, il est bien sûr possible de démarrer, stopper et redémarrer les services. Voici, par exemple, comment l’on redémarre le service de prise en charge du Bluetooth :
# systemctl restart bluetooth.service
cgroups
Nous avons vu que systemd
place chaque service dans un groupe distinct. Il serait donc intéressant d’identifier les différents groupes et les processus qu’ils contiennent.
Pour cela, deux possibilités. Utiliser l’utilitaire ps
ou l’outil maison de systemd
: « systemd-cgls »
.
$ ps waxf -eo 'user,pid,cgroup,command'
USER PID CGROUP COMMAND
root 1 name=systemd:/system /sbin/init
root 367 cpu:/system/systemd-logger. /lib/systemd/systemd-logger
root 390 cpu:/system/udev.service;na /sbin/udevd
root 544 cpu:/system/udev.service;na \_ /sbin/udevd
root 1450 cpu:/system/udev.service;na \_ /sbin/udevd
root 685 cpu:/system/abrtd.service;n /usr/sbin/abrtd -d -s
root 694 cpu:/system/crond.service;n /usr/sbin/crond -n
root 717 cpu:/system/rsyslog.service /sbin/rsyslogd -n -c 5
root 1086 cpu:/system/autofs.service; automount --pid-file /var/run/autofs.pid
root 1127 cpu:/system/prefdm.service; /usr/sbin/gdm-binary -nodaemon
root 1135 cpu:/system/prefdm.service; \_ /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1
root 1139 cpu:/system/prefdm.service; \_ /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-GWn7Er/database -n
root 1352 name=systemd:/user/spack/1 \_ pam: gdm-password
spack 1374 name=systemd:/user/spack/1 \_ gnome-session
spack 1526 name=systemd:/user/spack/1 \_ /usr/libexec/gnome-settings-daemon
spack 1562 name=systemd:/user/spack/1 \_ /usr/bin/gnome-shell
spack 1786 name=systemd:/user/spack/1 | \_ gnome-terminal
spack 1791 name=systemd:/user/spack/1 | \_ gnome-pty-helper
spack 1792 name=systemd:/user/spack/1 | \_ bash
spack 1810 name=systemd:/user/spack/1 | \_ bash
spack 1823 name=systemd:/user/spack/1 | \_ ps waxf -eo user,pid,cgroup,command
spack 1569 name=systemd:/user/spack/1 \_ gnome-power-manager
spack 1574 name=systemd:/user/spack/1 \_ nm-applet
spack 1575 name=systemd:/user/spack/1 \_ abrt-applet
spack 1578 name=systemd:/user/spack/1 \_ gnome-screensaver
root 1151 cpu:/system/getty@.service/ /sbin/agetty tty2 38400
root 1152 cpu:/system/getty@.service/ /sbin/agetty tty6 38400
root 1155 cpu:/system/getty@.service/ /sbin/agetty tty4 38400
root 1159 cpu:/system/getty@.service/ /sbin/agetty tty3 38400
root 1160 cpu:/system/getty@.service/ /sbin/agetty tty5 38400
$ systemd-cgls
├ user
│ └ spack
│ └ 1
│ ├ 1352 pam: gdm-password
│ ├ 1374 gnome-session
│ ├ 1383 dbus-launch --sh-syntax --exit-with-session
│ ├ 1384 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
│ ├ 1445 /usr/libexec/gvfsd
│ ├ 1521 /usr/libexec/gconfd-2
│ ├ 1526 /usr/libexec/gnome-settings-daemon
│ ├ 1529 /usr/bin/pulseaudio --start
│ ├ 1562 /usr/bin/gnome-shell
│ ├ 1569 gnome-power-manager
│ ├ 1574 nm-applet
│ ├ 1575 abrt-applet
│ ├ 1578 gnome-screensaver
│ ├ 1583 /usr/libexec/dconf-service
│ ├ 1786 gnome-terminal
│ ├ 1791 gnome-pty-helper
│ ├ 1792 bash
│ └ 1809 systemd-cgls
└ system
├ 1 /sbin/init
├ getty@.service
│ ├ tty5
│ │ └ 1160 /sbin/agetty tty5 38400
│ ├ tty3
│ │ └ 1159 /sbin/agetty tty3 38400
│ ├ tty4
│ │ └ 1155 /sbin/agetty tty4 38400
│ ├ tty6
│ │ └ 1152 /sbin/agetty tty6 38400
│ └ tty2
│ └ 1151 /sbin/agetty tty2 38400
├ prefdm.service
│ ├ 1127 /usr/sbin/gdm-binary -nodaemon
│ ├ 1135 /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1
│ ├ 1139 /usr/bin/Xorg :0 -background none -verbose -auth /var/run/gdm/auth-for-gdm-GWn7Er/database -nolisten tcp vt1
│ └ 1363 /usr/bin/gnome-keyring-daemon --daemonize --login
├ autofs.service
│ └ 1086 automount --pid-file /var/run/autofs.pid
├ dbus.service
│ ├ 707 /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
│ ├ 720 /usr/libexec/polkit-1/polkitd
│ ├ 750 /usr/sbin/modem-manager
│ ├ 816 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplic...
│ ├ 1037 /usr/libexec/colord
│ ├ 1282 /usr/libexec/udisks-daemon
│ ├ 1283 udisks-daemon: not polling any devices
│ ├ 1288 /usr/libexec/upowerd
│ └ 1559 /usr/libexec/packagekitd
├ crond.service
│ └ 694 /usr/sbin/crond -n
├ rsyslog.service
│ └ 717 /sbin/rsyslogd -n -c 5
├ abrtd.service
│ └ 685 /usr/sbin/abrtd -d -s
├ home.mount
├ boot.mount
├ fsck@.service
├ udev.service
│ ├ 390 /sbin/udevd
│ ├ 544 /sbin/udevd
│ └ 1450 /sbin/udevd
├ systemd-logger.service
│ └ 367 /lib/systemd/systemd-logger
└ media.mount
Vous l’aurez constaté, systemd
gère non seulement les ressources du système, mais aussi celles des utilisateurs.
Écrire un service
L’utilisation de systemd
simplifie un certain nombre de choses dans l’écriture d’un service :
- il n’est plus nécessaire de se relancer pour passer en arrière‐plan (fork), et encore moins de le faire deux fois (double‐fork) ;
- il n’est plus nécessaire d’implémenter de mécanisme pour changer d’utilisateur, pour modifier les privilèges, pour faire un
chroot
; tout cela étant pris en charge par systemd
;
- avec les
cgroups
, il n’est plus nécessaire d’écrire un fichier PID permettant de retrouver le service via son identifiant de processus ;
- pour les cas simples, il n’est plus nécessaire de mettre en place une interface vers le service de journalisation des événements, les sorties classiques étant gérées comme des journaux (cela ne permet toutefois pas de spécifier la sévérité) ;
- il n’est plus nécessaire de configurer et de créer les sockets.
Ces recommandations sont très semblables à celles d’Apple pour utiliser launchd
. Pour les services écrits de façon classique, systemd
dispose de mécanismes de compatibilité afin de pouvoir les démarrer selon l’ancienne mode. Enfin, il est tout à fait possible — et recommandé — d’écrire des services qui fonctionnent dans tous les modes (autonomes ou systemd
), évitant de produire une version spéciale ou de créer une branche.
systemd
vient avec un kit d’intégration en C, qui facilite l’intégration dans un service. Lennart a publié deux notes de documentation et une page de manuel très complète :
Conclusion
Au travers de cet article vous a été présenté un sous‐ensemble des fonctionnalités offertes par systemd
. Il repense totalement la gestion des processus des systèmes basés sur le noyau Linux, et utilise pleinement les technologies offertes par ce dernier.
Plusieurs distributions intègrent ou vont intégrer systemd
. D’après l’article concernant systemd sur Wikipédia, il est utilisé depuis Fedora 15, et est optionnel dans Fedora 14. openSUSE prévoyait de l’intégrer dans la version 11.4, mais cela semble être repoussé à la 12.1. Mandriva 2011 est prévue avec. Arch Linux a un paquet expérimental. Debian a un paquet dans « unstable » et devrait permettre de choisir un système ou l’autre dans une prochaine version. Gentoo rend systemd
accessible dans « testing ». Le grand absent est Ubuntu, qui a beaucoup investi dans Upstart et n’a pas manifesté, pour l’instant, de volonté de passer à systemd
(bien qu’un début d’intégration existe).
Cet outil à tout faire, semble faire un pied de nez à la philosophie d’UNIX en combinant plusieurs fonctions en un seul programme. D’un autre côté, le système de démarrage actuel montre ses limites.
La gestion de l’initialisation est très importante pour le système. On peut donc se demander s’il est judicieux d’inclure tant de fonctionnalités dans un programme critique ? Cependant, avec les contraintes de performances et la complexité des systèmes actuels, n’est‐ce pas un mal nécessaire pour gérer efficacement les processus exécutés sur nos systèmes ?