警告: 这篇教程介绍的方法是在系统运行时直接替换关键系统文件,包括内核和整个根文件系统。这是一个高风险操作,可能导致系统崩溃、数据丢失或无法启动。请确保您了解其风险,并已备份所有重要配置。官方推荐使用 sysupgrade 工具。

一、前言:为何选择手动升级?

在标准的 OpenWrt/ImmortalWrt 升级流程中,我们通常使用 sysupgrade 命令。然而,对于某些特殊的 x86/64 部署环境或特定的定制需求,您可能需要手动控制文件替换的每一个步骤,例如在双分区或特殊引导配置下。

本文将为您提供一个 Shell 脚本,用于实现手动升级根文件系统和内核。

二、手动升级脚本

将以下代码保存为 /root/manual_upgrade.sh,并给予执行权限:chmod +x /root/manual_upgrade.sh。

#!/bin/sh

# ----------------------------------------------------

# !!! 极高风险警告 !!!

# 在运行中的系统上直接覆盖根文件系统是高风险操作。

# 此脚本已包含兼容性修复和用户确认步骤。请务必提前备份重要数据。

# ----------------------------------------------------

# 函数:请求用户确认

confirm_action() {

while true; do

read -r -p "=> ${1} (y/N)? " response

case "$response" in

[yY][eE][sS]|[yY])

return 0

;;

[nN][oO]|[nN]|"")

echo "操作已取消。"

exit 1

;;

*)

echo "请输入 'y' 或 'n'."

;;

esac

done

}

# ====================================================

# 配置变量 (重要:请根据您的升级目标进行修改)

# ====================================================

# 目标发行版名称 (例如: openwrt, immortalwrt)

PROJECT="immortalwrt"

# 目标版本号 (例如: 24.10.3)

VERSION="24.10.3"

# 目标架构路径 (URL中使用: x86/64)

ARCH_PATH="x86/64"

# 目标架构名称 (文件名中使用: x86-64)

ARCH_NAME="x86-64"

# 构造 URL 和文件名

BASE_URL="https://downloads.${PROJECT}.org/releases/${VERSION}/targets/${ARCH_PATH}"

KERNEL_FILE="${PROJECT}-${VERSION}-${ARCH_NAME}-generic-kernel.bin"

ROOTFS_FILE="${PROJECT}-${VERSION}-${ARCH_NAME}-rootfs.tar.gz"

KERNEL_URL="${BASE_URL}/${KERNEL_FILE}"

ROOTFS_URL="${BASE_URL}/${ROOTFS_FILE}"

echo "----------------------------------------------------"

echo "目标版本信息: ${PROJECT} ${VERSION} (${ARCH_NAME})"

echo "内核下载链接: ${KERNEL_URL}"

echo "根文件系统下载链接: ${ROOTFS_URL}"

echo "----------------------------------------------------"

confirm_action "确认上述升级目标信息正确,并开始下载文件"

# ----------------------------------------------------

# 步骤 1: 导出用户安装的非内核模块软件包列表

# ----------------------------------------------------

# 仅保留用户手动安装的非内核模块和非基础系统包,以备后续重新安装。

opkg list-installed | \

awk '{print $1}' | \

grep -v '^kmod-' | \

grep -v '^lib' | \

grep -v '^base-files' > /root/user-packages.txt

echo "已导出用户软件包列表到 /root/user-packages.txt"

# ----------------------------------------------------

# 步骤 2 & 3: 下载文件并解压

# ----------------------------------------------------

cd /tmp

echo "正在下载文件..."

wget --no-check-certificate "${KERNEL_URL}"

wget --no-check-certificate "${ROOTFS_URL}"

mkdir -p /tmp/new_rootfs

echo "正在解压新的根文件系统..."

tar -xvzf "${ROOTFS_FILE}" -C /tmp/new_rootfs

# ----------------------------------------------------

# 步骤 4: 替换内核文件 (高风险操作)

# ----------------------------------------------------

echo "----------------------------------------------------"

confirm_action "警告:将替换 /boot/vmlinuz 内核文件。这将影响系统重启。是否继续"

# 备份旧版本内核文件

cp /boot/vmlinuz /boot/vmlinuz.old

echo "已备份旧内核到 /boot/vmlinuz.old"

# 覆盖新内核

cp "/tmp/${KERNEL_FILE}" /boot/vmlinuz

echo "新内核文件已复制到 /boot/vmlinuz"

# ----------------------------------------------------

# 步骤 5: 处理配置文件

# ----------------------------------------------------

# 删除新 rootfs 中的默认配置,以保留当前系统的配置 (假定旧配置兼容新版本)

echo "正在删除新 rootfs 中的默认配置文件..."

rm -rf /tmp/new_rootfs/etc/config

rm -f /tmp/new_rootfs/etc/passwd \

/tmp/new_rootfs/etc/shadow \

/tmp/new_rootfs/etc/group \

/tmp/new_rootfs/etc/hostname \

