{"id":6056,"date":"2024-02-19T10:39:52","date_gmt":"2024-02-19T09:39:52","guid":{"rendered":"https:\/\/www.blaess.fr\/christophe\/?p=6056"},"modified":"2024-03-12T17:53:23","modified_gmt":"2024-03-12T16:53:23","slug":"6056","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2024\/02\/19\/6056\/","title":{"rendered":"Early-init, les premiers instants du r\u00e9veil de Linux"},"content":{"rendered":"\n\n<div class=\"wp-block-image\">\n<figure class=\"alignright size-full\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/Sleepy-Tux-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"222\" height=\"261\" class=\"wp-image-6065\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/Sleepy-Tux-2.png\" alt=\"\" \/><\/a><\/figure><\/div>\n\n\n<p>Dans beaucoup de syst\u00e8mes embarqu\u00e9s, il est important de r\u00e9aliser certaines t\u00e2ches au d\u00e9marrage le plus rapidement possible et dans un ordre pr\u00e9cis. Citons par exemple l&rsquo;affichage d&rsquo;un \u00e9cran <em>splashscreen<\/em>, le montage d&rsquo;une partition de donn\u00e9es, l&rsquo;initialisation des interfaces r\u00e9seau, ou la mise \u00e0 l&rsquo;heure du syst\u00e8me.<\/p>\n\n\n\n<p>Or les syst\u00e8mes d&rsquo;initialisation modernes, comme <code><a href=\"https:\/\/fr.wikipedia.org\/wiki\/Systemd\" target=\"_blank\" rel=\"noreferrer noopener\">systemd<\/a><\/code>, sont pr\u00e9vus pour fonctionner sur des serveurs ou des postes de travail, sur lesquels l&rsquo;ordre d&rsquo;ex\u00e9cution et la rapidit\u00e9 des t\u00e2ches d&rsquo;initialisation ne sont pas primordiaux.<\/p>\n\n\n\n<p>Il est facile de r\u00e9soudre ce probl\u00e8me avec un petit script, nomm\u00e9 <code>early-init<\/code>, que je souhaite vous pr\u00e9senter ici.<\/p>\n\n\n\n<p>\n<!--more-->\n<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Objectifs<\/h2>\n\n\n\n<p>L&rsquo;id\u00e9e de ce mini-projet (<a href=\"https:\/\/github.com\/cpb-\/early-init\/\" target=\"_blank\" rel=\"noreferrer noopener\">disponible ici<\/a>) d\u00e9velopp\u00e9 avec mon coll\u00e8gue Alexandre Grosset est de disposer d&rsquo;un premier syst\u00e8me d&rsquo;<em>init<\/em>, inspir\u00e9 de <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Init\" target=\"_blank\" rel=\"noreferrer noopener\">SysVinit<\/a>, qui permet de lancer des scripts dans un ordre d\u00e9fini pour r\u00e9aliser les t\u00e2ches urgentes, puis d&rsquo;appeler ensuite le syst\u00e8me d&rsquo;<em>init<\/em> normal pour terminer le <em>boot<\/em> et lancer le code applicatif. Dans des syst\u00e8mes embarqu\u00e9s avec un code applicatif un peu complexe, l&rsquo;utilisation de <code>systemd<\/code> peut s&rsquo;av\u00e9rer indispensable pour g\u00e9rer les d\u00e9pendances et d\u00e9marrer les services n\u00e9cessaires (D-bus, etc.)<\/p>\n\n\n\n<p>Le script <code>early-init<\/code> lui m\u00eame doit faire \u00e0 peine cinquante lignes de code, mais il me permet de formaliser le m\u00e9canisme d&rsquo;ex\u00e9cution de ces t\u00e2ches \u00e0 faire au tout d\u00e9but du <em>boot<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n\n\n\n<p>Le principe est de demander au kernel de lancer <code>\/sbin\/early-init<\/code> au lieu du <code>\/sbin\/init<\/code> d&rsquo;origine gr\u00e2ce \u00e0 la directive <code>init=<\/code> sur la ligne de param\u00e8tres du noyau. Une fois que notre script aura ex\u00e9cut\u00e9 toutes les t\u00e2ches d\u00e9sir\u00e9es, il s&rsquo;occupera de lancer le processus <code>\/sbin\/init<\/code> normal. L&rsquo;enchainement des op\u00e9rations est r\u00e9sum\u00e9 sur la figure suivante.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"191\" class=\"wp-image-6069\" style=\"width: 840px; height: auto;\" src=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse-1024x191.png\" alt=\"\" srcset=\"https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse-1024x191.png 1024w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse-300x56.png 300w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse-768x144.png 768w, https:\/\/www.blaess.fr\/christophe\/wp-content\/uploads\/2024\/02\/early-init-timelapse.png 1327w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Pour ajouter l&rsquo;option <code>init=\/sbin\/early-init<\/code> sur la ligne de param\u00e8tre du noyau, je pr\u00e9f\u00e8re la solution simple qui consiste \u00e0 ajouter les lignes suivantes sous forme de fragment de configuration pour le <em>kernel<\/em> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CONFIG_CMDLINE=\"init=\/sbin\/early-init\"\nCONFIG_CMDLINE_EXTEND=y\n# CONFIG_CMDLINE_FROM_BOOTLOADER is not set<\/code><\/pre>\n\n\n\n<p>Comme cette solution ne fonctionne pas pour toutes les architectures, une autre possibilit\u00e9 consiste \u00e0 modifier la variable du <em>bootloader<\/em> qui contient les arguments pass\u00e9s au noyau (<code>bootargs<\/code> pour U-boot par exemple).<\/p>\n\n\n\n<p>Une recette (\u00e0 personnaliser) pour Yocto Project est pr\u00e9sente dans un sous-r\u00e9pertoire du d\u00e9p\u00f4t de <code>early-init<\/code>, une recette pour Buildroot sera ajout\u00e9e prochainement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">T\u00e2ches<\/h2>\n\n\n\n<p>Les t\u00e2ches que <code>early-init<\/code> lance se trouvent dans le r\u00e9pertoire <code>\/etc\/early-init.d\/<\/code>. Les t\u00e2ches sont appel\u00e9es dans l&rsquo;ordre alphanum\u00e9rique, aussi est-il utile de les num\u00e9roter pour avoir un ordre pr\u00e9cis d&rsquo;ex\u00e9cution. Les t\u00e2ches se d\u00e9roulent les unes apr\u00e8s les autres. Si on souhaite r\u00e9aliser des traitements en parall\u00e8le, il faudra le g\u00e9rer explicitement (par exemple avec des <code>&amp;<\/code> et des <code>wait<\/code> si la t\u00e2che est un script shell, ou avec des threads si la t\u00e2che est un programme plus \u00e9labor\u00e9).<\/p>\n\n\n\n<p>Une fois toutes les t\u00e2ches ex\u00e9cut\u00e9es, <code>early-init<\/code> (contrairement aux syst\u00e8mes d&rsquo;initialisation classiques) passe compl\u00e8tement la main au processus <code>init<\/code> original. Il ne reprend plus jamais le contr\u00f4le, notamment au moment de l&rsquo;arr\u00eat du syst\u00e8me via un <code>halt<\/code> ou un <code>shutdown<\/code> (qui par ailleurs se produisent rarement dans le domaine de l&#8217;embarqu\u00e9). Les t\u00e2ches ne sont donc jamais rappel\u00e9es une fois le <em>boot<\/em> termin\u00e9. Elles ne re\u00e7oivent pas d&rsquo;argument <code>start<\/code> ou <code>stop<\/code> comme c&rsquo;est le cas avec <em>SysVinit<\/em> et il n&rsquo;est pas non plus n\u00e9cessaire de pr\u00e9fixer les t\u00e2ches par un S (ou un K) comme avec les m\u00e9canismes d&rsquo;initialisation classiques.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Id\u00e9es de t\u00e2ches<\/h2>\n\n\n\n<p>Voici quelques id\u00e9es de t\u00e2ches que j&rsquo;ai d\u00e9velopp\u00e9es pour des projets&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Une t\u00e2che qui n&rsquo;est ex\u00e9cut\u00e9e qu&rsquo;au premier <em>boot<\/em>, juste apr\u00e8s le flashage de l&rsquo;image, en usine. Cette t\u00e2che peut remonter le <em>rootfs<\/em> en lecture-\u00e9criture pour enregistrer le num\u00e9ro de s\u00e9rie du produit, g\u00e9n\u00e9rer une cl\u00e9 SSH, stocker des certificats et des fichiers d&rsquo;options pour les applicatifs, etc. Une fois ces op\u00e9rations r\u00e9alis\u00e9es, ce script s&rsquo;auto-efface avant de remonter le <em>rootfs<\/em> en lecture-seule.<\/li>\n\n\n\n<li>Montage d&rsquo;une partition de donn\u00e9es accessible en lecture-\u00e9criture avec v\u00e9rification de la fiabilit\u00e9 de la partition, et dans le pire des cas reformatage de la partition si son syst\u00e8me de fichiers est incoh\u00e9rent.<\/li>\n\n\n\n<li>Montage d&rsquo;un <em>overlay<\/em> sur <code>\/etc<\/code> (qui se trouve sur le <em>rootfs<\/em> en lecture seule), le contenu de l&rsquo;<em>overlay<\/em> \u00e9tant stock\u00e9 sur la partition de donn\u00e9es ci-dessus. Ceci permet de modifier dynamiquement la configuration du syst\u00e8me (param\u00e8tres r\u00e9seau par exemple).<\/li>\n\n\n\n<li>Initialisation des interfaces r\u00e9seau, lecture de l&rsquo;horloge RTC, consultation \u00e9ventuelle d&rsquo;un serveur NTP&#8230;<\/li>\n\n\n\n<li>V\u00e9rification du fonctionnement des composants mat\u00e9riels n\u00e9cessaires pour l&rsquo;applicatif et d\u00e9clenchement d&rsquo;un mode d\u00e9grad\u00e9 en cas de d\u00e9faut.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Ce petit projet est extr\u00eamement simple mais il m&rsquo;a d\u00e9j\u00e0 rendu service plusieurs fois en permettant d&rsquo;avoir un <em>boot<\/em> fiable et pr\u00e9dictible avant l&rsquo;initialisation des t\u00e2ches applicatives complexes.<\/p>","protected":false},"excerpt":{"rendered":"<p>Dans beaucoup de syst&egrave;mes embarqu&eacute;s, il est important de r&eacute;aliser certaines t&acirc;ches au d&eacute;marrage le plus rapidement possible et dans un ordre pr&eacute;cis. Citons par exemple l&rsquo;affichage d&rsquo;un &eacute;cran splashscreen, le montage d&rsquo;une partition de donn&eacute;es, l&rsquo;initialisation des interfaces r&eacute;seau, ou la mise &agrave; l&rsquo;heure du syst&egrave;me. Or les syst&egrave;mes d&rsquo;initialisation modernes, comme systemd, sont [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-6056","post","type-post","status-publish","format-standard","hentry","category-linux-2"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/6056","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/comments?post=6056"}],"version-history":[{"count":22,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/6056\/revisions"}],"predecessor-version":[{"id":6093,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/6056\/revisions\/6093"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=6056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=6056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=6056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}