Renforcer une distribution Raspbian Jessie

Publié par cpb
Oct 19 2015

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 !

21 Réponses

  1. Thomas Petazzoni dit :

    Pour faire un système embarqué, l’aspect reproductibilité du processus de construction du système Linux est essentiel. Ici, la série de bricolage d’une Debian pour aboutir à un résultat approximatif et non-reproductible est pour moi clairement l’exemple parfait de ce qu’il ne faut absolument pas faire.

    Ce n’est pas pour rien qu’il existe des outils comme Buildroot ou Poky (du Yocto Project), précisément parce qu’ils permettent d’automatiser le processus de construction d’un système Linux embarqué.

    Et puis compiler le noyau nativement sur la RPi, pendant 8 heures, c’est là aussi un exemple de mauvaise pratique. Les débutants qui liront ce post vont reproduire, sans forcément réfléchir, cet exemple, alors qu’ils devraient plutôt apprendre à cross-compiler.

    Vraiment, je suis globalement attéré par la qualité générale de tous les posts/articles autour de la RPi. La très grande majorité d’entre eux sont très souvent des exemples parfaits de ce qu’il ne faut pas faire.

    • cpb dit :

      Bonjour Thomas,

      On peut regretter le manque de qualité des constructions réalisées autour d’un Raspberry Pi, le fait est qu’elles existent.

      Je suis parfaitement d’accord que dans un contexte industriel la reproductibilité d’une configuration est essentielle, c’est ce que je prêche à longueur d’années durant mes formations par exemple.
      Lorsque je réalise un projet pour un client industriel, j’utilise généralement Buildroot, parfois (de plus en plus) Yocto, mais surtout je n’utilise pas de Raspberry Pi.

      Néanmoins cette carte est à mon avis parfaitement adaptée pour des expériences hobbyistes ou des prototypes expérimentaux. Cet article s’adresse à ce type de public, qui ne dispose pas nécessairement de système hôte sous Linux, qui ne voudra pas s’investir dans la mise en oeuvre d’une toolchain de compilation croisée et encore moins dans la personnalisation de Yocto, mais qui a envie de réaliser rapidement un montage à base Raspberry Pi. Je connais pas mal de gens dans ce cas.

      Et il reste néanmoins que leur utilisation du Raspberry Pi s’approche des problématiques d’un « vrai » système embarqué, alors il leur faut le résoudre tant bien que mal en s’appuyant sur une distribution existante.

      Alors, pour être bien clair : cet article ne permet pas de transformer un Raspberry Pi avec une distribution Debian en un système embarqué robuste et industrialisable. Il s’agit simplement de le renforcer rapidement pour l’utiliser dans des circonstances un peu plus exigeantes que celles d’un desktop.

      J’encourage les lecteurs à dépasser le stade de la distribution précompilée et à se former à l’utilisation de Yocto et Buildroot (par exemple avec les slides mis en ligne par Thomas).

      • ferandec dit :

        Bonjour à tous,

        je suis « globalement sidéré » de la manière dont certains peuvent réagir à des articles que je trouve personnellement très didactiques et d’un niveau que j’estime élevé pour ma part. Le fait que quelqu’un d’aussi compétent que Thomas intervienne sur ce blog en est d’ailleurs une preuve, non ?
        L’avis de Thomas est tout à fait respectable …mais dans un contexte industriel où le déploiement du système doit se faire sur plusieurs cartes. La Raspberry n’est sûrement pas la plateforme de prédilection dans ce contexte.
        Ceci étant dit et toujours de mon point de vue, apprendre Buildroot (ou encore pire de Poky) me semble incompatible avec le temps dont dispose un utilisateur moyen de Raspberry pour mener à terme son projet avant qu’il ne se décourage.
        Convaincu de l’utilité des systèmes de build, je me suis essayé à ptxdist à l’époque où je « jouais » avec la carte mini2440. Cependant, j’ai assez vite laissé tomber car je me suis rendu compte qu’il fallait d’abord bien maitriser Linux avant de vouloir automatiser sa construction. Et c’est là que, d’une part, je rends hommage à Christophe pour ses articles qui donnent envie d’aller plus loin et, d’autre part, que je rejoins Thomas sur le fait que ce j’apprends ici me permettra peut-être un jour de ne plus me satisfaire de ce qui est présenté dans bon nombre de site ou de revues consacrés à la Raspberry.

        Au plaisir de vous relire

    • Fabrice Mousset dit :

      Bonsoir,

      je trouve la réaction de Thomas Petazzoni un peu sévère/impulsive.

      Il est clair, qu’une carte de type Raspberry Pi/BeagleBone/autre n’est pas une solution industrielle mais un point d’entrée à coût relativement faible pour le hobbyiste/bricoleur. Et c’est en ce sens que je comprends l’article.
      Il permet de donner des pistes pour rendre une carte RPi avec une distribution « standard » un peu plus proche des réalités d’un système embarqué.

      Les solutions de type Poky/Buildroot/Amgström et j’en passe, sont pour un public plus « averti » et avec un objectif bien différent du bidouilleur. Elles demandent également un investissement en terme d’apprentissage beaucoup plus important.

      Mes 2 cts.

    • Mouha dit :

      Bonjour,

      Tout d’abord bravo et merci de ce billet. J’aimerais également réagir sur la réponse de Thomas car je trouve que les propos sont injustifiés. Si l’auteur n’avait donné que de mauvaises directives, durs ou pas : il aurait fallut le dire. Pourtant ici, Christophe nous donne au fur et à mesure de ses billets des moyens : par transmission de la connaissance ou par les outils à utiliser, et pour moi ces moyens me permettent (et m’ont déjà permis) d’être autonome. Un peu comme la distribution Gentoo/funtoo pour comprendre les mecanismes en action: « apprendre à pêcher plutôt qu’avoir le poisson ». Et par conséquent, je regrette que Thomas n’ai pas inclus cet aspect important : dans quel but et quel contexte les propos de Christophe ont été donnés. J’y vois un jugement de valeurs assez surprenant pour un professionel de ce milieu. Etant d’ailleurs développeur/intégrateur dans le milieu de l’embarqué, je suis bien content d’avoir appris comment améliorer mon système, et même juste savoir comment trouver une information pour le noyau ou un FS, ce qui me fait défaut par manque de temps.
      J’imagine pour les hobbyistes.

      Un seul petit regret : la BeagleBone ou la Wandboard sont souvent mis en filigrane par rapport à la PI.

      Bravo donc Christophe

  2. lemoidului dit :

    systemd sur de l’embarqué, snif …..
    Enfin la raspberry c’est plutot un nano ordinateur pas vraiment de l’embarqué mais ou est la limite de nos jours? C’est quoi en fait de l’embarqué ? Pas facile de répondre clairement à cette question.
    Ah il est loin le temps ou on pouvait booter son noyau directement sur un DK et bidouiller une distribution pour l’adapter facilement.
    Heureusement pour l’embarqué y a des trucs spécialisés comme yocto, ou comment s’embrouiller avec une usine à gaz mais bon c’est pour de l’industrie , faut bien que ca reste compliqué sinon c’est pas marrant.
    Linux (le noyau) reste un critère de choix pour un système qu’il soit embarqué ou non. Par contre, pour les distributions on est clairement sur un virage un peu dur à négocier.

  3. Bonjour,

    Pareil je trouve la réaction de Thomas P. très dure. S’agissait-il d’une réaction sur l’ensemble des articles qui apparaissent sur la toile ?

    Je trouve les posts de Christophe B. simple et facile d’accès pour le bricoleur/hobbyeur lambda de son canapé qui n’a pas tous les outils (matériel et logiciel) ni la connaissance pointue pour déployer des architectures de build telles que Yocto ou Buildroot (qui reste très simple pour ce dernier).

    Mais ce qui est plus intéressant ce sont les petites astuces ou les complexités identifiées (qui viennent peut-être du monde Industriel) et qui amène l’utilisateur lambda à se poser des questions par exemple sur la robustesse, la sécurité, l’administration et qui amène aussi à se poser des questions sur l’architecture du noyau et des services sous Linux. Ces posts introductifs sont un début de réflexion qui à mon sens amèneront l’utilisateur lambda à aller voir plus loin.

    Enfin, il faudrait m’expliquer comment introduire la cross-compilation à quelqu’un sans que celui-ci n’ait de connaissance sur les compilateurs et la compilation native… Point de vue personnel qui me vient de mon expérience personnelle (enseignement…).

    C’était mes 2 cts.

  4. LIBERADO dit :

    Bonjour,

    Merci pour ce post.
    On oublie parfois de justifier le « périmètre de validité » d’un article, mais bon présenter une raspberryPI + debian c’est déjà en soit définir un contour que légèrement « industrialisable ».

    Cette approche est aussi très bonne pour sensibiliser au développement embarqué : « on prend vos outils et on vous démontre leur limite ».
    J’ai travaillé avec Yocto et je suis d’accord pour dire que l’apprentissage est « long » avant d’obtenir un premier résultat.
    Par ailleurs rien n’empêche avec buildroot ou Yocto d’avoir les problèmes d’init et de montage FS relevé ici.

    Et je pense que c’est le but de cet article : avoir une configuration cohérente du FS avec son environnement.

  5. thierryb dit :

    Vraiment, je suis globalement TRES HEUREUX par la qualité générale de tous les posts/articles autour de la RPi et particulièrement de celui-çi. La très grande majorité d’entre eux sont très souvent des exemples parfaits de ce qu’il faut faire, pour une utilisation personnelle.

    Un grand bravo à toi. Les débutants ont une meilleures connaissance du système RPi. Continue !

  6. ANCELOT STEPHANE dit :

    Dans l embarqué, chacun ses besoins ou pratiques. Personnellement, je considere que customiser une debian est une solution , de plus qui a fait ses preuves. L’essentiel est de maitriser ce qui doit être fait pour durcir le systeme. Je suis désolé Thomas, mais avec un buildroot, tu maitriseras pas plus / ni mieux !

  7. yann dit :

    Bonjour,
    Attention à l’illusion de la protection contre les ruptures d’alim avec la partition système montée read-only. De manière inattendue, les problèmes sont parfois pire quand on a un watchdog en fonctionnement (ou sur une phase de reboot qui dérape), sans rupture d’alim! Bien des fabricants de stockage flash SD/USB testent en fait mieux le redémarrage à froid du firmware interne du stockage que son redémarrage à chaud: Gare aux usb-reset, lors du reset controleur (et des réinit au démarrage: typiquement dans uboot/bist/linux), qui tombent alors que des écritures étaient en cours. Les pointeurs internes corrompus peuvent mener à des corruptions absolument partout, sans aucun égard pour le partitionnement logique du média! Typiquement, c’est des corruptions aléatoires de la taille d’une page interne du média de stockage… qu’on décide de creuser le jour ou ça tombe sur la table de partition ou sur un fichier de sa partition système ro!
    D’expérience, je ne partage pas non plus votre constat sur l’intérêt du FAT vs un Ext4 avec les options de montage qui vont bien (« noatime » pour limiter les écritures mais surtout « journal_checksum », afin d’éviter les problèmes de corruption massives, via les métadonnées, si on se limite au simple contrôle sémantique de base des transactions: Répertoires entiers qui « disparaissent », corruptions de SB catastrophiques sur de petites partitions ou il n’y en a pas de sauvegarde). Ext4 est très mature, les performances sont comparables à du FAT (attention: Un seul équivalent de SB=corruptions totales possibles), surtout dans le temps car le mballoc fait que cela ne fragmente pas. On conserve des perfs proches du séquentiel/gros fichier. Autre avantage, certains stockages USB (y compris grand public) supportent TRIM et peuvent être montés « discard » pour là aussi avoir des perfs plus constantes dans le temps.
    En réalité, le facteur perf le plus utile c’est dans /sys/block/sd?/device/max_sectors que cela se passe pour les stockages usb. Pour ne pas poser de problèmes à des clef USB grand public de mauvaise qualité, Linux limite par défaut ce paramètre à 240 (les autres stockages SCSI, c’est 1024) mais on peut très souvent aller au delà, surtout avec des stockages de qualité.
    Car ce paramètre joue sur le nombre max de blocs contigus pouvant être lus/écrits avec une seule commande SCSI read_10/write_10 (en dessous, donc, on saucissonnera les accès sans nécessité, ajoutant des commandes/protocole inutiles vs les stricts données, pénalisant les perfs).
    Le jeu de commande pur permettrait en fait un u16 (65535) mais aller jusque là pourrait générer d’autres problèmes sous OS (mais uboot peut sans problème mettre plus, en patchant la valeur USB_MAX_XFER_BLK du common/usb_storage.c, au bénéfice des temps de boot).
    Attention toutefois à bien tester ce que supportent les stockages pouvant rentrer dans son produit. Les problèmes ne sont parfois pas visibles de suite: Il faut un fichier suffisamment gros et sans fragmentation, sinon ou saucissonnera sous la limite sans voir les problèmes: Tester via l’écriture de tests hors OS (via un test écrit sous uboot ou dans un bist) ou après création de gros fichiers, sur une partition récemment formatée (=> il sera contigu) et verif via le combo debugfs (la commande, sur la partition) et la commande stat dans le shell debugfs, sur le fichier test (un seul extent Ext4 = contigu?).
    En pratique, on arrive à presque égaler sous Linux/Ext4 les performances brutes (sans FS) du stockage.
    Bref, les stockages flash ne nécessitent pas d’utiliser des systèmes de fichiers antédiluviens et les problèmes peuvent ne pas être tous gérables via le simple choix de système de fichier, quand ils concernent le firmware même du contrôleur intégré à son stockage flash: C’est bien plus fréquent qu’on ne le croit, y compris sur des modules destinés à un usage embarqué (eUSB). Ayant eu à en sélectionner, 100% de ceux que j’ai eu entre les mains ont leur problèmes, plus ou moins graves.

  8. Damien dit :

    Merci pour ce tuto qui fait ressortir de bonnes pratiques pour nos Rpi sous Raspbian

    Je n’aurais pas pensé par exemple à mettre le fs en lecture seule, je suis tombé sur ce blog en recherchant la meilleure façon de désactiver le swap (la désinstallation complete de l’outil en l’occurence :p) afin d’économiser un peu de lifetime sur ma nouvelle carte SD.

    J’ai trouvé bien plus d’infos utiles et détaillées que ce que je recherchais au départ 🙂

    Damien

    PS: Je me sers du Rpi afin d’avoir un environnement de dev pour mes projets NodeJS.

  9. stephaneg dit :

    bonjour Mr Blaess,

    j’applaudie pour tout le temps consacré à écrire vos articles, qui, de mon côté, m’ont permis de remettre un peu le pied à l’étrier en matière d’informatique embarquée, et même de construire un « réel » système embarqué Linux patché temps réel avec linux-rt, grâce à buildroot et d’avoir un résultat très rapidement, pour un besoin particulier (projet freeware)

    Tous vos articles sont des mines d’information et un trésor pour beaucoup de personnes.
    Votre attittude est « ouverte » et cela plait à beaucoup de gens.

    Dire que compiler un noyau Linux, et faire de l’embarqué sur une Raspi, est réservé aux « hobbyistes » est abusif et déplacé.

    Combien de personnes de l’informatique ne savent même pas les commandes de base sous Linux !

    Ayant une expérience en administration système et réseaux sous Unix/Linux, je suis réellement surpris par les propos de Mr Petazzoni, car vos articles sont d’un bon niveau et aide réellement et concrètement beaucoup de personnes, à faire le lien entre diverses choses de l’informatique en général, et de l’embarqué en particulier.

    Ce que devrait comprendre Mr Petazzoni, c’est que beaucoup de gens / informaticiens n’ont pas le temps ni les moyens de creuser un sujet : la polyvalence prévaut sur l’expertise maintenant dans beaucoup de projets, où sont entremêlés diverses compétences.

    Pour avoir regardé un petit peu du côté de Yocto, cela me fait un peu penser aux gens qui sont experts en Java / C++ : beaucoup de « branlette intellectuelle » pour beaucoup de complications aussi.

    Ne sont « experts » que les gens fermés et peu prompt à se remettre en question.

    Alors qu’on peut rester simple, et oui on peut faire de l’embarqué avec des cartes populaires comme la raspi, sans l’obligation d’avoir un TIMER, un WATCHDOG ou bien une RTC

    et sans être obligé de fourrer son nez dans Yocto,

    Stéphane

  10. f6k dit :

    Bonjour Christophe,

    Le temps a passé depuis l’écriture de cet article mais je voudrais savoir si les remarques concernant l’utilisation d’un système de fichier non journalisé pour préserver la carte SD sont toujours d’actualité ? Tu expliques en effet que l’on aurait à gagner en passant à ext2 à la place de ext4 (système par défaut de Raspbian) mais n’y apporte pas de solution. Dans mes souvenirs modifier dans le fstab ext4 pour ext2 suffirait à désactiver la journalisation sans avoir à formater la partition, mais je n’en suis pas certain. Serait-ce une bonne solution ? Ou bien est-ce simplement sans intérêt finalement ?

    • f6k dit :

      re-Bonjour,

      après quelques recherches, je me rends compte que mon souvenir est erroné ; ce truc ne fonctionne pas avec ext4. Je me corrige donc : il est possible de désactiver la journalisation de la partition grâce à tune2fs après l’avoir démontée bien sûr. Par contre la question de l’opportunité de la chose reste entière au regard des risques induits par l’absence de journalisation !

      • cpb dit :

        La journalisation est intéressante sur des serveurs pour permettre un redémarrage rapide en évitant de vérifier des centaines de Go ou des To de disque.

        Sur un système embarqué son intérêt est très limité. Généralement on essaye de laisser au maximum les partitions en lecture seulement.
        Pour les partitions accessibles en lecture-écriture, la journalisation présente un inconvénient (assez faible il est vrai) : elle multiplie le nombre d’écritures ce qui réduit la durée de vie de la mémoire flash. C’est pour cela que je l’évite sur les systèmes que je configure.

        • f6k dit :

          Merci beaucoup pour ces précisions !

        • yann dit :

          La journalisation apporte aussi de la robustesse en permettant de conserver un système de fichier valide: On peut en effet ne même pas arriver à charger l’initramfs pour pouvoir effectuer le fsck au boot. Normalement, certes, cela ne peut se présenter sur un système embarqué que sur un upgrade ou l’on autorise à ré-écrire ce qui est dans sa partition de boot. Mais sur un réseau avec des milliers d’équipements dans des pays au réseau electrique pas toujours fiable, cela arrive.

          Par contre il faut que le boot loader supporte le rejeu de journal, ce qui n’est pas le cas de u-boot et de son support ext simplifié: Là, on peut se retrouver coincé avec un système ne bootant pas. On le plug sur un PC Linux (avec support Ext complet), ca semble correct… et remis sur la carte cela fonctionne! On jette un coup d’oeuil au dmesg du PC à l’insertion du media pour découvrir un « replay journal » salvateur.
          Mais bon, en FAT en pareille circonstance c’est à tous les coups on perds.

  11. Dominique dit :

    Bonjour,
    J’ai tenté l’opération, mais il me reste une erreur au démarrage que je n’arrive pas à résoudre.

    Voici le message lorsque je fait un « systemctl status » :

    $ systemctl status systemd-rfkill@rfkill0.service
    * systemd-rfkill@rfkill0.service – Load/Save RF Kill Switch Status of rfkill0
    Loaded: loaded (/lib/systemd/system/systemd-rfkill@.service; static)
    Active: failed (Result: exit-code) since jeu. 2017-04-06 18:16:54 CEST; 6 days ago
    Docs: man:systemd-rfkill@.service(8)
    Process: 235 ExecStart=/lib/systemd/systemd-rfkill load %I (code=exited, status=1/FAILURE)
    Main PID: 235 (code=exited, status=1/FAILURE)

    Je me doute qu’il ne réussi pas à écrire quelque part, mais je ne trouve pas ou !
    Si vous aviez une idée ?

  12. Polochon dit :

    Bonjour et merci pour cet excellent article.
    Je me permet une petite remarque: après avoir effectué toutes les modifications décrites, le système se retrouve dans l’incapacité à utiliser la completion depuis le shell. J’obtiens l’erreur cannot create temp file for here-document: Read-only file system
    Je pensais pourvoir le contourner en créant un lien symbolique depuis /tmp vers /var/tmp mais ça ne semble pas fonctionner. Finalement j’ai monté mon propre tmpfs depuis fstab et maintenant tout semble marcher parfaitement: tmpfs /tmp tmpfs defaults,size=256M 0 0 (la capacité est peut être un peu grande, je verrai à l’usage)

URL de trackback pour cette page