2021年起的 RaspberryPi 官网提供的 RaspiOS 内置了 Overlay 模式,启用或者停用设置在 raspi-config 中进行。具体方法为在 shell 中运行 sudo raspi-config。
#-----------------------------------------------------------------------------------------------------------------------------
复制下面内容,保存为readme.txt,使用时按照本文档说明实施。
脚本用途
============================================================
将树莓派系统在可读写模式和只读模式下转换。
使用场景
============================================================
按照通常的方式,树莓派系统安装于TF/SD卡模式时,系统运行过程中,
可能会频繁的对TF/SD卡做读写操作,容易造成TF/SD卡的损坏。
将系统设定为只读模式后,所有的读写操作均只在内存中进行,不需要
对TF/SD卡进行写,能有效延长TF/SD卡的使用寿命。
只读模式适用于用树莓派做固定控制时使用。
支持环境
============================================================
硬件:RaspberryPi B+
软件:raspbian-jessie
当前脚本只对采用/dev/mmcblk0px方式索引磁盘分区的场景有效,对
采用PARTUUID方式索引磁盘分区时,脚本无效,待后续研究。
脚本组成
============================================================
overlay_enable.py 将系统由读写状态切换至只读状态,重启后生效。
overlay_disable.py将系统由只读状态切换至读写状态,重启后生效。
脚本使用
============================================================
在读写模式下,命令行执行
sudo python3 overlay_enable.py
或运行
chmod +x overlay_enable.py
设置为可执行后执行
sudo ./overlay_enable.py
将系统由读写状态切换至只读状态,重启后生效。
在只读模式下,命令行执行
sudo python3 overlay_disable.py
或运行
chmod +x overlay_disable.py
设置为可执行后执行
sudo ./overlay_disable.py
将系统由只读状态切换至读写状态,重启后生效。
只读状态下临时启用可写
============================================================
在命令行执行
sudo mount -o remount,rw /boot
将使/boot分区进入可写模式,操作完毕运行
sudo mount -o remount,ro /boot
将/boot分区切换回只读状态。
在命令行执行
sudo mount -o remount,rw /ro
将使/ro分区进入可写模式,操作完毕运行
sudo mount -o remount,ro /ro
将/ro分区切换回只读状态。
注:使用本脚本将系统切换进只读模式后,/dev/mmcblk0p2挂载到
/ro,故要使/dev/mmcblk0p2可写,需要对/ro进行重新挂载。
要保存的对/dev/mmcblk0p2中文件的修改,都需要通过对/ro下对应
位置的文件修改来实现。
补充说明
============================================================
Raspbian全系列官方下载地址
https://downloads.raspberrypi.org/raspbian_lite/images/
官方网站放出的最晚一版raspbian-jessie为
2017-07-05-raspbian-jessie
该版本烧写到TF/SD卡后,采用PARTUUID方式进行磁盘分区索引。
为适用本脚本,需要将磁盘索引更改为/dev/mmcblk0px方式,更改后
系统可以正常运行(raspbian-stretch及以后版本貌似不行)。
需要更改的位置有:
/boot/cmdline.txt 中,root=PARTUUID=....-02 更改为 root=/dev/mmcblk0p2
/etc/fstab中,PARTUUID=....-01更改为/dev/mmcblk0p1
PARTUUID=....-02更改为/dev/mmcblk0p2
先完成更改再运行脚本,脚本依赖前面修改的内容。
参考
============================================================
本脚本在
https://blog.csdn.net/zhufu86/article/details/78906046
介绍方法的基础上完成。
复制下面内容为 overlay_enable.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 21 15:20:19 2019
@author: farman
"""
import os
def execute(command):
return os.system(command)
def step_1_disable_swap_files():
flag = 0
flag |= execute("sudo dphys-swapfile swapoff")
flag |= execute("sudo dphys-swapfile uninstall")
flag |= execute("sudo update-rc.d dphys-swapfile remove")
return flag
def step_2_update_system():
flag = 0
flag |= execute("sudo apt update")
flag |= execute("sudo apt upgrade -y")
return flag
def step_3_make_shell_script():
content = '''#!/bin/sh
# Read-only Root-FS for Raspian using overlayfs
# Version 1.0
#
# Created 2017 by Pascal Suter @ DALCO AG, Switzerland
# to work on Raspian as custom init script
# (raspbian does not use an initramfs on boot)
#
# Modified 2017-Apr-21 by Tony McBeardsley
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
#
# Tested with Raspbian mini, 2017-01-11
#
# This script will mount the root filesystem read-only and overlay it with a temporary tempfs
# which is read-write mounted. This is done using the overlayFS which is part of the linux kernel
# since version 3.18.
# when this script is in use, all changes made to anywhere in the root filesystem mount will be lost
# upon reboot of the system. The SD card will only be accessed as read-only drive, which significantly
# helps to prolong its life and prevent filesystem coruption in environments where the system is usually
# not shut down properly
#
# Install:
# copy this script to /sbin/overlayRoot.sh and add "init=/sbin/overlayRoot.sh" to the cmdline.txt
# file in the raspbian image's boot partition.
# I strongly recommend to disable swapping before using this. it will work with swap but that just does
# not make sens as the swap file will be stored in the tempfs which again resides in the ram.
# run these commands on the booted raspberry pi BEFORE you set the init=/sbin/overlayRoot.sh boot option:
# sudo dphys-swapfile swapoff
# sudo dphys-swapfile uninstall
# sudo update-rc.d dphys-swapfile remove
#
# To install software, run upgrades and do other changes to the raspberry setup, simply remove the init=
# entry from the cmdline.txt file and reboot, make the changes, add the init= entry and reboot once more.
fail(){
echo -e "$1"
/bin/bash
}
# Load overlay module
modprobe overlay
if [ $? -ne 0 ]; then
fail "ERROR: missing overlay kernel module"
fi
# Mount /proc
mount -t proc proc /proc
if [ $? -ne 0 ]; then
fail "ERROR: could not mount proc"
fi
# Create a writable fs on /mnt to then create our mountpoints
mount -t tmpfs inittemp /mnt
if [ $? -ne 0 ]; then
fail "ERROR: could not create a temporary filesystem to mount the base filesystems for overlayfs"
fi
# Mount a tmpfs under /mnt/rw
mkdir /mnt/rw
mount -t tmpfs root-rw /mnt/rw
if [ $? -ne 0 ]; then
fail "ERROR: could not create tempfs for upper filesystem"
fi
# Identify root fs device, PARTUUID, mount options and fs type
#rootDev=`blkid -o list | awk '$3 == "/" {print $1}'`
# Changed here(point to / ) in case the cmd above doesn't work # By ChenYang 20171122
rootDev=/dev/mmcblk0p2
rootPARTUUID=`awk '$2 == "/" {print $1}' /etc/fstab`
rootMountOpt=`awk '$2 == "/" {print $4}' /etc/fstab`
rootFsType=`awk '$2 == "/" {print $3}' /etc/fstab`
# Mount original root filesystem readonly under /mnt/lower
mkdir /mnt/lower
mount -t ${rootFsType} -o ${rootMountOpt},ro ${rootDev} /mnt/lower
if [ $? -ne 0 ]; then
fail "ERROR: could not ro-mount original root partition"
fi
# Mount the overlay filesystem
mkdir /mnt/rw/upper
mkdir /mnt/rw/work
mkdir /mnt/newroot
mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work overlayfs-root /mnt/newroot
if [ $? -ne 0 ]; then
fail "ERROR: could not mount overlayFS"
fi
# Create mountpoints inside the new root filesystem-overlay
mkdir /mnt/newroot/ro
mkdir /mnt/newroot/rw
# Remove root mount from fstab (this is already a non-permanent modification)
grep -v "$rootPARTUUID" /mnt/lower/etc/fstab > /mnt/newroot/etc/fstab
echo "#the original root mount has been removed by overlayRoot.sh" >> /mnt/newroot/etc/fstab
echo "#this is only a temporary modification, the original fstab" >> /mnt/newroot/etc/fstab
echo "#stored on the disk can be found in /ro/etc/fstab" >> /mnt/newroot/etc/fstab
# Change to the new overlay root
cd /mnt/newroot
pivot_root . mnt
exec chroot . sh -c "$(cat <<END
# Move ro and rw mounts to the new root
mount --move /mnt/mnt/lower/ /ro
if [ $? -ne 0 ]; then
echo "ERROR: could not move ro-root into newroot"
/bin/bash
fi
mount --move /mnt/mnt/rw /rw
if [ $? -ne 0 ]; then
echo "ERROR: could not move tempfs rw mount into newroot"
/bin/bash
fi
# Unmount unneeded mounts so we can unmout the old readonly root
umount /mnt/mnt
umount /mnt/proc
umount /mnt/dev
umount /mnt
# Continue with regular init
exec /sbin/init
END
)"
'''
with open('/boot/cmdline.txt', 'r') as f:
line = f.read()
begin = line.find('root=') + len('root=')
end = line.find(' ', begin)
part = line[begin:end]
content = content.replace("rootDev=/dev/mmcblk0p2", "rootDev="+part)
with open("/home/pi/overlayroot.sh", 'w') as f:
f.write(content)
flag = False
flag |= execute("sudo mv /home/pi/overlayroot.sh /sbin/overlayroot.sh")
flag |= execute("sudo chmod +x /sbin/overlayroot.sh")
return flag
def step_4_edit_cmdline():
with open("/boot/cmdline.txt", "r") as f:
line = f.read()
tail = "init=/sbin/overlayroot.sh"
if line.find(tail) == -1:
line = line.rstrip('\n')
line += ' '
line += tail
with open("/boot/cmdline.txt", 'w') as f:
f.write(line)
return
def step_5_edit_fstab():
lines = []
with open('/etc/fstab', 'r') as f:
for line in f:
lines.append(line.rstrip('\n'))
with open('/etc/fstab', 'w') as f:
for line in lines:
if line.find('/boot') > -1:
segs = line.split()
if segs[3].find('ro') == -1:
segs[3] += ',ro'
line = ' '.join(segs)
f.write(line + '\n')
return
def step_6_set_prompt():
with open('/home/pi/.bashrc', 'r') as f:
content = f.read()
if content.find('overlay') == -1:
content += '\n'
content += '''
find_overlay=`mount | grep overlay`
if [ ${#find_overlay} -gt 0 ]; then
PS1="[OVL] $PS1"
fi
'''
with open('/home/pi/.bashrc', 'w') as f:
f.write(content)
return
if __name__ == '__main__':
step_1_disable_swap_files()
#step_2_update_system()
step_3_make_shell_script()
step_4_edit_cmdline()
step_5_edit_fstab()
step_6_set_prompt()
复制下面代码,保存为overlay_disable.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 21 15:20:19 2019
@author: farman
"""
import os
def execute(command):
return os.system(command)
def step_1_make_partition_writable():
execute("sudo mount -o remount,rw /boot")
execute("sudo mount -o remount,rw /ro")
return
def step_2_edit_cmdline():
with open("/boot/cmdline.txt", "r") as f:
line = f.read()
tail = "init=/sbin/overlayroot.sh"
line = line.replace(tail, '')
with open("/boot/cmdline.txt", 'w') as f:
f.write(line)
return
def step_3_edit_fstab():
lines = []
with open('/ro/etc/fstab', 'r') as f:
for line in f:
lines.append(line.rstrip('\n'))
with open('/ro/etc/fstab', 'w') as f:
for line in lines:
if line.find('/boot') > -1:
segs = line.split(' ')
while segs.count(''):
segs.remove('')
segs[3] = segs[3].replace(',ro', '')
line = ' '.join(segs)
f.write(line + '\n')
return
if __name__ == '__main__':
step_1_make_partition_writable()
step_2_edit_cmdline()
step_3_edit_fstab()