Android .rc规则详解与init 启动

Android智能座舱系列文章

系列文章请扫关注公众号!

简介

Android的init进程是启动各种服务的核心进程,并处理属性设置等。怎么启动各个服务和监听属性的呢?启动过程中会解析rc文件,并存下来。当系统属性更改或启动某项服务时,init就会按照rc中的设置运行对应应用。

Init 语言:http://aospxref.com/android-10.0.0_r47/xref/system/core/init/README.md

Rc文件是按照一定规范组成的。分为5大类语句:Actions, Commands, Services, Options,Imports.

C++/java都是面向对象的语言,init语言是面向行的,就是一行就是一个语句。由空格分隔。也可使用\插入空格,双引号可防止空格拆分文本,即双引号下是一个整体。行最后使用\表示折行,即续接下行。

#开头的是注释

引用系统属性使用${property.name},还可以字符拼接;例如 `import /init.recovery.${ro.hardware}.rc`

Actions  Services 开头等于隐式声明了一个section,所有的CommandsOptions都属于这个section。

Services只有唯一的名字,如果名字重复,第二个将被忽略,并输出异常log。

Init rc文件

是以rc为后缀的文本文件

/init.rc是重要文件,在init进程启动时加载,并负责初始化系统设置。在加载完/init.rc后,执行first stage mount即加载/{system,vendor,odm}/etc/init/路径的rc文件,实现/system, /vendor

的挂载。

Mount_all命令可以指定fstab文件,挂载相应分区。没有指定就搜索默认路径/{system,vendor,odm}/etc/init/。这主要是为了支持工厂模式和其它非标准启动模式。正常启动应该使用如下3个路径的。

  1. /system/etc/init/ 用于系统核心项,例如SurfaceFlinger, MediaService,logcatd.

  2. /vendor/etc/init/ 用于SoC vendor 项,例如core SoC 需要的actions daemons.

  3. /odm/etc/init/ 给设备制造商使用, 例如外设、运动传感器等。

/{system,vendor,odm}目录下的bin文件都在其对应的 /etc/init/下有其对应项。系统中存在一个编译宏LOCAL_INIT_RC给开发者使用。每一个rc文件应该包含某个服务的全部关联操作。

例如:logcat

system/core/logcat/Android.bp

system/core/logcat/logcatd.rc

Init 加载logcatd.rc并将任务放入队列,合适时机运行。

根据init .rc文件的守护进程拆分init .rc文件比以前使用的整体init .rc文件更好。这样可以确保init读取的是唯一的服务entry和action,还有助于解决服务冲突。

mount_all命令可以有"early" "late"两个选项。当在可选路径后带 "early"init可执行程序将跳过挂载分区中带有"latemount" flag的挂载项,并触发fs 加密状态事件。设置"late" 选项后,不导入rc文件并只挂载带"latemount" flag的项。如果没有设置就会mount所有项。

Actions 理解为一系列命令操作,并有一个触发器 trigger。trigger决定Action何时执行。当有一个事件匹配了Action的trigger,就会将此Action放入to_be_excuted queue的尾部。如果已经在to_be_excuted queue中就跳过。

Action按顺序执行,Action中的command也按顺序执行。

Init在两个命令中间可以执行服务重启等任务:(device creation/destruction, property setting, process restarting)

Actions 格式:

 

     on <trigger> [&& <trigger>]*

        <command>

        <command>

        <command>

Action的执行顺序按照添加队列的顺序执行,也就是import导入的顺序放入队列,然后按照单个文件顺序执行。

例如一个文件包含

 on boot

        setprop a 1

        setprop b 2

 

     on boot && property:prop_true=true

        setprop c 1

        setprop d 2

 

     on boot

        setprop e 1

        setprop f 2

当trigger boot时并且 属性prop_true的值是true,顺序执行的结果是:

     setprop a 1

     setprop b 2

     setprop c 1

     setprop d 2

     setprop e 1

     setprop f 2

Services

是init加载起来的应用程序,并且程序退出时会重启。服务的格式如下

    service <name> <pathname> [ <argument> ]*

        <option>

        <option>

        ...

Options

