書き込みはメモリへ書くようにしてみようかと思ってやってみた
一部のパーティションだけなら簡単なんだろうけどrootまるごとやろうと思うと
/をマウントする前に下準備をしないといけないですね
という訳でinitramfsを作ってみました
書き込みをメモリに逃がす方法は以下のページにあるようにいろいろありますが
今回は特にパッチも必要なさそうなdevice-mapperのsnapshotを使用する方法で試しました
・番外編: LiveCDのファイルシステム(1) - O'Reilly Japan Community Blog
目標としては root ファイルシステムがある /dev/sda2 は読み込み
書き込みは tmpfs で確保した領域に書き込みます
したがって再起動時に tmpfs が開放されるので再起動後は元の状態に戻ります
また必要に応じて通常通り /dev/sda2 を書き込みでマウントする起動方法も選べるようにします
device-mapper で扱うにはブロックデバイスである必要があるみたいなので
最終的には以下のような構成になります
/dev/sda2 (ro) -------+ | tmpfs -> cow.img (rw) ---> /dev/mapper/rootfs (rw) -> / としてマウント
まずカーネルで以下の項目を有効にする必要があります
当然全て組み込みにしておいたほうがいいと思います
General setup -> Initial RAM filesystem and RAM disk (initramfs/initrd) support Device Drivers -> Generic Driver Options -> Maintain a devtmpfs filesystem to mount at /dev Device Drivers -> Multiple devices driver support (RAID and LVM) -> Device mapper support Device Drivers -> Multiple devices driver support (RAID and LVM) -> Device mapper support -> Snapshot target
次に必要なパッケージをインストールします
# emerge -v app-arch/cpio dev-embedded/u-boot-tools sys-fs/lvm2 # USE=static emerge -v sys-apps/busybox
これで必要なものは一式揃ったので initramfs のシステムの作成とスクリプトを書きます
以下のページが非常に参考になります
・Initramfs - Gentoo Linux Wiki
・Early Userspace Mounting - Gentoo Wiki
ディレクトリ構造を /usr/src/initramfs 以下に作成していきます
# mkdir /usr/src/initramfs # cd /usr/src/initramfs
まず必要なディレクトリの作成
/mnt/root に最終的な root ファイルシステムをマウント
/mnt/cowfs は tmpfs をマウントしてその中にディスクイメージを作成します
# mkdir -p bin dev etc mnt/cowfs mnt/root proc sbin sys
次に必要なコマンドをコピー
busybox と dmsetup のスタティックビルドされているものが必要です (ldd で確認しています)
# which busybox /bin/busybox # ldd /bin/busybox not a dynamic executable # cp /bin/busybox bin/ # which dmsetup.static /sbin/dmsetup.static # ldd /sbin/dmsetup.static not a dynamic executable # cp /sbin/dmsetup.static sbin/
コマンドはコピーしたのであとは init を書きます
今回は以下のようにしました
#!/bin/busybox sh rescue_shell() { echo fallback to busybox shell exec /bin/sh } mount -t proc none /proc || rescue_shell mount -t sysfs none /sys || rescue_shell mount -t devtmpfs none /dev || rescue_shell /bin/busybox --install -s /bin for c in $(cat /proc/cmdline); do case $c in root=*) ROOT=$(echo $c | cut -d= -f2-) ;; rootdelay=*) DELAY=$(echo $c | cut -d= -f2-) ;; snapshot) SNAPSHOT=y ;; esac done if [ -z "$ROOT" ]; then echo root is empty rescue_shell fi if [ -n "$DELAY" ]; then echo waiting for rootfs $DELAY sec sleep $DELAY fi if [ -z "$SNAPSHOT" ]; then mount -o ro $ROOT /mnt/root || rescue_shell else mount -t tmpfs none /mnt/cowfs || rescue_shell dd if=/dev/zero of=/mnt/cowfs/cow.img bs=1 count=1 seek=$(blockdev --getsize64 $ROOT) || rescue_shell LOOP=$(losetup -f) losetup $LOOP /mnt/cowfs/cow.img dmsetup.static create rootfs --table "0 $(blockdev --getsz $ROOT) snapshot $ROOT $LOOP P 0" || rescue_shell mount /dev/mapper/rootfs /mnt/root || rescue_shell if [ ! -d /mnt/root/mnt/cowfs ]; then mkdir -p /mnt/root/mnt/cowfs fi sed -i "s|^$ROOT|#\0|" /mnt/root/etc/fstab mount -o remount,ro /dev/mapper/rootfs /mnt/root || rescue_shell mount --move /mnt/cowfs /mnt/root/mnt/cowfs || rescue_shell fi umount proc sys dev exec switch_root /mnt/root /sbin/init
肝心な部分は以下で
else mount -t tmpfs none /mnt/cowfs || rescue_shell dd if=/dev/zero of=/mnt/cowfs/cow.img bs=1 count=1 seek=$(blockdev --getsize64 $ROOT) || rescue_shell LOOP=$(losetup -f) losetup $LOOP /mnt/cowfs/cow.img dmsetup.static create rootfs --table "0 $(blockdev --getsz $ROOT) snapshot $ROOT $LOOP P 0" || rescue_shell mount /dev/mapper/rootfs /mnt/root || rescue_shell if [ ! -d /mnt/root/mnt/cowfs ]; then mkdir -p /mnt/root/mnt/cowfs fi sed -i "s|^$ROOT|#\0|" /mnt/root/etc/fstab mount -o remount,ro /dev/mapper/rootfs /mnt/root || rescue_shell mount --move /mnt/cowfs /mnt/root/mnt/cowfs || rescue_shell fi
- まず /mnt/cowfs に tmpfs をマウント
- 次に dd でスパースファイルを作成、この際 root のデバイスと同じサイズにする
(ここは tmpfs と同じサイズにしたほうがいいかもしれない) - losetup を使用して作成したディスクイメージをブロックデバイスとひもづけ
- dmsetup の snapshot を使用して新たなデバイスを作成
(成功すると /dev/mapper/rootfs が作成される) - 起動後のシステムから /mnt/cowfs が見えるようにするために一度 rw でマウントしてディレクトリを作成
- /etc/fstab を編集して root をマウントしないようにする
- ro でリマウント (必要ないかも)
- mount --move を使用して /mnt/cowfs を起動後も見える位置へ移動
というような具合になっています
起動時のカーネルのコマンドラインに snapshot を付けるとここに入ってきて付けなければ
従来通り rw でマウントして起動するようになっています (if の部分)
init を忘れず実行可能にして必要なファイルは揃ったのでアーカイブにまとめてイメージを作成します
PC の場合は cpio.gz のファイルで起動できた気がするのですが U-Boot はそれだけではダメで
更にイメージに変換する必要がありました
・Gentoo Forums :: View topic - [Solved] Help with initramfs for Marvell OpenRD-Client LE
# chmod +x init # find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz # mkimage -A arm -O linux -T ramdisk -C gzip -n "Gentoo Busybox" -d ../initramfs.cpio.gz /boot/initramfs.kwb
以上でようやく必要なファイルが全部揃いました
上記で /boot/initramfs.kwb にコピーしていますが /boot は /dev/sda1 です
U-boot でこのファイルを読み込んで起動するように設定すれば完了です
カーネルの引数に snapshot を追加するのと initramfs の読み込み
bootm に kernel initramfs dtb の順にアドレスを渡すようにします
以下に自分の環境の例をあげます
Marvell>> print baudrate=115200 bootcmd=${x_bootcmd_usb}; ${x_bootcmd_kernel}; ${x_bootcmd_init}; ${x_bootcmd_dtb}; setenv bootargs ${x_bootargs} ${x_bootargs_snapshot}; bootm 0x6400000 0x6700000 0x6900000 bootdelay=3 eth1addr=xx:xx:xx:xx:xx:xx ethact=egiga0 ethaddr=xx:xx:xx:xx:xx:xx stderr=serial stdin=serial stdout=serial x_bootargs=console=ttyS0,115200 root=/dev/sda2 rootdelay=3 x_bootargs_snapshot=snapshot x_bootcmd_dtb=ext2load usb 0 0x6900000 kirkwood-dreamplug.dtb x_bootcmd_init=ext2load usb 0 0x6700000 initramfs.kwb x_bootcmd_kernel=ext2load usb 0 0x6400000 uImage x_bootcmd_usb=usb start Environment size: 618/4092 bytes
と、これでおおよそ期待通りに動いているんですが
いくつか問題があります
まずメモリには限りがあるのでそれを超えるデータを書き込んだ場合正常に動作しなくなります
例えばディスクの空き領域が1Gでメモリ上に確保した領域が10Mだった場合
書き込みは全てメモリへ行われるので当然10MBまでしか書き込めません
システムはまだ990MB空いていると思っているのでおかしなことになるのだと思われます
他に、新たにファイルを作成してメモリ上へ行われた書き込みは
ファイルを削除しても開放されないという事です
ディスクイメージをスパースファイルで作成しましたがこれの動作によるものです
なので10MBのファイルを作成したあとにそれを削除してもメモリは確保されたままです
これによってひとつめの問題が起こりやすくなっています
最後に、終了時に /mnt/cowfs をアンマウントできないと言われます
これはまぁ当然と言えば当然なのですが (/ より上位でマウントしているため)
/ をアンマウントしてからでないとアンマウントできないですよね
これについてはもしかしたら解決方法があるのかもしれないですが今のところわかっていません
0 件のコメント:
コメントを投稿