以下の内容はhttps://mickey-happygolucky.hatenablog.com/entry/2025/03/13/084357より取得しました。


Yocto Project ラズベリーパイ4でディスクレス

ルートFSのサイズが小さいLinuxでは、OSの起動時にRAM上にすべてのファイルを展開することで、 ストレージデバイスに全くアクセスしないシステム構成を捕ることができる。

これはKnoppixPuppy Linuxなど古くから存在する手法である。下記にRAM上での動作が可能なLinuxディストリビューションの一覧を確認することができる。

https://en.wikipedia.org/wiki/List_of_Linux_distributions_that_run_from_RAM

実装方法

Casperの実装

UbuntuのLiveイメージで使用されるCasptertoramオプションがこのような挙動となる。その実装はここで確認することができる。

toramオプションを指定されると、copy_live_to関数が呼び出される。

この関数を紐解いていくと次のような処理を行っていることがわかる。

  1. ルートFSをマウント
  2. ルートFSのサイズをduで計算
  3. bcでサイズを1.3倍
  4. tmpfs(RAM)を/dev/shmにマウント
  5. ルートFSのファイルをすべて/dev/shmにコピー
  6. ルートFSをアンマウント
  7. tmpfsをルートFSがマウントされていた場所にmoveマウント

これらのことをinitramfs上で行っている。

ルートFSを構成するすべてのファイルをtmpfsでマウントされた場所にコピーすることで、ルートFSがRAM上に置かれるということを行っている。

Yocto Projectのinitramfs

Yocto Projectのinitramfsの使い方については以前紹介したとおり。

/init.dに格納されたスクリプトが順に実行されていく。90-rootfsで最終的にルートFSとなるディレクトリツリーが/rootfsに展開される。

91-toramなどのスクリプトを作成して、この/rootfsの内容をtmpfsにコピー、マウントポイントを入れ替えるようにmoveマウントすればRAM上で動作するLinuxは実現できそうだ。

環境構築

YoctoProject ラズベリーパイでinitramfsを参考に環境を作成していく。

作業環境

$ mkdir -p ~/yocto/rpi-scarthgap
$ cd ~/yocto/rpi-scarthgap

pokyの取得

$ git clone -b scarthgap git://git.yoctoproject.org/poky.git

環境変数の設定

$ source poky/oe-init-build-env

meta-raspberrypiの取得

$ bitbake-layers layerindex-fetch meta-raspberrypi

local.confを編集

initramfsをバンドルしたカーネルで起動するイメージを作成する。 rootfsモジュールが実行された後にシェルにフォールバックする設定を追加している。

MACHINE = "raspberrypi4-64"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

# Bundle initramfs
INITRAMFS_IMAGE = "core-image-minimal-initramfs"
INITRAMFS_IMAGE_BUNDLE = "1"
BOOT_SPACE = "1073741"
INITRAMFS_MAXSIZE = "315400"
IMAGE_FSTYPES_pn-${INITRAMFS_IMAGE} = "${INITRAMFS_FSTYPES}"

# fallback to shell in the initramfs
INITRAMFS_SCRIPTS = "\
                      initramfs-framework-base \
                      initramfs-module-debug \
                    bc \
 "
CMDLINE_DEBUG = "debug shell=after:rootfs"

IMAGE_BOOT_FILES = "${BOOTFILES_DIR_NAME}/* \
                 ${@make_dtb_boot_files(d)} \
               ${KERNEL_IMAGETYPE}-${INITRAMFS_LINK_NAME}.bin;${SDIMG_KERNELIMAGE} \
"

ビルド

RAM上に展開するので一番小さいイメージを作成する。

$ bitbake core-image-minimal

マイクロSDの作成

出来上がったwicイメージをSDカードに書き込む。

$ pushd ./tmp/deploy/images/raspberrypi4-64
$ sudo bmaptool copy ./core-image-minimal-raspberrypi4-64.wic.bz2 /dev/sdX

ディスクレスの確認

手動でディスクレス設定

作成したイメージでターゲットを輝度すると、rootfsモジュールが実行された後医にinitramfsのシェルに入る。 そこで下記のコマンドを実行する。