Services的修饰符,决定了init什么时候,怎么运行这个service。

capabilities

参考https://man7.org/linux/man-pages/man7/capabilities.7.html

`class <name> [ <name>\* ]`

指定服务的类别,同一类别的class会一起启动和停止,例如class core;

如果没有指定就是default。

class `animation` 应该包含所有boot animation和shutdown animation的服务。所有这些服务可以在bootup阶段启动并且运行到shutdown的最后阶段。animation阶段不能保证/data分区没无法保证的。应用可以access文件,但不应该open /data下的文件,并且应该保证/data 分区不可用时可正常工作。

Class `console [<console>]` 表明服务需要一个console。第二个可选参数指定一个console代替默认的。默认的console "/dev/console" 可以被内核参数"androidboot.console"设置修改。/dev前缀被省略,所以想指定/dev/tty0,只需设置console tty0

`critical` 关键服务。如果这个服务在启动完成之前退出,或者4分钟内退出4次,将进入BootLoader。

`disabled` 表示这个服务不会随着 此类服务一起启动。需要通过接口或名字启动。

`enter_namespace <type> <path>`

进入path下的type namespace,目前type只支持net。

`file <path> <type>`

打开一个文件,并将fd给到进程,type必须是"r", "w" or "rw".对于native层应用,可以参考libcutils  android_get_control_file()函数。

`group <groupname> [ <groupname>\* ]`

服务运行前更改组名为groupname第二哥groupname表示附加组,通过setgroups()默认是root

`interface <interface name> <instance name>`

将此服务与提供的aidl hidl服务进行关联。例如用于servicemanager hwservicemanager启动lazily services   

例如:interface vendor.foo.bar@1.0::IBaz default

`ioprio <class> <priority>`

IO优先级,可以通过SYS_ioprio_set syscall设置,class必须是 "rt", "be", or "idle"_priority_  0 - 7

`keycodes <keycode> [ <keycode>\* ]`

设置按键触发,通常用于bugreporter。

`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`

系统启动了memcgrooup时,设置子进程的内存限制。

`memcg.limit_property <value>`

`memcg.soft_limit_in_bytes <value>`

`memcg.swappiness <value>`

`namespace <pid|mnt>`

Fork这个服务时指定一个新的pid,或者mount一个namespace。

`oneshot`

这个程序退出时不重启它。

`onrestart`

当服务重启时,执行一个命令

`oom_score_adjust <value>`

设置value值到 /proc/self/oom\_score\_adj范围是 -1000 - 1000.

`override`

用于覆盖之前定义的同名服务,一般用于/odm 覆盖 /vendor的,init使用最后一个override定义的服务。

`priority <priority>`

进程调度优先级,-20 to 19默认是0,-20优先级更高。setpriority()

`restart_period <seconds>`

重启期间段;一个non-oneshot服务,从运行时开始计时,seconds秒后restart一次。默认是5s,防止服务频繁crash。可以用于周期性服务,例如3600 ,表示每1小时重启一次。

`rlimit <resource> <cur> <max>`

用于resource limit,这个限制会继承给子进程,所以rlimit是应用于一个进程树。命令解析方式可以参考setrlimit

`seclabel <seclabel>`

更改服务的 'seclabel'主要用于rootfs运行的服务使用,例如uevent、adbd.

`setenv <name> <value>`

服务运行时,设置环境变量到系统中。

`shutdown <shutdown_behavior>`

设置服务在关机时的行为。如果未指定,进程被SIGTERM and SIGKILL

如果设置了shutdown_behavior"critical"shutdown critical

进程在shutdown执行超时之前不会被kill掉,当shutdown timeout时,标记shutdown critical的服务也会被kill掉。设置shutdown critical时,如果关机进程启动时此服务不存在,就会启动此服务。

`sigstop`

在执行此服务之前先发送SIGSTOP 用于debug调试。

`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`

创建一个unix domain socket 名字为/dev/socket/_name_,并把fd传给服务。_type_ 必须是 "dgram", "stream" or "seqpacket"User  group 默认 0.

'seclabel' 是这个socket的 SELinux security context

参考libcutils android_get_control_socket()

`timeout_period <seconds>`

