☰
Android init.rc 修改实战:从 boot.img 解包到 ramdisk 编辑
2026/9/28 18:14:55 网站建设 项目流程

1. 项目概述:为什么你必须亲手改 init.rc,而不是靠“一键工具”

init.rc 是 Android 启动流程的总开关,它不是配置文件,而是整个系统服务生命周期的编排脚本。我第一次在高通平台调试一个 USB-C 音频设备时,发现内核已经正确识别了 codec,但 audio HAL 死活不加载——查日志发现audioserver进程根本没启动。翻遍/system/etc/init/下所有.rc文件,最终定位到init.rc里一句被注释掉的import /system/etc/init/hw/init.audio.rc。当时手边没有现成的 boot.img 修改工具,临时用abootimg解包、手动编辑、再打包,5 分钟搞定。这件事让我彻底明白:init.rc 不是“可以改”,而是“必须能改”——它决定了你的设备到底能不能开机、能不能联网、能不能识别外设、甚至能不能进 recovery。

这个标题里的“手把手”,不是教你怎么点鼠标,而是带你从零建立对 Android 启动链的肌肉记忆。boot.img 不是黑盒,它由三部分组成:头部(header)、kernel(zImage 或 Image)和 ramdisk(cpio.gz)。其中 ramdisk 才是 init.rc 的真实载体,它在内核启动后被解压到内存中作为根文件系统运行。很多人卡在第一步:用unmkbootimg解出来的 ramdisk 是个二进制 blob,直接vim打开全是乱码——那是因为它本质是一个 gzip 压缩的 cpio 归档。你真正要操作的,是解压后的纯文本 init.rc。

关键词 “Android”、“boot.img”、“init.rc”、“ramdisk”、“解压与打包” 不是孤立标签,而是一条完整的实操链路:Android 系统 → boot.img 容器 → ramdisk 载体 → init.rc 控制逻辑 → 修改后重新打包验证。后续所有操作,包括烧写、调试、甚至 OTA 升级失败排查,都绕不开这条链。尤其在定制 ROM、硬件适配、安全加固(比如禁用 adb root 权限)、或调试早期启动问题(如 kernel panic 前的最后日志)时,这是你唯一能触达的“第一行代码”。

适合谁学?不是只给 AOSP 编译老手。如果你是嵌入式工程师刚接手一款新开发板,需要让摄像头驱动在开机时自动加载;如果你是安全研究员想验证某个 SELinux 策略是否在 init 阶段生效;如果你是 App 开发者发现自己的后台服务总在开机后 30 秒才启动,想确认是不是被 init.rc 里的start on property:规则延迟了——这些场景,你都需要亲手打开 init.rc,看清每一行service、on、import背后的执行时机与依赖关系。这不是炫技,而是 Android 底层调试的生存技能。

2. 核心原理拆解:boot.img 结构、ramdisk 本质与 init.rc 的执行机制

2.1 boot.img 的真实结构:Header + Kernel + Ramdisk,缺一不可

boot.img 不是单一镜像,而是一个精心设计的容器格式,其结构由 Android 官方定义(AOSP 中system/core/mkbootimg/include/bootimg/bootimg.h)。我们常误以为它是“内核镜像”,其实它更像一个 ZIP 包:里面装着内核(kernel)、初始内存盘(ramdisk),以及一个描述它们位置和大小的头部(header)。

  • Header(头部):固定 2048 字节(2KB),包含 magic 字符串"ANDROID!"、内核偏移量(kernel_offset)、ramdisk 偏移量(ramdisk_offset)、内核大小(kernel_size)、ramdisk 大小(ramdisk_size)、命令行参数(cmdline)、页大小(page_size)等关键元数据。这个 header 是解析整个 boot.img 的钥匙——没有它,你连 kernel 和 ramdisk 在哪都不知道。abootimg工具读取的就是这个 header,它能告诉你当前 boot.img 使用的是 2K 还是 4K 页对齐,这对后续解包至关重要。

  • Kernel(内核):通常是压缩过的zImage(ARM32)或Image(ARM64),位于 header 之后。它负责初始化 CPU、内存、中断控制器等硬件,并挂载 ramdisk 为根文件系统。注意:kernel 本身不解析 init.rc,它只负责把 ramdisk 解压到内存并跳转到/init入口。

  • Ramdisk(初始内存盘):这才是 init.rc 的物理载体。它是一个cpio归档,再经gzip压缩(常见后缀.cpio.gz)。cpio是一种古老的归档格式,比 tar 更轻量,专为嵌入式系统设计。它的核心优势是:支持按需解压单个文件,无需解压整个归档。这正是 Android 快速启动的关键——内核只需解压/init和/init.rc就能开始执行,其他文件(如/sbin/adbd)可按需加载。

