当前位置: 首页 > news >正文

Android init启动过程

Android 开机第一道门槛:从内核到 init 进程,你的系统服务是怎么拉起来的?(附实战配置)

目录

  • 一、init 是什么
  • 二、init 的启动流程
  • 三、init.rc 语法详解
  • 四、init 的启动阶段
  • 五、实战:添加自定义 Native 服务
  • 六、实战:开机自启脚本
  • 七、ueventd 与设备节点
  • 八、init 与 SELinux
  • 九、常见踩坑记录
  • 十、调试技巧与总结

一、init 是什么

Android 开机:BootLoader → Linux Kernel →init→ 其他所有进程。

init 是内核启动后运行的第一个用户态进程,PID 永远是1。它是 Android 系统的大管家——负责解析 init.rc 文件,按配置把系统的各种服务和守护进程拉起来。

# 在手机上确认 init 是 PID 1adb shellps-A|grep" init$"# 输出: root 1 0 0 0 init

init 干了哪些事:

init 启动

挂载文件系统
tmpfs / devpts / proc / sysfs

解析所有 .rc 文件

按阶段触发
early-init → init → late-init

启动属性服务
property_service

拉起 zygote / servicemanager
surfaceflinger 等核心服务

进入主循环
监控子进程 / 处理属性变更

init 的源码在 AOSP 的system/core/init/目录下,入口是init.cppmain()函数。


二、init 的启动流程

init 分两个阶段启动——第一阶段挂载基础文件系统,第二阶段加载 rc 并进入主循环。

整个流程:

Kernel 启动
执行 /system/bin/init

FirstStageMain()
挂载 /dev /proc /sys

execv 进入
SecondStageMain()

property_init()
初始化属性系统

SelinuxInitialize()
加载 SELinux 策略

LoadBootScripts()
解析所有 .rc 文件

按阶段触发
early-init → init → late-init

while(true) 主循环
epoll 监听事件
执行 Action
重启崩溃的服务

主循环干了什么:

  • 处理子进程退出:服务挂了,按 rc 配置决定要不要自动重启
  • 执行到期的 Action:属性变化触发的命令
  • 处理控制消息ctl.start/ctl.stop来启停服务
  • 处理关机重启:收到sys.powerctl属性执行对应操作

三、init.rc 语法详解

init.rc 用 Android Init Language 写,三种核心元素:

元素关键字作用
Actionon条件触发时执行命令
Serviceservice定义守护进程
Importimport引入另一个 .rc 文件

Action 语法:

on<trigger># 当 trigger 触发时<command># 执行命令<command>

Service 语法:

service<name><path>[<argument>]*<option># 各类选项<option>

常用 Service 选项:

选项含义
class <name>分组,可批量启停(class_start core)
user / group以哪个用户/组运行
oneshot一次性执行,挂了不重启
disabled默认不启动,需手动 start
critical关键服务——挂了多次进 recovery
onrestart该服务重启时执行的命令
socket创建 Unix Domain Socket
seclabelSELinux 上下文

实战示例:

# Action 示例on early-initmounttmpfs tmpfs /mntmode=0755mkdir/mnt/vendor 0755 root root on late-init trigger zygote-start# 最后拉起 zygote# Service 示例——zygote 是最重要的服务servicezygote /system/bin/app_process64-Xzygote/system/bin--zygote--start-system-server class main user root group root readproc reserved_disk socket zygote stream660root system onrestart restart audioserver onrestart restart cameraserver onrestart restart media

四、init 的启动阶段

early-init

init
启动 core class 服务

early-fs → fs → post-fs

late-fs → post-fs-data
/data 分区就绪

zygote-start
★ 拉起 Zygote

early-boot → boot
启动 main class 服务

开机完成
sys.boot_completed=1

阶段主要做什么
early-init创建基础目录
init启动 ueventd、logd 等服务
fsmount_all挂载分区
post-fs-data/data 分区就绪,创建目录结构
zygote-start拉起 Zygote → SystemServer → 所有系统服务
boot启动 main class 的服务,发开机广播

你的 Service 如果不加disabled,class 对上了就会在对应阶段自动启动。


五、实战:添加自定义 Native 服务

写一个 C++ 程序,让它开机自启且挂了自动重启。

第 1 步:写程序

// my_daemon.cpp#include<unistd.h>#include<android/log.h>#defineLOG_TAG"MyDaemon"#defineLOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)intmain(){LOGI("MyDaemon started, PID=%d",getpid());while(true){LOGI("MyDaemon is running...");sleep(10);}return0;}

第 2 步:Android.bp 编译

cc_binary{name:"my_daemon",srcs:["my_daemon.cpp"],shared_libs:["liblog"],init_rc:["my_daemon.rc"],}

init_rc属性会自动把你的 .rc 安装到/system/etc/init/

第 3 步:写 my_daemon.rc

servicemy_daemon /system/bin/my_daemon class main# 随 main class 启动user system group system seclabel u:r:my_daemon:s0

不加oneshot不加disabled= 开机自启 + 挂了自动重启。

第 4 步:编译测试

sourcebuild/envsetup.sh lunch<你的产品>mmm path/to/modulemake-j16# 验证adb shellps-A|grepmy_daemon adb shell start my_daemon# 手动启动adb shell stop my_daemon# 手动停止adb logcat-ball|grepMyDaemon

六、实战:开机自启脚本

不需要 C++ 程序,只想开机执行几条命令。

脚本:

# /system/bin/init_custom.sh#!/system/bin/shsetprop vendor.my.feature.enabled1mkdir-p/data/my_app/configchmod755/data/my_app/config

rc 配置——开机完成后执行:

# my_custom.rcon property:sys.boot_completed=1exec- system system -- /system/bin/sh /system/bin/init_custom.sh

