Android启动初始化init.rc详解
1. Android启动与init.rc简介
1.1 Android启动过程
一张图简单阐述一下
(网络图片,侵删)
1.2 init.rc 简介
Linux的重要特征之一就是一切都是以文件的形式存在的,例如,一个设备通常与一个或多个设备文件对应。这些与内核空间交互的文件都在用户空间,所以在Linux内核装载完,需要首先建立这些文件所在的目录。而完成这些工作的程序就是本文要介绍的init。Init是一个命令行程序。其主要工作之一就是建立这些与内核空间交互的文件所在的目录。当Linux内核加载完后,要做的第一件事就是调用init程序,也就是说,init是用户空间执行的第一个程序。 当然init程序的功能不仅仅是加载上面设备文件。
init.rc有两个,确切的说是两套,分别位于:
- ./system/core/rootdir/init.rc
- ./bootable/recovery/etc/init.rc
从目录上大致可以猜测,这两个init.rc使用场景不一样,一个是刷机用到的,也就是进入recorvery模式,一个是正常启动用到的;我们这里重点分析的是上面那个,也是init.c关联的那个。
1.3 .rc文件的存放目录以及目的
/init.rc是主要的.rc文件,由init可执行文件在开始执行时加载。它负责系统的初始设置。
在加载主目录/init.rc后,init立即加载包含在/{system,vendor,odm}/etc/init/目录中的所有文件。(这一点从android 7开始,分成多个不同的rc文件)
- /system/etc/init/ 用于核心系统项,例如 SurfaceFlinger, MediaService和logd。
- /vendor/etc/init/ 是针对SoC供应商的项目,如SoC核心功能所需的actions或守护进程。
- /odm/etc/init/ 用于设备制造商的项目,如actions或运动传感器或其他外围功能所需的守护进程。
每个系统如何加载这些不同目录的.rc文件,需要具体看从init.rc开始的import语句。 其中,import /init.${ro.hardware}.rc 较为常见。通过cat proc/cpuinfo可以查看ro.hardware的值=Hardware的值;一般在cpuinfo文件的末尾。
2. init.rc基本语法
下面的语法说明均以init.rc为例。
2.1. init.rc中三大模块:
- 1)import 导入其他的init.xx.rc文件。
- 2)action: 以trigger动作为触发点的一系列命令command ,包括service
- 3)service: 带有各种Options的一系列services的定义
service 和action的基本模型如下:
#service
service <name> <pathname> [ <argument> ]* (services可以带有多个参数和选项) <option> <option>#action 模型
on <trigger> <command> <command> <command>
2.2. service
Services(服务)是一个程序,以 service开头,由init进程启动,一般运行于另外一个init的子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service,在启动时会通过fork方式生成子进程。Services(服务)的形式如下:
服务(services)是指那些须要在系统初始化时就启动或退出时自己主动重新启动的程序.
它的语法结构例如以下:
1. service <name> <pathname> [ <argument> ]*
2. <option>
3. <option>
4. ...
pathname: 必须要有可执行的权限 service的name: 在所有rc文件中不能重复
2.3.option 选项:(用于services下面)
- class <class_name>
说明服务属于class_name这个类。缺省值service属于 “default” 类。同一个class下面的服务可以一起启动或停止。 - disabled
表示当这个服务所在的class启动的时候,服务不会自动启动,
要用start server_name 或 property_set("ctl.start", server_name);才能启动。 - oneshot
当服务退出后,不会再重新启动,如果没有加这个option,则服务默认退出后又会重新重启 - user <username>
执行服务之前,先声明服务的用户名,缺省值应该为root用户. - group <groupname> [ <groupname> ]*
执行服务之前,先声明服务所属组名,可以一次声明属于多个组。
声明多个组时,除第一个组名外,其他的为服务的补充组名(调用接口 setgroups()). - onrestart + command
服务重启的时,会执行onrestart后面的command.
eg:onrestart restart media 重启名为media的服务 - setenv <name> <value>
在当前服务进程中设置环境变量name的值为value。
注意:setenv定义的环境变量仅在本进程内生效,退出该进程,或者关闭相应的程序运行窗口,该环境变量即无效)
程序中可通过getenv("name")接口获取这个环境变量的值
setenv和export 的区别:
setenv csh ,本进程生效,退出后,变量无效
export bash ,全局生效,一直存在
格式:
export key=value
setenv key value
- critical
声明为关键服务。如果服务在四分钟内退出了四次,则设备会进入recovery模式 - socket <name> <type> <perm> [ <user> [ <group> ] ]
创建名为/dev/socket/<name>的unix domain socket ,并把它的句柄fd传给本服务进程
<type> 必须为 "dgram", "stream" or "seqpacket".User and group default to 0 ,也就是root. - seclablel 执行服务之前改变安全上下文
2.4. Action:定义了被触发执行的一系列命令
Action代表一组命令(Commands),Actions都有一个trigger(触发器),该触发器决定了何时执行这个Action,即在什么情况下才能执行该Action中的定义命令。当一些条件满足触发器的条件时,该Action中定义的命令会被添加到要执行命令队列的尾部(如果这组命令已经在队列中,则不会再次添加)。
队列中的每一个action都被依次提取出,而这个action中的每个command(命令)在一个Action从队列移除时,该Action定义的命令会依次被执行。
Action的格式如下:
on <trgger> [&& <trigger>]*<command1><command2><command3>
on后面跟着一个触发器,当trigger被触发时,command1,command2,command3,会依次执行,直到下一个Action或下一个Service。
简单来说,Actions就是Android在启动时定义的一个启动脚本,当条件满足时,会执行该脚本,脚本里都是一些命令commands,不同的脚本用on来区分。
2.5.Triggers: 用来触发action下面command的执行
trigger是Action的一部分,跟在on后面的动作名,用来触发action下面command的执行。 Triggers(触发器),本质上是一个字符串,能够匹配某种包含该字符串的事件,用于使Actions发生。 trigger又被细分为事件触发器(event trigger)和属性触发器(property trigger). - 事件触发器可由"trigger"命令或初始化过程中通过QueueEventTrigger()触发,通常是一些事先定义的简单字符串,例如:boot,late-init - 属性触发器是当指定属性的变量值变成指定值时触发,其格式为property:=*
一个Action可以有多个属性触发器,但是最多有一个事件触发器。
android常用的triggers的:
- early-init 在初始化早期阶段触发
- late-init 在初始化晚期阶段触发
- init 在初始化阶段触发
- late-init 在初始化晚期阶段触发
- boot/charger 当系统启动/充电时触发
- property:<key>=<value> 当属性值满足条件时触发 如:on property:ro.debuggable=1
- fs 挂载mtd分区时触发
- boot 基本网络的初始化,内存管理等时触发
- post-fs 改变系统目录的访问权限时触发
- device-added-<path> 设备节点添加时触发
- device-removed-<path> 设备节点删除时触发
- service-exited-<name> 在特定服务(service)退出时触发
可以自定义一些triggers,并选择合适的触发方式 (例如:关机充电功能,可以只启动charger服务进程)
- <name>=<value> 形式,如:
on property:ro.debuggable=1 或 on property:sys.boot_completed=1
- device-added-<path> 或 device-removed-<path>
一个设备节点/dev/XXX添加或者删除时可以触发一个action,这个可以很好的去利用 - service-exited-<name>
当某个服务退出时,可以触发一个action
2.6.command:(action下面的一系列命令)
常用命令:
1).import <filename> 导入init.XX.rc、xxx.conf等文件 Parse an init config file, extending the current configuration.2).chmod <octal-mode> <path> Change file access permissions.3).chown <owner> <group> <path> Change file owner and group. 4).chdir <directory> Change working directory. 5).chroot <directory> 改变进程根目录 6).insmod <path> 加载XX.ko驱动模块7).start <service> Start a service running if it is not already running.8).stop <service> Stop a service from running if it is currently running.9).class_start <serviceclass> Start all services of the specified class if they are not already running.10).class_stop <serviceclass> Stop all services of the specified class if they are currently running. class_reset <serviceclass> //重启class下面所有的服务 11).setprop <name> <value> Set system property <name> to <value>. 通过getprop命令可以查看当前系统的属性值 12).export <name> <value> 设置全局环境变量,这个变量值可以被所有进程访问(全局的,一直存在) 在代码中通过value = getenv("name")接口可以获取这个环境变量的值 13).mkdir <path> [mode] [owner] [group] 创建目录,后面项缺省值为 mode,owner,group: 0755 root root14).trigger <event> Trigger an action. Used to queue an action from another action. 例:trigger post-fs-data15).exec <path> [ <argument> ]* 执行<path>指定的Program,并可以带有执行参数。 exec在调用进程内部执行一个可执行文件,并会阻塞当前进程,直到运行完成。 最好避免和那些builtin commands一样使用exec命令,否则容易造成阻塞 or stuck ( maybe there should be a timeout?)16).ifup <interface> 启动某个网络接口,使其为up状态,通过netcfg可以查看,ifup eth0 等价于 netcfg eth0 up 功能一样 17).hostname <name> 设置设备的主机名,一般默认设置为localhost,可以在终端通过hostname new_name进行修改18).domainname <name> 设置网络域名localdomain19).mount <type> <device> <dir> [ <mountoption> ]* 把device挂接到dir目录下面,文件系统类型为type。 <mountoption>s include "ro", "rw", "remount", "noatime", “nosuid”......,具体可查看[linux](http://lib.csdn.net/base/linux "Linux知识库")的mount命令说明 20).setkey TBD == to be determined 暂时没有使用21).setrlimit <resource> <cur> <max> 设置本服务进程的资源上限值。(使用例子??)22).symlink <target> <path> path 链接到 ---》target ;创建符号链接23).sysclktz <mins_west_of_gmt> 设置系统时区(0 if system clock ticks in GMT)24).wait <path> [ <timeout> ] 轮询查找给定的文件path是否存在,如果找到或者超时则返回默认超时为5秒。(使用实例???)25).write <path> <string> [ <string> ]* 打开一个文件,利用write命令写入一个或多个字符串
3. 日志与结果查看
- init.rc中的服务启动后,都会生成进程,可以通过ps命令查询到。
ps -ef|grep myservicename
如果没有查询到,则是启动过程中出现异常。 - 可以通过dmesg命令,查询*.rc文件启动过程中的错误信息。
dmesg|grep C5 -myservicename
根据查看到的错误信息,进行必要的修改。
- 通过 getprop可查看所有的service运行状态。状态总共分为:running, stopped, restarting
getprop | grep init.svc
4. 常见问题
- 执行权限 如果service中的程序文件没有执行权限,service启动不会成功。需要使用chmod命令修改其权限
chmod u+x filename
或
chmod 755 filename
- 文件的安全上下文(file_contexts)Security Context配置错误 错误信息类似
[ 13.399564] init: Command ‘exec - root system – /system/postboot.sh’ action=sys.boot_completed=1 (/init.rc:722) took 1ms and failed: Could not start exec service: File /system/postboot.sh(labeled “u:object_r:system_file:s0”) has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly?
这是非常非常容易出错的一个问题。SELinux的安全上下文有是一个复杂的设计,请参考相关资料,这里不做展开。 改变文件的安全上下文,使用chcon命令:
chcon u:object_r:init_exec:s0 /system/bin/myservicename
查看文件的安全上下文
ls -lZ /system/bin/myservicename
5. 参考资料
- android6.0 init进程main之klog https://blog.csdn.net/songlan0012/article/details/119767562 klog的输出就是在给文件(/dev/kmsg)写入内容而已
- linux 内核日志级别与查看方式(https://blog.csdn.net/samallhorse/article/details/105542670)
- Android系统启动流程(一)解析init进程启动过程 https://blog.csdn.net/itachi85/article/details/54783506 总结起来init进程主要做了三件事:
1.创建一些文件夹并挂载设备
2.初始化和启动属性服务
3.解析init.rc配置文件并启动zygote进程 - Android系统学习(四)------关于init进程及开机自启动 https://blog.csdn.net/Guet_Kite/article/details/87655437 这里试着尝试通过配置init.rc文件实现开机自启动脚本:
脚本实现每次开机后台抓取dmesg写入文本- init.c 、init.rc init.xx.rc 等最终会编译到ramdisk.img(根文件系统)中,和kernel一起打包成boot.img。android启动后每次都会从boot.img中解压出init.c等文件到内存,所以要修改必须修改替换boot.img
- 描述了配置编译打包boot.img的过程
- Android系统init进程启动及init.rc全解析(https://blog.csdn.net/zhonglunshun/article/details/78615980)
- 其中对init.rc文件咋启动过程中的说明非常清晰
- 对概念的说明非常清晰
- android系统启动流程之init.rc详细分析笔记(https://blog.csdn.net/andrewblog/article/details/17122303)
- 对command的说明很详细