提示:page_size是理解 boot.img 对齐的关键。常见值有 2048(2K)、4096(4K)。如果页大小是 4096,那么 kernel 和 ramdisk 的起始地址必须是 4096 的整数倍。abootimg -i boot.img输出的pagesize字段就是这个值。很多打包失败报错Invalid boot image,根源就是新 ramdisk 大小导致整体镜像未按 page_size 对齐。

2.2 ramdisk 的本质:一个内存中的微型 Linux 根文件系统

ramdisk 不是“磁盘”,而是一块被内核分配的内存区域,上面挂载了一个极简的 Linux 文件系统。它之所以叫“ramdisk”,是因为它完全驻留在 RAM 中,断电即失,但启动速度极快。其内容结构高度标准化:

/ ├── init # 第一个用户空间进程,由 kernel 直接执行 ├── init.rc # 主配置文件,定义服务、动作、导入规则 ├── init.environ.rc # 环境变量设置(如 PATH) ├── default.prop # 系统属性(ro.build.type=eng) ├── sbin/ │ ├── adbd # Android Debug Bridge 守护进程 │ └── charger # 充电模式守护进程 ├── etc/ │ └── fstab.<device> # 分区挂载表(如 fstab.qcom) └── proc/ # 内核 procfs 挂载点(虚拟文件系统)

init.rc就是这个微型系统的“宪法”。它不是 shell 脚本,而是一种专用语法(Android Init Language),由init进程解析执行。其核心概念有三:

  • Actions(动作):以on <trigger>开头的代码块,定义在特定事件发生时执行的命令序列。例如on early-init在 init 初始化早期执行,on property:sys.boot_completed=1在系统启动完成时触发。

  • Services(服务):以service <name> <pathname>开头的声明,定义一个长期运行的进程。init负责启动、监控、重启(如果配置了restart)这些服务。audioserver、surfaceflinger、zygote全部在此定义。

  • Imports(导入):import /init.<xxx>.rc语句,用于模块化管理。主init.rc通常只做基础导入,具体功能(音频、蓝牙、电源管理)分散在/init.hardware.rc、/init.usb.rc等文件中。修改 init.rc,本质是调整这些服务的启动顺序、依赖关系或启动条件。

注意:init.rc的解析是单线程、顺序执行的。on init块里的命令会逐行执行,直到遇到下一个on或service。这意味着,如果你在on init里加了一行write /proc/sys/vm/swappiness 10,它会在所有service启动前就生效。这种确定性是底层调试的基石。

2.3 init.rc 的执行生命周期:从 kernel 跳转到 Zygote 启动的完整链条

理解 init.rc 的执行时机,比记住语法更重要。整个过程是严格时序化的:

  1. Kernel 加载并解压 ramdisk:内核启动后,根据 boot.img header 信息,将 ramdisk 数据从 flash 读入内存,并用gunzip解压,再用cpio解包到内存中的 rootfs。

  2. Kernel 执行/init:内核调用execve("/init", ...),启动 init 进程。此时/init是一个静态链接的二进制程序(/system/bin/init的精简版),不依赖任何动态库。

  3. Init 解析 init.rc:/init进程首先读取/init.rc,构建内部的 action 和 service 列表。它不会立即执行所有内容,而是等待触发器(trigger)。

  4. Early-init 阶段:init发出early-inittrigger,执行on early-init块。这里做最底层的初始化:创建/dev、/proc、/sys目录,挂载 tmpfs,设置 umask。此阶段禁止启动任何服务(service),因为/system还未挂载。

  5. Init 阶段:init发出inittrigger,执行on init块。这里设置环境变量、创建设备节点、加载 SELinux 策略。import语句在此阶段生效,将/init.hardware.rc等文件的内容合并进来。

  6. Late-init 阶段:init发出late-inittrigger,执行on late-init块。此时/system、/vendor已挂载完毕,init开始启动第一批关键服务:logd(日志守护进程)、servicemanager(HIDL 服务管理器)、hwservicemanager(HAL 服务管理器)。

  7. Zygote 启动:on late-init块中有一行start zygote,这会触发zygote服务的启动。zygote是 Android 应用进程的“孵化器”,它预加载 Dalvik/ART 运行时和核心 Java 类库,然后通过fork()快速创建新的应用进程。可以说,init.rc 的最后一行start zygote,才是 Android 用户界面真正的起点。