想更早执行就把sys.boot_completed=1换成post-fs-data

编译配置(device.mk):

PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/my_custom.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/my_custom.rc \ $(LOCAL_PATH)/init_custom.sh:$(TARGET_COPY_OUT_VENDOR)/bin/init_custom.sh

七、ueventd 与设备节点

ueventd 是 init 的子进程,接管内核 uevent(设备热插拔),创建设备节点并设权限。

内核 uevent
设备插入/拔出

ueventd

解析 ueventd.rc

创建设备节点并设权限

/dev/video0
owner=root group=camera
perm=0660

ueventd.rc 示例:

/dev/i2c-* 0660 root system /dev/tty* 0666 root root /dev/camera 0660 system camera /dev/video* 0660 system camera

新加的硬件驱动,对应的设备节点权限写在这里,不然上层 App 没权限访问。


八、init 与 SELinux

Android 强制开启 SELinux,init 启动的每个 Service 都要有对应策略,不然启动直接被 kill。

调试:

adb shell getenforce# 查看状态adb shell setenforce0# 临时关掉调试adb logcat-ball|grepavc# 看拒绝日志

策略文件示例(my_daemon.te):

typemy_daemon, domain;typemy_daemon_exec, exec_type, vendor_file_type, file_type;init_daemon_domain(my_daemon)allow my_daemon system_file:file{openread};allow my_daemon property_socket:sock_filewrite;

调试流程:setenforce 0 → 看程序能不能跑 → 能跑就是 SELinux 问题 → 抓 avc 日志 → 补权限 → setenforce 1 验证。


九、常见踩坑记录

坑 1:改了 rc 没重新编译刷机—— rc 文件打包在系统镜像里,光改不编不刷等于没改。

坑 2:路径写错—— init 找不到可执行文件,先adb shell ls -la /system/bin/xxx确认。

坑 3:SELinux 权限没给够—— 日志里搜avc: denied

坑 4:oneshot 和 disabled 搞混—— oneshot 只跑一次不重启;disabled 默认不启动;都不加才自启+自动重启。

坑 5:class 没写—— 没 class 就不会被class_start自动拉起。

坑 6:exec 脚本不指定 shell—— 明确用exec - system system -- /system/bin/sh /path/to/script.sh


十、调试技巧与总结

常用命令:

adb shell dumpsys|grep-A5"Service "# 查看所有 Service 状态adb shell start/stop servicename# 启停adb logcat-ball|grep"init: "# init 日志adb shelldmesg|tail-50# 内核日志adb shell getprop init.svc.servicename# 服务状态adb shell setprop ctl.start servicename# 等同于 start

rc 文件位置:

分区路径谁放的
system/system/etc/init/hw/init.rcAOSP 根 rc
system/system/etc/init/*.rcAOSP 模块
vendor/vendor/etc/init/*.rc芯片/硬件厂商
odm/odm/etc/init/*.rcODM 定制

小结:init 机制不复杂——开机读 rc → 触发 Action → 启动 Service → 主循环当管家。你加的任何系统服务,本质就是写个 rc 让 init 管起来。记住三句话:rc 定义 Service → class 决定何时启 → SELinux 给权限才能跑。

http://www.jsqmd.com/news/908182/

相关文章:

  • 不只是VMware:开启AMD-V后,你的Win10/Win11还能玩转这些虚拟化工具
  • GPT5.5对Gemini3.5对DeepSeekV4编程能力横评
  • 别再死记硬背build.gradle了!用Groovy闭包和DSL思维,5分钟看懂Gradle配置的本质
  • 帆软报表FineReport连接Elasticsearch实战:从插件安装到SQL查询的保姆级避坑指南
  • 推荐几个博客
  • 用STM32F103 DIY一个JTAG边界扫描测试仪(附源码和避坑指南)
  • 别再只用洞洞板了!用嘉立创EDA+370电机,低成本搞定POV旋转LED全套硬件
  • AI与机器学习驱动的智能运营:从数据到决策的自动化闭环
  • 别再只盯着5G了!聊聊IMS:这个藏在通话、视频背后的‘老’技术,为啥现在又火了?
  • LLM生成Verilog代码的常见错误与修正技术
  • 保姆级教空间转录组分析| 01. 绪论
  • 【NCCL】transport数据传输(二)
  • 从5篇高温合金文章到16层协议:一个工业AI知识萃取的方法论
  • 用N32G031的TIM1驱动无刷电机:从寄存器配置互补PWM到死区时间实战避坑
  • SaaS未来趋势:AI融合、垂直化与生态化演进
  • 枚举三大应用场景
  • 别再只用BERT做分类了!用SentenceTransformers的5个实战场景(含代码)
  • Elasticsearch聚合分析实战
  • 火箭着陆制导算法:从凸优化到6-DoF控制
  • FreeRTOS性能调优利器:用SystemView揪出任务阻塞和中断延迟的元凶
  • 学习导师:从工具模式到感知模式的整合
  • LogAnalyzer实战:除了看系统日志,我这样用它监控Nginx访问和MySQL慢查询
  • Haskell与TensorFlow:用函数式编程构建安全可靠的AI系统
  • 视频隐写术实战:位置与精度如何决定信息隐藏的成败
  • Java(分支结构)
  • 别再凭感觉选电阻了!手把手教你计算MOSFET驱动电阻的功耗与封装(附Excel计算模板)
  • 魔百盒M401A安装Home Assistant Supervisor实录:我踩过的那些坑与终极解决方案
  • 定点乘法避坑指南:DSP和嵌入式开发中精度丢失与溢出处理的实战经验
  • Web安全实战解析与核心技术落地指南
  • AI赋能客户体验:从智能客服到预测性服务的实战指南