/tmp/new_rootfs/etc/hosts \

/tmp/new_rootfs/etc/resolv.conf

# ----------------------------------------------------

# 步骤 6: 覆盖根文件系统 (最核心、最危险的步骤)

# ----------------------------------------------------

echo "----------------------------------------------------"

echo "!! 最终警告: 接下来将覆盖正在运行的系统文件。"

echo "!! 此过程不可逆,可能导致系统在运行时崩溃或无法启动。"

echo "----------------------------------------------------"

confirm_action "是否确认覆盖根文件系统 (将排除只读目录) 并继续升级"

echo "开始复制文件到根文件系统,排除只读/虚拟目录..."

# 排除目录列表。避免复制到只读的 SquashFS 基础层、虚拟文件系统或特殊的挂载点。

EXCLUDE_DIRS="dev proc sys tmp overlay rom mnt media boot"

EXCLUDE_FILE="/tmp/tar_excludes.txt"

# 1. 创建排除文件 (使用通配符来匹配目录下的所有内容,兼容 BusyBox tar -X)

echo "" > "${EXCLUDE_FILE}"

for dir in $EXCLUDE_DIRS; do

echo "${dir}/*" >> "${EXCLUDE_FILE}" # 排除目录下的所有内容

echo "${dir}" >> "${EXCLUDE_FILE}" # 排除目录本身

done

# 2. 使用兼容 BusyBox 的 tar 命令进行复制。

# 打包 /tmp/new_rootfs 并排除列表中的目录,然后通过管道解压到 / 根目录。

# -p 选项用于保留文件权限。

tar -C /tmp/new_rootfs -cf - -X "${EXCLUDE_FILE}" . | tar -x -p -C /

echo "根文件系统文件覆盖完成。"

# 清理临时文件和目录

rm -f "${EXCLUDE_FILE}"

rm -rf /tmp/new_rootfs

rm -f "/tmp/${KERNEL_FILE}" "/tmp/${ROOTFS_FILE}"

# ----------------------------------------------------

# 步骤 7: 重新安装用户软件包 (兼容 BusyBox xargs)

# ----------------------------------------------------

echo "正在更新软件包列表并重新安装用户软件包..."

opkg update

# 重新安装导出的非内核模块软件包

if [ -s /root/user-packages.txt ]; then

echo "正在重新安装用户软件包..."

# 使用 cat 将文件内容通过管道传给 xargs,以兼容 BusyBox 的 xargs

cat /root/user-packages.txt | xargs -r opkg install

else

echo "用户软件包列表为空,跳过重新安装。"

fi

echo "用户软件包重新安装完成。"

# 清理 opkg 临时文件

rm -f /root/user-packages.txt

# ----------------------------------------------------

# 步骤 8: 完成并重启

# ----------------------------------------------------

echo "----------------------------------------------------"

echo "文件系统和内核替换完成。系统需要重启。"

echo "!!! 再次强调:重启后请立即手动安装所需的内核模块(kmod-),否则相关硬件功能可能失效。"

echo "----------------------------------------------------"

confirm_action "是否确认立即重启以完成升级过程"

reboot三、脚本执行流程概览配置与确认: 在脚本开头修改 PROJECT 和 VERSION 变量,然后确认下载链接。

准备软件包列表: 脚本会记录您当前系统上所有非内核模块、非基础文件的用户安装软件包,用于升级后重新安装。

下载与解压: 下载新的内核 (*-generic-kernel.bin) 和根文件系统 (*-rootfs.tar.gz),并解压根文件系统到 /tmp/new_rootfs。

配置文件保留: 删除新根文件系统中的默认配置文件(如 /etc/config、/etc/passwd 等),以保留您旧系统的配置。

替换内核: 备份旧内核并用新内核替换 /boot/vmlinuz。

覆盖根文件系统: 使用 tar 命令,排除 /dev、/proc、/sys、/rom 等只读或虚拟目录,将新的文件覆盖到根目录 (/)。

重新安装软件包: 更新 opkg 列表,然后重新安装在步骤 1 中导出的用户软件包。

重启: 提示用户确认后执行 reboot。

四、重启后的关键善后工作这是最重要的一步!

由于新内核的 ABI(应用程序二进制接口)与旧内核不兼容,您原先安装的所有内核模块(kmod-*)将无法在新系统上加载。

您必须在重启后执行以下操作:

连接网络: 确保您的网络接口(如果它们不依赖特殊的 kmod)可以正常工作。

安装内核模块: 根据您旧系统上的硬件功能,手动安装对应的内核模块。

例如,如果您之前安装了 USB 存储支持和 CIFS 文件系统:

codeBash

opkg update

opkg install kmod-usb-storage kmod-fs-cifs

# ... 其他您需要的 kmod 包如果您的系统功能在重启后无法正常工作(例如 Wi-Fi 或某些网口),请根据您的硬件型号和功能搜索对应的 kmod-* 包名并进行安装。

2026-01-17 00:12:19