这个链条解释了为什么修改init.rc如此关键:它控制着从内核到应用框架的每一个“开关”。你想让某个驱动模块在late-init之前加载?改on early-init。你想让adbd只在充电时启动?改service adbd /sbin/adbd的disabled属性,并添加on property:sys.usb.config=adb触发器。一切皆可编程,前提是你懂这个链条。

3. 实操全流程:从提取、解压、编辑到重新打包、验证的每一步详解

3.1 准备工作:环境搭建与工具链选择(Linux/macOS 优先)

强烈建议在 Linux 或 macOS 上操作。Windows 的 WSL2 虽然可用,但cpio和gzip的路径处理、权限问题频发,会浪费大量时间在环境调试上。我用的是 Ubuntu 22.04 LTS,所有命令均在此验证。

必备工具清单(全部通过 apt/brew 安装):

  • abootimg:核心工具,用于解析、解包、重新打包 boot.img。它比unpackbootimg更可靠,支持更多厂商定制格式(如高通、联发科)。安装:sudo apt install abootimg(Ubuntu)或brew install abootimg(macOS)。

  • cpio:标准 GNU 工具,用于解包/打包 cpio 归档。几乎所有 Linux 发行版自带,macOS 需brew install cpio。

  • gzip:标准压缩工具,用于解压/压缩 ramdisk。同样普遍预装。

  • vim/nano:文本编辑器。vim更高效,但nano对新手更友好。

  • adb:Android Debug Bridge,用于推送新 boot.img 并验证。确保adb devices能识别设备。

  • fastboot:用于烧写 boot 分区。确保fastboot devices能识别设备(需进入 fastboot 模式)。

提示:不要使用网上流传的“bootimg editor”图形化工具。它们封装了底层细节,一旦出错(如 header 校验失败),你完全无法定位原因。abootimg的命令行输出清晰显示每个步骤的 offset 和 size,是调试的黄金标准。

环境验证:

# 检查工具版本 abootimg --version # 应输出 1.2 或更高 cpio --version # 应输出 2.13 或更高 gzip --version # 应输出 1.12 或更高 adb version # 应输出 1.0.41 或更高 fastboot --version # 应输出 30.0.3 或更高

3.2 第一步:提取原始 boot.img 并解析其结构

绝对不要直接修改你手机上的 boot 分区!先从官方固件或自己编译的 AOSP 中获取原始boot.img。假设你已将其放在当前目录,名为boot-original.img。

执行解析命令:

abootimg -i boot-original.img

典型输出:

Android Boot Image Info: * file name = boot-original.img * image size = 20971520 bytes (20.0 MB) * page size = 4096 bytes * kernel size = 12345678 bytes * ramdisk size = 8765432 bytes * second size = 0 bytes * recovery dtbo size = 0 bytes * dtb size = 0 bytes * cmdline = androidboot.hardware=qcom androidboot.console=ttyMSM0 ... * board = * base = 0x80000000 * kernel_addr = 0x80008000 * ramdisk_addr = 0x81000000 * second_addr = 0x8f000000 * tags_addr = 0x80000100 * os_version = 13.0.0 * os_patch_level = 2023-04

关键信息解读:

  • page_size = 4096:所有后续操作必须按 4096 字节对齐。
  • kernel_size = 12345678:内核二进制长度,用于校验。
  • ramdisk_size = 8765432:ramdisk 压缩后大小,这是你接下来要解压的对象。
  • cmdline:内核命令行,可能包含重要参数(如androidboot.selinux=permissive),修改后需保持一致。

注意:abootimg -i输出的image size是整个 boot.img 的大小。kernel_size + ramdisk_size + 2048(header)应该小于等于image size,差值是 padding(填充字节),用于对齐。如果abootimg报错Invalid boot image,首要检查page_size和 padding 是否匹配。

3.3 第二步:解包 boot.img,分离 kernel 和 ramdisk

