URL: https://linuxfr.org/news/evolutions-techniques-de-systemd Title: Évolutions techniques de systemd Authors: Sébastien Koechlin Date: 2011年07月29日T10:47:21+02:00 License: CC By-SA Tags: systemd, admin, démarrage, dbus, troll, init et opensuse Score: 144 _LinuxFr.org_ a déjà publié quelques articles à propos de **systemd**, sans entrer trop dans les détails des améliorations techniques. On trouve en particulier un [entretien avec son auteur, Lennart Poettering](http://linuxfr.org/news/un-entretien-avec-lennart-poettering), et un [journal contestant la qualité et les dépendances du code](http://linuxfr.org/users/rewind/journaux/systemd-est-un-bloat). L’arrivée de **systemd** provoque pas mal de remous, justifiés ou non. On peut citer l’objectif _« Linux only »_ affiché par l’auteur, les multiples dépendances et en particulier celle de [[D-Bus]], la personnalité de l’auteur et la qualité de ses réalisations précédentes, le périmètre de responsabilité de `systemd` (gdm) et probablement de nombreux autres points. Cet article a pour objectif de passer en revue les évolutions techniques et les objectifs de `systemd`. Les autres questions citées ci‐dessus ne sont pas injustifiées (en tout cas, pas toutes), mais sont en dehors du périmètre fixé. L’article se base essentiellement sur les présentations de Lennart Poettering publiées sur [son site](http://0pointer.de/blog/projects/systemd-docs.html) en particulier, certains paragraphes sont des traductions un peu condensées de sa [présentation initiale](http://0pointer.de/blog/projects/systemd.html). _Merci aux relecteurs : Davy, Spack, npa._ ---- [Page du projet systemd](http://www.freedesktop.org/wiki/Software/systemd) [En savoir plus sur systemd](http://0pointer.de/blog/projects/systemd-docs.html) [Comparatif de SysVinit, Upstart et Systemd par Lennart Poettering](http://0pointer.de/blog/projects/why.html) ---- # État des lieux ## Processus de PID 1 Sur un système UNIX, le tout premier processus ([PID](http://fr.wikipedia.org/wiki/Identifiant_de_processus) 1) a un rôle particulier : 1. 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 ; 1. 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_](http://fr.wikipedia.org/wiki/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](http://fr.wikipedia.org/wiki/Init#.22init.22_de_Unix_System_V_.28SysVinit.29), le système d’initialisation hérité d’[UNIX Système V](http://fr.wikipedia.org/wiki/UNIX_System_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](http://fr.wikipedia.org/wiki/Berkeley_sockets) (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](https://secure.wikimedia.org/wikipedia/fr/wiki/Daemon_%28informatique%29) 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_](http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-starting-services) » 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](http://launchd.macosforge.org/). 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](http://www.harald-hoyer.de/) a eu l’idée d’utiliser le système [`autofs`](http://www.autofs.org/) 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`](http://www.mjmwired.net/kernel/Documentation/cgroups.txt) (_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`](http://fr.wikipedia.org/wiki/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](http://en.wikipedia.org/wiki/Service_Management_Facility). 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](http://linux-mm.org/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](http://en.wikipedia.org/wiki/Tcp_wrappers) », des modules d’authentification [PAM](http://fr.wikipedia.org/wiki/Pluggable_Authentication_Modules). * 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](http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html) ». * 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`](http://fr.wikipedia.org/wiki/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](http://fr.wikipedia.org/wiki/Ramdisk) » 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`](http://en.wikipedia.org/wiki/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`](http://en.wikipedia.org/wiki/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](http://0pointer.de/public/systemd-man/). 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. ```ini [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](https://fedorahosted.org/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 : ```ini [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 : ```console $ 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`](http://0pointer.de/public/systemd-man/systemctl.html). 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 : ```console $ 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 : ```console $ systemctl status ntpdate.service ntpdate.service - Set time via NTP Loaded: loaded (/lib/systemd/system/ntpdate.service) Active: failed since 2011年8月01日 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 : ```console # 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 : ```console # 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`](http://fr.wikipedia.org/wiki/Ps_%28Unix%29) ou l’outil maison de `systemd` : `« systemd-cgls »`. ```console $ 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 ``` ```console $ 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 : * une note pour expliquer pas à pas [les modifications nécessaires du code et de la configuration](http://0pointer.de/blog/projects/socket-activation.html) ; * une note décrivant un [cas pratique avec CUPS](http://0pointer.de/blog/projects/socket-activation2.html) ; * la page de manuel « [daemon (7)](http://0pointer.de/public/systemd-man/daemon.html) ». # 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](http://en.wikipedia.org/wiki/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](http://fr.wikipedia.org/wiki/Arch_Linux "Définition Wikipédia") a un paquet expérimental. [[Debian]] a un paquet dans _« unstable »_ et devrait permettre de [choisir un système ou l’autre](http://wiki.debian.org/systemd) 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](https://wiki.ubuntu.com/systemd) existe). Cet outil à tout faire, semble faire un pied de nez à la [philosophie d’UNIX](http://fr.wikipedia.org/wiki/Philosophie_d%27Unix) 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 ?