在服务被kill掉时,延时多长时间。设置timeout_periodoneshot 设置还是有效的,也就是说不会restart。但没设置oneshot的服务会再seconds秒后restart。这个和restart_period 可以混合使用。

`updatable`

用于apex更新,一个服务带着`updatable`启动,在APEXes 完全激活之前,进程是delayed的。如果一个服务没有updatable就不能被APEXes 更新。

`user <username>`

程序执行前修改 'username' 默认是root。

`writepid <file> [ <file>\* ]`

写子进程的pid到文件,用于cgroup/cpuset如果writepid时, /dev/cpuset/下的文件不存在,但系统属性ro.cpuset.default是非空cpuset name例如'/foreground'这个pid就会写到/dev/cpuset/_cpuset\_name_/tasks.

Triggers

--------

触发器,让一个Action运行。分为event triggers property triggers

'trigger'命令和init中的 QueueEventTrigger() 属于event trigger。比如'boot' or 'late-init

Property triggers是当属性设置或发生变化时触发;形式是'property:<name>=<value>' and 'property:<name>=\*'

Property triggers在init的boot阶段,会额外触发。

一个Action可以有多个属性trigger,但只能有一个event trigger。

`on boot && property:a=b` d

`on property:a=b && property:c=d` d

Commands

--------

执行的命令

`bootchart [start|stop]`

Start/stop bootcharting

出现在默认的init.rc中,只有当 /data/bootchart/enabled存在时才有意义。

`chmod <octal-mode> <path>`

修改文件访问权限,和手动执行chmod没区别。

`chown <owner> <group> <path>`

修改文件的ownergroup

`class_start <serviceclass>`

启动某一类服务,比如class core

`class_start_post_data <serviceclass>`

`class_start <serviceclass>`差不多,只考虑在/data/分区mount之后才运行的服务,并且要class_reset_post_data调用之后。一般用于FDE devices

`class_stop <serviceclass>`

如果某类服务运行着,就stop并disable掉。

`class_reset <serviceclass>`

停止某类服务,但并不disable掉,只有还可以用class_start <serviceclass>启动。

`copy <src> <dst>`

理解为cp命令,对于src文件, symbolic link file and world-writable  or group-writable files是不允许的。对于dst文件,如果不存在,默认 0600权限,如果已经存在就会截断覆盖。

`domainname <name>`

设置domain name.

`enable <servicename>`

当一个服务没被设置为disable时,将这个服务设置为enable。如果一个预期需要运行,那enable servicename的服务就立即运行了。例如BootLoader设置一个属性要运行这个服务。

on property:ro.boot.myfancyhardware=1

        enable my_fancy_service_for_my_fancy_hardware

`exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`

Fork并运行带参数的command,Init 暂停执行其它commands 直到command进程退出.

`exec_background [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`

exec 差不多,当并不暂停执行其它命令。

`exec_start <service>`

启动一个服务并暂停init的其它command执行,和exec类似,只是把命令改为了服务名。

`export <name> <value>`

相当于export命令,export name=value,设置一个全局环境变量,在这之后运行的程序都可以访问到。

`hostname <name>`

设置host name

`ifup <interface>`

使network interface _interface_ online

`insmod [-f] <path> [<options>]`

Insmod path路径下的ko。Option是其选项,-f标志强制insmod,不管是否和内核版本匹配。

`load_system_props`

这个强烈不建议使用。

`load_persist_props`

在/data/解密之后加载persistent properties应该包含在默认的init.rc

`loglevel <level>`

设置内核loglevel等级

`mark_post_data`

用于mark /data挂载的时刻。用于`class_reset_post_data` and `class_start_post_data`

`mkdir <path> [mode] [owner] [group]`

创建path路径的文件夹,如果默认不带其它参数,mode是755 owner、group是root。

`mount_all <fstab> [ <path> ]\* [--<option>]`

调用 fs_mgr_mount_all mount fstab文件中的分区,option有late和early,详见前面的init.rc说明。

`mount <type> <device> <dir> [ <flag>\* ] [<options>]`