使用abootimg解包,生成三个文件:

  • zImage:内核二进制(不要修改它,除非你有内核源码)
  • initrd.img:压缩的 ramdisk(这就是我们要操作的核心)
  • bootimg.cfg:一个文本配置文件,记录了 header 中的所有参数。

执行命令:

abootimg -x boot-original.img

输出:

Unpacking boot image 'boot-original.img' Extracting kernel to file 'zImage' Extracting ramdisk to file 'initrd.img' Extracting second stage bootloader to file 'second' Extracting recovery dtbo to file 'recovery_dtbo' Extracting dtb to file 'dtb' Creating bootimg.cfg

此时目录下会出现zImage、initrd.img和bootimg.cfg。bootimg.cfg是你的“备份指南”,里面详细记录了page_size、base、kernel_addr等所有参数,重新打包时必须严格遵循它。

3.4 第三步:解压 ramdisk(initrd.img),获得可编辑的 init.rc

initrd.img是一个gzip压缩的cpio归档。解压分两步:

Step 1: 解压 gzip

gunzip -c initrd.img > initrd.cpio

-c参数表示将解压结果输出到 stdout,重定向到initrd.cpio。这一步后,initrd.cpio是一个未压缩的 cpio 归档。

Step 2: 解包 cpio

mkdir ramdisk-root cd ramdisk-root cpio -i -F ../initrd.cpio

cpio -i表示“extract”,-F指定输入文件。执行后,ramdisk-root目录下将出现完整的 ramdisk 文件树,包括init.rc、default.prop等所有文件。

验证:

ls -l init.rc # 应输出类似:-rw-r--r-- 1 user user 123456 Jan 1 12:00 init.rc

提示:cpio解包时,如果遇到权限错误(如Cannot create symlink),请确保你在 Linux/macOS 上运行,并且当前用户有写权限。cpio会忠实还原原始文件的权限和符号链接,这是它比tar更适合 ramdisk 的原因之一。

3.5 第四步:编辑 init.rc —— 三种最常用、最安全的修改场景

原则:永远先备份!cp init.rc init.rc.backup。

场景一:添加一个自定义服务(例如,开机自动启动一个日志收集脚本)

假设你想在系统启动后,自动运行/system/bin/log-collector.sh来抓取内核日志。

步骤:

  1. 创建脚本文件(在ramdisk-root目录外):
    echo '#!/system/bin/sh' > log-collector.sh echo 'dmesg > /data/local/tmp/kernel.log' >> log-collector.sh chmod 755 log-collector.sh
  2. 将脚本复制到 ramdisk 的sbin目录:
    cp log-collector.sh ramdisk-root/sbin/
  3. 编辑ramdisk-root/init.rc,在文件末尾(on late-init之后)添加:
    # Custom log collector service service log-collector /sbin/log-collector.sh class main user root group root oneshot
    关键参数解释:
    • class main:将其归类到main服务组,start main命令会启动它。
    • user root:以 root 用户运行(必要时)。
    • oneshot:执行完即退出,不常驻。如果需要常驻,请删除此行,并添加restart。
场景二:修改现有服务的启动条件(例如,禁用 adb root 权限)

在eng版本中,adbd默认以 root 运行,存在安全风险。你想让它只在userdebug版本中启用 root。

步骤:

  1. 在ramdisk-root/init.rc中找到service adbd块(通常在on init或on property:块中)。
  2. 修改其disabled属性,并添加条件触发器:
    service adbd /sbin/adbd class core user shell group shell log system wakelock input camera disabled on property:ro.debuggable=1 write /sys/class/android_usb/android0/enable 0 start adbd
    关键点:disabled让服务默认不启动;on property:ro.debuggable=1是一个触发器,当系统属性ro.debuggable为1(即userdebug版本)时,才执行start adbd。这样在user版本中,adbd将永远不会启动。
场景三:导入一个新的 .rc 文件(例如,为特定硬件添加初始化)

你想为一块新传感器添加初始化脚本,避免污染主init.rc。

步骤:

  1. 创建新文件ramdisk-root/init.sensor.rc:
    # init.sensor.rc on early-init write /sys/class/sensor/gyro/enabled 1 write /sys/class/sensor/accel/enabled 1 on property:sys.boot_completed=1 exec_start sensor_init
  2. 在ramdisk-root/init.rc的on init块末尾,添加导入语句:
    on init ... import /init.sensor.rc

注意:import语句必须在on init块内,且路径必须是绝对路径(以/开头)。init进程会按顺序解析所有import的文件,因此init.sensor.rc中定义的on动作会与主init.rc中的同名动作合并。