$ size=$(du -sk "/rootfs" | awk '{print $1}')
$ size=$(echo "($size * 1.3)/1" | bc)
$ mkdir -p /dev/shm
$ mount -t tmpfs -o size="$size"k tmpfs /dev/shm
$ cp -a /rootfs/* /dev/shm
$ umount /rootfs
$ mount -o move /dev/shm /rootfs
$ sed -i 's|/dev/mmcblk0p1|#/dev/mmcblk0p1|' /rootfs/etc/fstab
$ exit

そのままではSDカードのブートパーティション/bootにマウントされてしまうので、/rootfs/etc/fstabを書き換えている。

ディスクレスの動作確認

rootでログインしてmountコマンドを実行するとSDカード(/dev/mmcblk1pX)がマウントされていないことが確認できる。

Poky (Yocto Project Reference Distro) 5.0.7 raspberrypi4-64 ttyS0

raspberrypi4-64 login: root

WARNING: Poky is a reference Yocto Project distribution that should be used for
testing and development purposes only. It is recommended that you create your own distribution for production use.

root@raspberrypi4-64:~# mount
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=1664796k,nr_inodes=416199,mode=755)
tmpfs on / type tmpfs (rw,relatime,size=103444k)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,relatime,gid=5,mode=620,ptmxmode=666)
tmpfs on /run type tmpfs (rw,nosuid,nodev,size=776204k,nr_inodes=819200,mode=755)
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursivepr)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,nr_inodes=1048576)
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /var/volatile type tmpfs (rw,relatime)

この状態でSDカードを抜くと、割り込みが上がりSDカードが抜かれたことが検出される。

root@raspberrypi4-64:~# [  272.895023] mmc0: card 1234 removed

その後、lsなどでファイルシステムにアクセスしても問題なく動作する。

root@raspberrypi4-64:/# ls /
bin         etc         lost+found  proc        sbin        tmp
boot        home        media       root        srv         usr
dev         lib         mnt         run         sys         var

この状態で試しに電源LED(赤)を点滅させてみるときちんと動作することが確認できる。

root@raspberrypi4-64:/# echo timer > /sys/class/leds/PWR/trigger

レシピ化

実機上でディスクレスが実現できることが確認できたのでこれをレシピ化する。

レイヤの作成

Casperのtoram機能を模倣するのでmeta-toramを作成する。

$ bitbake-layers create-layer -p10 -a ../poky/meta-toram

レシピの作成

格納場所

他のinitramfsモジュールと同じ構成する。

$ mkdir -p ../poky/meta-toram/recipes-core/initrdscripts/initramfs-module-toram

スクリプト

実際に処理を行うスクリプトmeta-toram/recipes-core/initrdscripts/initramfs-module-toram配下にtoram.shとして作成する。

#!/bin/sh -e

toram_enabled() {
    return 0
}

toram_run() {
    size=$(du -sk "$ROOTFS_DIR" | awk '{print $1}')
    size=$(echo "($size * 1.3)/1" | bc)

    mkdir -p /dev/shm
    mount -t tmpfs -o size="$size"k tmpfs /dev/shm
    cp -a $ROOTFS_DIR/* /dev/shm
    umount $ROOTFS_DIR
    mount -o move /dev/shm $ROOTFS_DIR
}

レシピ

レシピはmeta-toram/recipes-core/initrdscripts配下に,initramfs-module-toram_0.1.bbとして作成する。initramfs上に本番用のルートFSをマウントするモジュールが90-rootfsなので、それより後にtoram.shを動作させるために91-toramとしてインストールしている。また、bcコマンドを使用するので依存関係を設定している。

SUMMARY = "initramfs-framework module for toram"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
RDEPENDS:${PN} = "initramfs-framework-base bc"

SRC_URI = "file://toram.sh"

S = "${WORKDIR}"

do_install() {
    install -d ${D}/init.d
    install -m 0755 ${S}/toram.sh ${D}/init.d/91-toram
}

FILES:${PN} = "/init.d/91-toram"

wksファイル

手動で試したときには/bootがマウントされるのを回避するためにfstabを修正した。 しかし、toram実現の処理とは直接関係ないためtoram.shで実装することは避ける。

/bootがマウントされるのはmeta-raspberrypiのsdimage-raspberrypi.wksで下記の行が記述されているため。

part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --size 100

格納ディレクトリを作成する。

$ mkdir -p ../poky/meta-toram/wic

sdimage-raspberrypi-toram.wksを以下の内容で作成する。

ブートパーティションの行からマウントポイントの設定を削除している。

part --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --size 100
part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4096

ビルド

local.conf

local.confのディスクレス設定の追加部分は下記のようになる。

MACHINE = "raspberrypi4-64"

# enable uart
ENABLE_UART = "1"

# systemd
INIT_MANAGER = "systemd"

# Bundle initramfs
INITRAMFS_IMAGE = "core-image-minimal-initramfs"
INITRAMFS_IMAGE_BUNDLE = "1"
BOOT_SPACE = "1073741"
INITRAMFS_MAXSIZE = "315400"
IMAGE_FSTYPES_pn-${INITRAMFS_IMAGE} = "${INITRAMFS_FSTYPES}"

# fallback to shell in the initramfs
INITRAMFS_SCRIPTS = "\
        initramfs-module-toram \
 "
CMDLINE_DEBUG = "debug shell=after:rootfs"

IMAGE_BOOT_FILES = "${BOOTFILES_DIR_NAME}/* \
        ${@make_dtb_boot_files(d)} \
  ${KERNEL_IMAGETYPE}-${INITRAMFS_LINK_NAME}.bin;${SDIMG_KERNELIMAGE} \
"

WKS_FILE = "sdimage-raspberrypi-toram.wks"

ビルド

$ bitbake core-image-minimal

動作確認

mmcblk1がマウントされていないことが確認できる。

root@raspberrypi4-64:~# mount
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=1664796k,nr_inodes=416199,mode=755)
tmpfs on / type tmpfs (rw,relatime,size=103112k)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,relatime,gid=5,mode=620,ptmxmode=666)
tmpfs on /run type tmpfs (rw,nosuid,nodev,size=776204k,nr_inodes=819200,mode=755)
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursivepr)
bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,nr_inodes=1048576)
configfs on /sys/kernel/config type configfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /var/volatile type tmpfs (rw,relatime)

まとめ

起動時にルートFSの内容をtmpfsにコピーしてそこをルートFSとしてマウントすることで、システム全体をRAM上で動作扠せられることを確認した。 今回はブートにSDカードを使用したが、ネットワークからブートするなどシーケンスを工夫すれば完全にディスクレスでブートできる可能性がある。 tmpfsにコピーできれば良いので、ルートFSはsquashfsイメージなどでも良い。

initramfs自体自由度が高いため実装によっては様々な構成に対応することができる。

Yocto Projectのinitramfsのフレームワークはわかりやすい実装になっているため、比較的簡単にこのようなことが実現できるのは興味深い。




以上の内容はhttps://mickey-happygolucky.hatenablog.com/entry/2025/03/13/084357より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14