尝试挂载 dir 路径下的 device设备, _flag_s 包含"ro", "rw", "remount", "noatime", ...

 _options_ 包含"barrier=1", "noauto_da_alloc", "discard", ... 并用逗号‘,’分开,eg: barrier=1,noauto_da_alloc

`parse_apex_configs`

用于解析apex的配置,一般用在 apexd.status 属性设置时。

`restart <service>`

停止并重启此服务,如果正在重启中,不做任何事情,

`restorecon <path> [ <path>\* ]`

将 _path_ 命名的文件恢复到 file_contexts 中指定的安全上下文

`restorecon_recursive <path> [ <path>\* ]`

上边命令的递归恢复。

`rm <path>`

对path路径调用unlink(2)也可以用"exec -- rm ..."代替。

`rmdir <path>`

对path路径调用 rmdir(2)

`readahead <file|dir> [--fully]`

对file|dir调用readahead(2)--fully表示完整的读取整个内容。

`setprop <name> <value>`

设置属性名name值为value

`setrlimit <resource> <cur> <max>`

对某项资源的限制,对全局的进程有效。应该在init之前设置,比如设置'cpu', 'rtio''RLIM_CPU', 'RLIM_RTIO',设置'unlimited' or '-1'表示不限制。

`start <service>`

如果一个服务不是running就启动它。这个操作是不保证状态同步的,也就是同一时刻可能运行两次这个服务,可能存在冲突。

`stop <service>`

停止一个服务

`swapon_all <fstab>`

对fstab文件调用fs_mgr_swapon_all

`symlink <target> <path>`

对path创建一个软链接。

`sysclktz <mins_west_of_gmt>`

设置系统时钟基准,如果已GMT标准为0.

`trigger <event>`

触发一个event

`umount <path>`

Unmount the filesystem mounted at that path.

`verity_load_state`

 用于加载 dm-verity 状态的内部实现细节。

`verity_update_state <mount-point>`

用于加载 dm-verity 状态的内部实现细节和adb remount 设置属性partition._mount-point_.verified fs_mgr不直接操作。

`wait <path> [ <timeout> ]`

轮询给定文件是否存在,找到或超时返回。如果指定超时,默认为五秒。

`wait_for_prop <name> <value>`

等待属性name值为value,如果相等,立即执行。

`write <path> <content>`

对path路径的文件写content内容,相当于调用 write(2)Path不存在就创建,存在会被截断重写,相当与覆盖而不是追加。

Imports

-------

`import <path>`

导入path路径的rc文件给init解析,如果是一个文件夹,只解析当前路径下的所有rc文件,不会解析包含的子文件夹。

import关键字不是命令,而是它自己的部分,这意味着它不是作为Action的一部分发生,而是作为正在解析的文件处理导入,并遵循以下逻辑。

只有在以下三种情况下才导入rc文件。

  1. 导入/init.rc 或boot initail 时设置`ro.boot.init_rc`属性时,

2、在导入/init.rc后,first mount stage阶段导入{system,vendor,odm}/etc/init/ 时。

3、当mount_all指定路径时,导入指定路径/{system,vendor,odm}/etc/init/或.rc文件

由于兼容性和历史原因,import文件是一个复杂的问题,没法保证顺序。

只有两种方式可以保证一个服务运行于另一个服务之前。1、放在一个可以更早trigger的Action中运行,2、放在同一个文件同一个Action的更早行中执行。

first stage mount devices的顺序是:

1、/init.rc 然后递归解析import的rc文件,Android12+是 /system/etc/init/hw/init.rc

2、/system/etc/init/是按字母顺序导入的解析的,解析后递归执行。

3、再按照步骤2处理 /vendor/etc/init 然后处理/odm/etc/init

伪代码如下:

     fn Import(file)

       Parse(file)

       for (import : file.imports)

         Import(import)

 

     Import(/init.rc)

     Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]

     for (directory : Directories)

       files = <Alphabetical order of directory's contents>

       for (file : files)

         Import(file)

Properties

----------

。。。。。。

Debugging init

--------------

Host Init Script Verification

-----------------------------

。。。。。。

Early Init Boot Sequence

------------------------

。。。。。。。

启动顺序:

。。。。。。

系列文章请扫关注公众号!

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值