3.6 第五步:重新打包 ramdisk 和 boot.img

Step 1: 重新打包 ramdisk

cd ramdisk-root find . | cpio -o -H newc | gzip > ../new-ramdisk.cpio.gz cd ..

find .列出所有文件;cpio -o -H newc以newc格式(支持长文件名和大文件)创建 cpio 归档;gzip压缩。输出为new-ramdisk.cpio.gz。

Step 2: 用 abootimg 重新打包 boot.img

abootimg --create boot-new.img \ -f bootimg.cfg \ -k zImage \ -r new-ramdisk.cpio.gz

--create指定新镜像名;-f指定配置文件(必须是之前abootimg -x生成的bootimg.cfg);-k指定内核;-r指定新 ramdisk。abootimg会自动读取bootimg.cfg中的page_size等参数,并进行正确对齐。

验证新镜像:

abootimg -i boot-new.img # 比较输出,确认 kernel_size, ramdisk_size, page_size 与原始一致

3.7 第六步:烧写与验证 —— 从 fastboot 到 adb 的完整闭环

Step 1: 进入 fastboot 模式

  • 方法一:adb reboot bootloader
  • 方法二:关机后按住音量减 + 电源键(各厂商组合键不同)

Step 2: 烧写新 boot.img

fastboot flash boot boot-new.img # 如果设备有单独的 boot_a/boot_b 分区(A/B 分区),请确认当前活动分区 fastboot getvar current-slot # 查看当前 slot (a or b) fastboot flash boot_a boot-new.img # 烧写到 slot a

Step 3: 启动并验证

fastboot reboot # 等待设备启动完成 adb wait-for-device adb shell getprop ro.build.type # 确认是 userdebug/eng

关键验证点:

  • 检查 init.rc 是否生效:adb shell cat /proc/1/cmdline应输出init,证明 init 进程已启动。
  • 检查自定义服务:adb shell ps -A | grep log-collector,应看到进程。
  • 检查属性修改:adb shell getprop ro.debuggable,应为1。
  • 检查日志:adb logcat -b events | grep init,查看 init 的启动日志,确认log-collector服务已启动。

提示:如果设备无法启动(黑屏/循环重启),第一时间用fastboot boot boot-original.img临时启动原始镜像,然后检查adb logcat -b all或dmesg日志。最常见的错误是init.rc语法错误(如少了一个换行)或ramdisk未按page_size对齐。abootimg -i是你的第一道防线。

4. 常见问题与独家避坑指南:那些文档里不会写的实战经验

4.1 问题速查表:高频报错与精准解决方案

