![Raspbian Jessie]()
Depuis quelques jours une nouvelle distribution Raspbian est disponible pour le Raspberry Pi : la version Jessie. Il s’agit de l’adaptation de la distribution Debian 8 sortie au printemps.
L’avantage de Debian est de disposer aisément d’un très large éventail d’applications, utilitaires, bibliothèques pré-configurés et faciles à installer. En outre il s’agit en quelque sorte de la distribution de référence lorsqu’on parle d’un système Linux.
L’inconvénient, à mes yeux, de Raspbian est qu’elle est prévue pour une utilisation « desktop » qui convient très bien pour un PC mais pas vraiment pour un système embarqué. Je lui reproche entre autre de ne pas être très robuste vis-à-vis des coupures d’alimentation. Mais rien ne nous empêche de la configurer comme un système embarqué classique. Essayons…
Premier boot, premières impressions
Je démarre la distribution Raspbian Jessie sur un Raspberry Pi modèle 1 B, et j’observe les traces de boot sur la console série.
[ 0.000000] Linux version 4.1.7+ (dc4@dc4-XPS13-9333) (gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03) ) #817 PREEMPT Sat Sep 19 15:25:36 BST 2015
[ 0.000000] CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d
[ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
[ 0.000000] Machine model: Raspberry Pi Model B Rev 2
Le modèle est bien identifié (Raspberry Pi Model B), et nous voyons que le noyau est plutôt récent (4.1.7), compilé le 19 septembre 2015. Il s’agit de la 817ème compilation depuis l’extraction des sources.
Je me demande si cette valeur est bien réelle. Le noyau 4.1.7 a été publié sur www.kernel.org le 13 septembre, cela représente donc une moyenne de 136 compilations par jour ce qui est très intensif, même dans le cas, d’un portage sur une nouvelle plate-forme !
Au bout de quelques secondes de boot, nous apercevons la fin des messages :
[ 3.552810] systemd[1]: Starting Forward Password Requests to Wall Directory Watch.
[ 3.565385] systemd[1]: Started Forward Password Requests to Wall Directory Watch.
[ 3.577346] systemd[1]: Expecting device dev-ttyAMA0.device...
[ 3.588483] systemd[1]: Starting Remote File Systems (Pre).
[ 3.599047] systemd[1]: Reached target Remote File Systems (Pre).
[ 3.607838] systemd[1]: Starting Arbitrary Executable File Formats File System Automount Point.
[ 3.624897] systemd[1]: Set up automount Arbitrary Executable File Formats File System Automount Point.
[ 3.638953] systemd[1]: Starting Encrypted Volumes.
[ 3.649175] systemd[1]: Reached target Encrypted Volumes.
[ 3.657151] systemd[1]: Starting Swap.
[ 3.666030] systemd[1]: Reached target Swap.
[ 3.672566] systemd[1]: Expecting device dev-mmcblk0p1.device...
[ 3.683667] systemd[1]: Starting Root Slice.
[ 3.692723] systemd[1]: Created slice Root Slice.
[ 3.699726] systemd[1]: Starting User and Session Slice.
[ 3.710168] systemd[1]: Created slice User and Session Slice.
[ 3.718224] systemd[1]: Starting /dev/initctl Compatibility Named Pipe.
[ 3.730063] systemd[1]: Listening on /dev/initctl Compatibility Named Pipe.
[ 3.739330] systemd[1]: Starting Delayed Shutdown Socket.
[ 3.749556] systemd[1]: Listening on Delayed Shutdown Socket.
[ 3.757494] systemd[1]: Starting Journal Socket (/dev/log).
[ 3.767964] systemd[1]: Listening on Journal Socket (/dev/log).
[ 3.776167] systemd[1]: Starting udev Control Socket.
[ 3.786159] systemd[1]: Listening on udev Control Socket.
[ 3.793864] systemd[1]: Starting udev Kernel Socket.
[ 3.803610] systemd[1]: Listening on udev Kernel Socket.
[ 3.811114] systemd[1]: Starting Journal Socket.
[ 3.820696] systemd[1]: Listening on Journal Socket.
[ 3.828070] systemd[1]: Starting System Slice.
[ 3.837506] systemd[1]: Created slice System Slice.
[ 3.844769] systemd[1]: Starting File System Check on Root Device...
[ 3.860653] systemd[1]: Starting system-systemd\x2dfsck.slice.
[ 3.882537] systemd[1]: Created slice system-systemd\x2dfsck.slice.
[ 3.897474] systemd[1]: Starting system-autologin.slice.
[ 3.915110] systemd[1]: Created slice system-autologin.slice.
[ 3.924527] systemd[1]: Starting system-serial\x2dgetty.slice.
[ 3.939584] systemd[1]: Created slice system-serial\x2dgetty.slice.
[ 3.949190] systemd[1]: Starting Increase datagram queue length...
[ 3.971123] systemd[1]: Starting Restore / save the current clock...
[ 4.011520] systemd[1]: Mounting POSIX Message Queue File System...
[ 4.045988] systemd[1]: Mounted Huge Pages File System.
[ 4.114605] systemd[1]: Mounting Debug File System...
[ 4.220232] systemd[1]: Started Set Up Additional Binary Formats.
[ 4.279891] systemd[1]: Starting Load Kernel Modules...
[ 4.312892] systemd[1]: Starting udev Coldplug all Devices...
[ 4.358785] systemd[1]: Starting Create list of required static device nodes for the current kernel...
[ 4.403001] fuse init (API version 7.23)
[ 4.434562] systemd[1]: Starting Slices.
[ 4.462789] systemd[1]: Reached target Slices.
[ 4.523991] systemd[1]: Mounted Debug File System.
[ 4.545602] systemd[1]: Mounted POSIX Message Queue File System.
[ 4.569828] i2c /dev entries driver
[ 4.588558] systemd[1]: Started File System Check on Root Device.
[ 4.627558] systemd[1]: Started Increase datagram queue length.
[ 4.641169] systemd[1]: Started Restore / save the current clock.
[ 4.657331] systemd[1]: Started Load Kernel Modules.
[ 4.685012] systemd[1]: Started Create list of required static device nodes for the current kernel.
[ 4.723014] systemd[1]: Time has been changed
[ 4.883663] systemd[1]: Started udev Coldplug all Devices.
[ 5.095077] systemd[1]: Starting Create Static Device Nodes in /dev...
[ 5.112404] systemd[1]: Starting Apply Kernel Variables...
[ 5.146732] systemd[1]: Mounting Configuration File System...
[ 5.199784] systemd[1]: Mounting FUSE Control File System...
Clairement, systemd a pris beaucoup d’ampleur dans cette version de la distribution Raspbian. Comme beaucoup de Linux « historiques », je ne suis pas très enthousiaste devant ce système, tout particulièrement dans le domaine de l’embarqué. Je le trouve plutôt complexe et difficile à maîtriser. Pour des systèmes restreints, que l’on doit ajuster précisément pour un besoin particulier, je trouve plus aisé de reposer sur quelques scripts shell, plutôt que sur un enchevêtrement de services imbriqués et peu configurables.
raspberrypi login: pi
Password: (raspberry)
Je suis connecté sur la console série du système (sur les broches 8 et 10 du Connecteur P1). Cet accès paraît certainement un peu aride à la plupart des utilisateurs, mais étant habitué à travailler sur des systèmes embarqués restreints, je suis plus à l’aise sur le Raspberry Pi en console texte que dans un terminal graphique qui « rame » un peu sur les modèles 1B et 1B+.
Utilisons quelques commandes d’administration simples pour examiner le système.
$ mount
/dev/mmcblk0p2 on / type ext4 (rw,noatime,data=ordered)
devtmpfs on /dev type devtmpfs (rw,relatime,size=218252k,nr_inodes=54563,mode=755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct)
mqueue on /dev/mqueue type mqueue (rw,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
configfs on /sys/kernel/config type configfs (rw,relatime)
fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)
/dev/mmcblk0p1 on /boot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=44508k,mode=700,uid=1000,gid=1000)
gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
$
Il y a un nombre important de montage de systèmes de fichiers virtuels, notamment dans la hiérarchie des Control Groups. Leur présence sur un système embarqué peut être discutable.
$ df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/root 4031552 3197560 609480 84% /
devtmpfs 218252 0 218252 0% /dev
tmpfs 222520 0 222520 0% /dev/shm
tmpfs 222520 4548 217972 3% /run
tmpfs 5120 4 5116 1% /run/lock
tmpfs 222520 0 222520 0% /sys/fs/cgroup
/dev/mmcblk0p1 57288 20232 37056 36% /boot
tmpfs 44508 0 44508 0% /run/user/1000
Il reste un peu de place dans le système de fichiers principal. Tant mieux car je ne compte pas l’agrandir pour le moment.
Désactivation du swap
$ free
total used free shared buffers cached
Mem: 445044 149644 295400 4960 17156 77196
-/+ buffers/cache: 55292 389752
Swap: 102396 0 102396
Horreur ! une zone de swap est réservée sur la carte SD. C’est une mauvaise idée, car dans le cas d’un système saturé on va réaliser un très grand nombre de lectures et d’écritures sur la mémoire Flash, ce qui peut avoir comme effet un vieillissement prématuré. Je conseille très fortement de désactiver ce mécanisme. Pour cela le plus simple, est de désinstaller le package dphys-swapfile
utilisé pour le support du swap intégré dans un système de fichiers.
$ sudo apt-get remove dphys-swapfile
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages will be REMOVED:
dphys-swapfile
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 85.0 kB disk space will be freed.
Do you want to continue? [Y/n] y
[...]
Bien que le package dphys-swapfile
ait été supprimé, le fichier de 100 Mo qu’il utilisait pour proposer cette mémoire alternative subsiste encore… éliminons-le !
$ ls -l /var/swap
-rw------- 1 root root 104857600 Sep 24 15:33 /var/swap
$ sudo rm -f /var/swap
$ free
total used free shared buffers cached
Mem: 445044 220612 224432 4960 20384 142052
-/+ buffers/cache: 58176 386868
Swap: 0 0 0
Je préfère cette situation ! Nous voyons au passage qu’une bonne part de la mémoire est encore potentiellement libre (386868 ko).
Système de fichiers en lecture seule
Sur un système embarqué, je trouve important de laisser le système de fichiers principal, celui qui contient les exécutables binaires, les bibliothèques, etc. en lecture-seule. Ainsi en cas d’interruption brutale d’alimentation électrique, le système de fichiers restera intact, aucune écriture ne pouvant être en cours d’exécution au moment de la coupure. Cela réclame quelques efforts.
Tout d’abord, afin de simplifier les manipulations ultérieures, j’ai coutume de me créer deux petits scripts, que je nomme ro
(read only) et rw
(read/write), qui ont pour rôles respectifs de basculer le système de fichiers en lecture-seule ou lecture-écriture. Je les crée rapidement :
/usr/local/bin/ro:
#! /bin/sh
mount / -o ro,remount
et
/usr/local/bin/rw:
#! /bin/sh
mount / -o rw,remount
Il ne faut pas oublier de rendre ces scripts exécutables :
$ sudo chmod +x /usr/local/bin/ro /usr/local/bin/rw
Nous pouvons toujours essayer d’invoquer ro
, mais il échouera car certains processus maintiennent déjà ouverts des fichiers de l’arborescence principale.
$ sudo ro
mount: / is busy
Lors du boot, le noyau monte lui-même la racine du système de fichier en s’appuyant sur le contenu du paramètre root=
transmis par le bootloader. Sur le Raspberry Pi, les paramètres de démarrage du noyau se trouvent dans le fichier /boot/cmdline.txt
. Le bootloader en ajoute d’autres également, mais cela ne nous concerne pas ici.
Par défaut, le noyau monte son arborescence en lecture-écriture, mais la présence du mot-clé ro
dans ses paramètres de démarrage lui demande de réaliser le montage en lecture-seule. Je vais donc commencer par éditer le fichier /boot/cmdline.txt
pour ajouter ce mot-clé.
/boot/cmdline.txt:
ro dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
J’ai inséré ici le mot-clé en début de ligne, mais on peut le placer n’importe où sur celle-ci. Attention : il faut bien que toutes les options soient sur la même ligne.
Une fois le boot du noyau terminé, l’espace utilisateur prend le relais, par l’intermédiaire de init
et ses scripts sur les distributions de type Système V ou de systemd
pour la plupart des distributions récentes. Durant cette étape de démarrage, le système de fichiers principal peut être remonté en lecture-écriture s’il est configuré ainsi.
Pour m’assurer que la racine du système de fichiers reste en lecture-seule après le boot du noyau, j’édite le fichier /etc/fstab
qui contient la configuration des partitions et je rajoute le mot clé ro
sur la ligne décrivant le montage de « /
« . Attention à ne pas ajouter d’espaces autour du mot-clé ou des virgules.
/etc/fstab:
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,ro,noatime 0 1
J’en ai profité pour effacer les deux dernières lignes de commentaire qui parlaient de dphys-swapfile
(que nous avons supprimé précédemment).
Vérifions le fonctionnement :
# reboot
[...]
raspberrypi login: pi
Password: (raspberry)
pi@raspberrypi:~$ sudo -i
# echo hello > /test
-bash: /test: Read-only file system
# rw
# echo hello > /test
# cat /test
hello
# rm /test
# ro
mount: / is busy
#
Systèmes de fichiers temporaires non persistants
Notre système démarre bien en mode lecture-seule, et nous ne pouvons pas écrire sur le système de fichiers principal. Une fois basculé explicitement en mode lecture-écriture, il devient possible de modifier le contenu de ce système de fichiers.
Néanmoins, il est impossible de revenir en mode lecture-seule, car des processus ont profité du laps de temps où le système est monté en lecture-écriture pour y ouvrir des fichiers en écriture et les maintenir ouverts. Essayons de savoir quels fichiers sont concernés. Tout d’abord, je cherche la liste des processus qui tiennent un fichier ouvert dans cette arborescence.
# fuser -v -m /
USER PID ACCESS COMMAND
/: root kernel mount /
root 1 .rce. systemd
root 2 .rc.. kthreadd
[...]
root 60 .rc.. kworker/0:1H
root 91 .rce. systemd-journal
root 93 frce. systemd-udevd
root 180 .rc.. kworker/0:3
root 375 .rc.. cfg80211
root 450 .rce. sshd
root 451 .rce. cron
root 455 .rce. systemd-logind
avahi 471 .rce. avahi-daemon
messagebus 478 .rce. dbus-daemon
avahi 490 .rce. avahi-daemon
nobody 492 .rce. thd
ntp 501 .rce. ntpd
root 502 Frce. rsyslogd
root 521 .rce. login
root 522 .rce. login
pi 534 .rce. systemd
pi 545 .rce. (sd-pam
pi 571 .rce. bash
root 599 .rce. dhcpcd
pi 697 .rce. bash
root 707 .rce. sudo
root 714 .rce. bash
root 740 frce. lightdm
root 745 Frce. Xorg
root 758 .rce. lightdm
pi 771 Frce. lxsession
pi 795 Frce. ssh-agent
pi 798 .rce. dbus-launch
pi 799 .rce. dbus-daemon
pi 805 .rce. gvfsd
pi 809 .rce. gvfsd-fuse
pi 820 Frce. openbox
pi 821 Frce. lxpolkit
pi 823 Frce. lxpanel
pi 825 Frce. pcmanfm
pi 832 Frce. ssh-agent
root 834 .rce. polkitd
pi 847 .rce. gvfs-udisks2-vo
root 849 .rce. udisksd
pi 859 .rce. gvfs-afc-volume
pi 864 .rce. gvfs-mtp-volume
pi 868 .rce. gvfs-gphoto2-vo
pi 872 .rce. gvfs-goa-volume
pi 881 Frce. menu-cached
pi 888 .rce. gvfsd-trash
J’ai éliminé de la liste les threads du noyau sur lesquels je n’ai pas vraiment de contrôle. Les processus qui nous intéressent sont ceux maintenant un fichier ouvert en lecture-écriture. Ceci est caractérisé par un F
majuscule en début de troisième champ. Je filtre ces processus et affiche la liste complète des descripteurs dont ils disposent. La liste est énorme car il y a également les terminaux, les sockets, les pipes, les fichiers exécutables, etc…
# fuser -v -m / 2>&1 | awk '($3 ~ /F.*/){ print "/proc/"$2"/fd"}' | xargs ls -l
[...]
À l’aide d’une longue séquence de grep
successifs, je ne conserve que la liste des fichier qui m’intéressent.
# fuser -v -m / 2>&1 | awk '($3 ~ /F.*/){ print "/proc/"$2"/fd"}' | xargs ls -l| grep '^l.w' | grep -v socket: | grep -v /dev/ | grep -v "/proc" | grep -v anon_inode | grep -v pipe
l-wx------ 1 root root 64 Oct 4 15:52 10 -> /var/log/auth.log
l-wx------ 1 root root 64 Oct 4 15:52 11 -> /var/log/user.log
l-wx------ 1 root root 64 Oct 4 15:52 6 -> /var/log/syslog
l-wx------ 1 root root 64 Oct 4 15:52 7 -> /var/log/kern.log
l-wx------ 1 root root 64 Oct 4 15:52 8 -> /var/log/messages
l-wx------ 1 root root 64 Oct 4 15:52 9 -> /var/log/daemon.log
l-wx------ 1 root root 64 Oct 4 15:55 0 -> /var/log/Xorg.0.log
l-wx------ 1 pi pi 64 Oct 4 15:55 1 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 2 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 1 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 2 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 4 -> /home/pi/.cache/openbox/openbox.log
l-wx------ 1 pi pi 64 Oct 4 15:55 1 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 2 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 1 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 2 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 1 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
l-wx------ 1 pi pi 64 Oct 4 15:55 2 -> /home/pi/.cache/lxsession/LXDE-pi/run.log
Nous voyons que les fichiers ouverts en écriture servent essentiellement à enregistrer des traces d’activité.
Sur un système embarqué les traces servent au moment de la mise au point, mais ne sont plus utiles après le basculement en production. Nous pouvons donc accepter de les placer sur un disque tmpfs (ramdisk automatiquement formaté et dimensionné par le noyau) où elles resteront accessibles pendant le fonctionnement du système mais ne survivront pas à un reboot du Raspberry Pi. Nous avons vu plus haut, dans le résultat de la commande mount
, que le répertoire /run
est un point de montage d’un tmpfs. Utilisons-le pour y stocker les fichiers de trace.
# rm -rf /var/log
# ln -s /run/log /var/log
Il existe également un cas particulier dans les fichiers de configuration du système : /etc/resolv.conf
. Lorsque le système utilise le mécanisme DHCP pour demander à un serveur local (une box par exemple) les paramètres du réseau, il enregistre dans ce fichier l’adresse du DNS (le serveur de noms qui permet de résoudre un nom de machine par exemple www.kernel.org en une adresse IP comme 199.204.44.194). Le fichier doit donc être accessible en écriture pendant le fonctionnement normal du système. Pour cela le plus simple est de le rediriger via un lien symbolique vers un répertoire sur un système de fichier tmpfs.
# rm -f /etc/resolv.conf
# ln -sf /run/resolv.conf /etc/
# reboot
Système de fichiers modifiables et persistants
Nous pouvons également être amenés à utiliser des fichiers qui devront être modifiables pendant le fonctionnement du système, mais qui devront aussi être persistants, c’est-à-dire survivre à un redémarrage de la machine. Nous allons les placer sur une partition spécifique en sachant que leur situation est néanmoins un peu plus précaire que le reste du système en cas de coupure d’alimentation électrique.
Créons une partition, que nous utiliserons par la suite pour le répertoire /home/pi
. Ceci permettra ainsi de sauvegarder les fichiers de traces applicatives que nous avons observés plus haut. Je voudrais coller cette partition à la fin de l’espace disque libre, pour pouvoir ensuite agrandir la partition système (celle en lecture seule) afin de disposer de place supplémentaire si je veux installer de nouveaux packages. Je vais créer une partition de /home/pi
de 2 Go.
# fdisk /dev/mmcblk0
Welcome to fdisk (util-linux 2.25.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xba2edfb9
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 8192 122879 114688 56M c W95 FAT32 (LBA)
/dev/mmcblk0p2 122880 8447999 8325120 4G 83 Linux
Notre système dispose déjà de deux partitions : la première contient le bootloader et le noyau (nous y reviendrons plus bas), la seconde notre système de fichiers principal. Nous voyons dans le résultat de la commande ‘p
‘ que la taille des secteurs est de 512 octets et que la carte SD contient 15523840 secteurs. Je vais donc démarrer ma partition au secteur 15523840 – 4*1024*1024 = 11329536.
Command (m for help): n
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (3,4, default 3): 3
First sector (2048-15523839, default 2048): 11329536
Last sector, +sectors or +size{K,M,G,T,P} (11329536-15523839, default 15523839): (Entrée)
Created a new partition 3 of type 'Linux' and of size 2 GiB.
Command (m for help): p
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xba2edfb9
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 8192 122879 114688 56M c W95 FAT32 (LBA)
/dev/mmcblk0p2 122880 8447999 8325120 4G 83 Linux
/dev/mmcblk0p3 11329536 15523839 4194304 2G 83 Linux
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy
The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).
#
Le message final m’indique que le noyau n’a pas honoré la demande de fdisk
concernant la relecture de la nouvelle table de partition. Pour s’assurer qu’elle soit bien prise en compte, redémarrons.
# reboot
[...]
[ 5.202270] systemd[1]: Mounting FUSE Control File System...
Raspbian GNU/Linux 8 raspberrypi ttyAMA0
raspberrypi login: pi
Password: (raspberry)
Last login: Sun Oct 4 16:13:30 UTC 2015 on tty1
Linux raspberrypi 4.1.7+ #817 PREEMPT Sat Sep 19 15:25:36 BST 2015 armv6l
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@raspberrypi:~$ sudo -i
# ls -l /dev/mmcblk0*
brw-rw---- 1 root disk 179, 0 Oct 4 16:13 /dev/mmcblk0
brw-rw---- 1 root disk 179, 1 Oct 4 16:13 /dev/mmcblk0p1
brw-rw---- 1 root disk 179, 2 Oct 4 16:13 /dev/mmcblk0p2
brw-rw---- 1 root disk 179, 3 Oct 4 16:13 /dev/mmcblk0p3
La partition 3 est bien présente. Formattons-la.
# mkfs.vfat -n HOME /dev/mmcblk0p3
mkfs.fat 3.0.27 (2014-11-12)
J’ai choisi un format vfat pour mon système de fichiers. Ce choix peut paraître a priori surprenant, il s’agit d’un format limité, ne supportant ni le choix d’appartenance des fichiers (ils appartiennent tous à root par défaut), ni la modifications des droits (rwxr-xr-x
pour tous les fichiers), ni les fichiers spéciaux de périphériques, ni les sockets, ni les tubes nommés, ni même les liens symboliques. Autant dire que pour un système de type Unix, ce format est particulièrement restrictif. Mais justement, c’est ce qui lui donne une bonne robustesse en cas de coupure d’alimentation pendant une écriture. Naturellement le fichier en cours de modifications sera incomplet, mais le système de fichiers lui-même ne sera pas endommagé. Peut-être que quelques secteurs seront « perdus » car ils seront considérés comme utilisés alors qu’aucun fichier ne permettra d’y accéder, mais il n’y aura pas d’incohérence au niveau de la partition elle-même. C’est ce que notre expérience quotidienne des clés USB nous apprend : même si un utilisateur l’arrache de l’ordinateur sans l’avoir démontée proprement, la clé reste utilisable même si le fichier en cours d’écriture peut être éventuellement incomplet.
C’est pour cette propriété de vfat que je le préfère à d’autres systèmes comme ext2 qui peuvent nécessiter une étape de récupération (une vérification avec fsck
) avant d’être remontés en cas d’arrêt intempestif. Bien sûr pour une partition système en lecture seule je préfère très largement ext2 qui offre tout le support des fichiers Unix, mais pour un répertoire de sauvegarde simple (comme /home/pi
ici) le format fat32 conviendra parfaitement.
J’ai cité à dessein ext2 plutôt que ext3 ou ext4 car je refuse habituellement l’usage d’un journal lorsque le périphérique de support est une mémoire flash. Avec la journalisation proposée par ext3 et ext4 on multiplie par trois le nombre d’écritures en cas de modification d’un fichier. Ce qui divise donc par trois la durée de vie de la mémoire flash… Ceci n’a pas d’influence lorsque le système de fichiers est en lecture seule comme notre partition principale.
La partition est prête, il faudra la monter automatiquement au démarrage, je l’ajoute donc dans /etc/fstab
avec des options uid
et gid
qui fixent l’appartenance par défaut des fichiers qu’elle contient :
# rw
# nano /etc/fstab
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,ro,noatime 0 1
/dev/mmcblk0p3 /home/pi vfat defaults,uid=pi,gid=pi 0 1
# reboot
[...]
Raspbian GNU/Linux 8 raspberrypi ttyAMA0
raspberrypi login: pi
Password: (raspberry)
[..]
pi@raspberrypi:~$ ls
pi@raspberrypi:~$ echo hello > test
pi@raspberrypi:~$ ls
test
pi@raspberrypi:~$ cat test
hello
pi@raspberrypi:~$
Nous avons bien réussi à créer un fichier sur la partition montée sur le répertoire /home/pi
.
J’aimerais également augmenter la taille de la partition principale au maximum afin qu’elle remplisse tout l’espace restant de la carte SD pour pouvoir ajouter éventuellement d’autres applications. C’est ce que fait habituellement l’utilitaire raspi-config
de la distribution Raspbian. Malheureusement il ne peut pas fonctionner ici car il n’accepte que de redimensionner la dernière partition de la liste, ce qui n’est pas notre cas.
Commençons donc par agrandir avec fdisk
la partition sans toucher au système de fichiers. Pour cela, il faut la détruire et la reconstruire avec le même secteur de départ et une longueur plus grande.
pi@raspberrypi:~$ sudo fdisk /dev/mmcblk0
[...]
Command (m for help): p
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xba2edfb9
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 8192 122879 114688 56M c W95 FAT32 (LBA)
/dev/mmcblk0p2 122880 8447999 8325120 4G 83 Linux
/dev/mmcblk0p3 11329536 15523839 4194304 2G 83 Linux
Command (m for help): d
Partition number (1-3, default 3): 2
Partition 2 has been deleted.
Command (m for help): n
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)
Select (default p): p
Partition number (2,4, default 2): 2
First sector (2048-15523839, default 2048): 122880
Last sector, +sectors or +size{K,M,G,T,P} (122880-11329535, default 11329535): (Entrée)
Created a new partition 2 of type 'Linux' and of size 5.4 GiB.
Command (m for help): p
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xba2edfb9
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 8192 122879 114688 56M c W95 FAT32 (LBA)
/dev/mmcblk0p2 122880 11329535 11206656 5.4G 83 Linux
/dev/mmcblk0p3 11329536 15523839 4194304 2G 83 Linux
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy
The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8).
# reboot
Après redémarrage nous pouvons redimensionner le système de fichiers sans perdre son contenu
pi@raspberrypi:~$ sudo rw
pi@raspberrypi:~$ sudo resize2fs /dev/mmcblk0p2
pi@raspberrypi:~$ sudo reboot
[...]
pi@raspberrypi:~$ df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/root 5449836 3121868 2047096 61% /
devtmpfs 218240 0 218240 0% /dev
tmpfs 222512 0 222512 0% /dev/shm
tmpfs 222512 4784 217728 3% /run
tmpfs 5120 4 5116 1% /run/lock
tmpfs 222512 0 222512 0% /sys/fs/cgroup
/dev/mmcblk0p1 57288 24256 33032 43% /boot
/dev/mmcblk0p3 2093048 1010248 1082800 49% /home/pi
Recompilation du noyau
Une dernière phase de personnalisation consiste à recompiler notre noyau. Pourquoi cela ? Tout d’abord pour le plaisir de voir notre système fonctionner sur un kernel que nous pouvons configurer et ajuster à notre guise. Ensuite pour disposer de tout l’environnement nécessaire (fichiers d’en-tête, Makefiles, et fichiers de configuration) nécessaires à la compilation de modules supplémentaires personnels.
Cette compilation est facile à réaliser, elle nécessite surtout un temps de traitement assez long (deux bonnes heures avec un Raspberry Pi 2, et huit environ avec le modèle 1).
Installons les packages nécessaires non présents dans la distribution de base :
pi@raspberrypi:~$ sudo rw
pi@raspberrypi:~$ sudo apt-get install -y bc libncurses5-dev
Ensuite récupérons les sources du noyau adaptées pour le Raspberry Pi :
pi@raspberrypi:~$ git clone https://github.com/raspberrypi/linux rpi-linux
remote: Counting objects: 4521622, done.
remote: Compressing objects: 100% (4139/4139), done.
remote: Total 4521622 (delta 3292), reused 400 (delta 400), pack-reused 4517078
Réception d'objets: 100% (4521622/4521622), 1.26 GiB | 1.80 MiB/s, done.
Résolution des deltas: 100% (3735624/3735624), done.
Vérification de la connectivité... fait.
Checking out files: 100% (49945/49945), done.
Nous allons compiler un noyau 4.1 :
pi@raspberrypi:~$ cd rpi-linux
pi@raspberrypi:~/rpi-linux$ git checkout rpi-4.1.y
Voyons les configurations existantes pour notre système :
pi@raspberrypi:~/rpi-linux$ make help
[..]
bcm2709_defconfig - Build for bcm2709
bcm2835_defconfig - Build for bcm2835
bcm_defconfig - Build for bcm
bcmrpi_defconfig - Build for bcmrpi
bockw_defconfig - Build for bockw
cerfcube_defconfig - Build for cerfcube
clps711x_defconfig - Build for clps711x
[..]
Celle qui nous intéresse pour le Raspberry Pi modèle 1 (B, A B+) est bcmrpi
(Broadcom Raspberry Pi).
pi@raspberrypi:~/rpi-linux$ make bcmrpi_defconfig
Pour un Raspberry Pi modèle 2, on fera :
pi@raspberrypi:~/rpi-linux$ make bcm2709_defconfig
Nous partons de cette configuration mais pouvons la modifier quelque peu :
pi@raspberrypi:~/rpi-linux$ make menuconfig
Je lance la compilation avec deux jobs en parallèle sur Raspberry Pi modèle 1 B, on peut en lancer environ 8 en parallèle sur un modèle 2.
pi@raspberrypi:~/rpi-linux$ make -j 2
Huit heures plus tard environ sur un modèle 1 (ou 1h30 sur un modèle 2) j’installe le nouveau noyau et ses modules :
pi@raspberrypi:~/rpi-linux$ sudo make modules_install
pi@raspberrypi:~/rpi-linux$ sudo cp /boot/kernel.img /boot/kernel.img.prev
pi@raspberrypi:~/rpi-linux$ sudo cp /boot/kernel7.img /boot/kernel7.img.prev
pi@raspberrypi:~/rpi-linux$ sudo cp arch/arm/boot/zImage /boot/kernel.img
pi@raspberrypi:~/rpi-linux$ sudo cp arch/arm/boot/zImage /boot/kernel7.img
pi@raspberrypi:~/rpi-linux$ sudo reboot
Notez que le bootloader charge l’image kernel.img
sur les Raspberry Pi 1 et kernel7.img
sur les Raspberry Pi 2. Nous avons sauvegardé les images précédentes au cas où le nouveau noyau ne fonctionne pas… Après le redémarrage nous pouvons vérifier notre version :
pi@raspberrypi:~$ uname -a
Linux raspberrypi 4.1.10-cpb #1 PREEMPT Mon Oct 5 10:58:11 UTC 2015 armv6l GNU/Linux
Conclusion
Nous avons pris ainsi possession d’une distribution Raspbian standard et à travers quelques modifications (qui ne changent pas la compatibilité pour les mises à jour) amélioré sa résilience en cas de coupure d’alimentation, protégé son système de fichiers principal contre les modifications involontaires, évité l’usure prématurée due au swap et conservé une partition spécifique pour l’utilisateur.
Je pense que ces modifications sont particulièrement utiles si l’on souhaite utiliser le Raspberry Pi dans un contexte de système embarqué plutôt qu’en desktop classique.
Toutes les remarques, corrections, critiques sont les bienvenues !