错误现象根本原因解决方案我的实测心得
fastboot flash boot xxx.img后设备无法启动,停留在 logoramdisk 大小变化导致整体 boot.img 未按page_size对齐用abootimg -i boot-new.img检查image size。如果新镜像比原镜像小,abootimg会自动填充;但如果更大,需确保kernel_size + ramdisk_size + 2048是page_size的整数倍。手动计算:padding = (page_size - ((kernel_size + ramdisk_size + 2048) % page_size)) % page_size,然后用dd if=/dev/zero bs=1 count=$padding >> boot-new.img补齐。我在高通平台一次因ramdisk增大了 123 字节,导致page_size=4096下未对齐,fastboot显示成功,但设备死机。用 `hexdump -C boot-new.img
adb shell进入后,/init.rc文件内容未更新adb shell cat /init.rc读取的是内存中已加载的 ramdisk,而非 flash 中的原始文件。修改后必须fastboot flash boot并reboot才能生效。adb shell无法直接验证init.rc修改,必须通过adb shell ps或adb logcat观察服务行为。新手常犯的错误。有一次我反复adb push到/init.rc,以为覆盖了,结果当然无效。记住:init.rc是只读的内存映射,push操作会被拒绝或写入到其他位置。
cpio -i解包时报错Cannot create symlink或Permission denied当前用户无权创建符号链接或修改文件权限,常见于 macOS 或 WSL2在 Linux 上,确保用sudo运行cpio?不!cpio不需要sudo。正确做法是:mkdir ramdisk-root && cd ramdisk-root && sudo cpio -i -F ../initrd.cpio。sudo只用于解包时创建特殊文件(如/dev/console),不影响后续编辑。macOS 的cpio对符号链接支持不佳,强烈建议在真 Linux 环境操作。我在 macOS 上试过 3 次都失败,切到 Ubuntu 后 1 分钟搞定。
abootimg --create报错Invalid boot image: bad magicbootimg.cfg文件被手动编辑过,破坏了格式(如多了一个空格、少了换行)绝对不要手动编辑bootimg.cfg!它是由abootimg -x自动生成的,格式极其严格。如果必须修改(如更改cmdline),用abootimg --set-config bootimg.cfg命令,它会校验格式。我曾为添加androidboot.selinux=permissive,直接vim bootimg.cfg,结果少了一个换行,abootimg报错。后来发现abootimg --set-config会自动修复格式,省时省力。

4.2 独家避坑技巧:十年踩坑总结的 5 条铁律

铁律一:永远用abootimg -x解包,永远用abootimg --create打包。
不要混用unpackbootimg和mkbootimg。前者是 Google 官方工具,后者是 AOSP 编译脚本,两者对 vendor header 的处理逻辑不同。我见过太多人用unpackbootimg解包高通镜像,再用mkbootimg打包,结果烧写后 kernel panic。abootimg是经过各大芯片厂商认证的通用工具,兼容性最好。

铁律二:修改init.rc前,先adb shell getprop查清所有相关属性。
init.rc的on property:触发器依赖于系统属性。例如,on property:sys.usb.config=adb依赖sys.usb.config的值。但这个值可能被init.usb.rc或init.qcom.rc修改。用adb shell getprop | grep usb全局搜索,确认你修改的触发器在哪个阶段生效。否则,你的start myservice可能永远等不到那个property。

铁律三:service的class属性决定启动时机,start <class>是调试利器。
init将服务分为core、main、late_start等 class。start main命令会启动所有class main的服务。在调试时,不要重启整个设备,直接adb shell start myservice或adb shell start main,可以快速验证服务定义是否正确。这是我每天必用的技巧,比reboot快 10 倍。

铁律四:init.rc中的exec和exec_start有本质区别,选错会导致服务无法重启。
exec /path/to/script是同步执行,init会等待脚本结束才继续解析下一行。exec_start script_name是异步启动一个服务,init会监控它。如果你想让一个脚本在开机时运行一次,用exec;如果你想让它常驻并能被init重启,必须定义为service并用start。我曾用exec启动一个网络配置脚本,结果脚本卡住,init永远停在那里,zygote无法启动。

铁律五:default.prop是init.rc的上游,修改它会影响整个启动链。
default.prop定义了ro.*和persist.*属性,如ro.build.type=eng。init.rc中的on property:ro.build.type=eng就依赖于此。如果你修改了default.prop,必须确保init.rc中的逻辑与之匹配。例如,default.prop设为user,但init.rc里还有on property:ro.build.type=eng的块,那块永远不会执行。我习惯在修改init.rc前,先adb shell getprop > props.txt,把当前所有属性导出,作为参考。

4.3 进阶调试:当init.rc修改后行为异常,如何快速定位?

方法一:利用init的内置日志。
init进程会将关键事件写入logcat -b events。执行:

adb logcat -b events | grep -E "(init|property)"

你会看到类似init: starting service 'log-collector'...和init: processing action (on property:sys.boot_completed=1)...的日志。这能清晰告诉你,你的on触发器是否被触发,服务是否被start。

方法二:在init.rc中插入write命令打点。
在关键位置插入:

on init write /dev/kmsg "DEBUG: init.rc parsing started" ... on property:sys.boot_completed=1 write /dev/kmsg "DEBUG: boot completed, starting my service" start myservice

然后adb shell dmesg | grep DEBUG,就能看到精确的执行流。/dev/kmsg是内核日志缓冲区,init有权限写入,且日志不会被logcat清除。

方法三:用strace追踪init进程(需 root)。

adb shell su -c "strace -p 1 -e trace=openat,read,write -s 256"

这会实时显示init(PID 1)打开了哪些文件、读取了哪些内容。当你怀疑init.rc没被正确加载时,这个命令能直接告诉你init是否读取了你修改后的文件。

最后分享一个小技巧:我有一个init-debug.rc文件,里面只有一行on init write /dev/kmsg "INIT_DEBUG: loaded"。每次修改完init.rc,我都把它import进去。只要看到dmesg里有这行输出,就证明我的修改已生效。简单粗暴,但百试不爽。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询