Android init源代码分析(2)init.rc解析

本文描述init.rc脚本解析以及执行过程,读完本章后,读者应能
(1) 了解init.rc解析过程
(2) 定制init.rc

init.rc介绍
init.rc是一个文本文件,可认为它是Android系统启动脚本。init.rc文件中定义了环境变量配置、系统进程启动,分区挂载,属性配置等诸多内容。init.rc具有特殊的语法。init源码目录下的readme.txt中详细的描述了init启动脚本的语法规则,是试图定制init.rc的开发者的必读资料。
Android启动脚本包括一组文件,包括
init.rc
init.usb.rc
init.trace.rc
init.{hardware}.rc
init.environ.rc
init.zygote32.rc
这些文件可能分布于如下目录中
  1. system/core/rootdir
  2. device/{vendor}/{hardware}/
除init.rc外,其他文件都由init.rc中以import语句导入,一般来说init.rc文件存放通用配置,而其他特定模块以及特定硬件的配置则放置在独立的文件中,这样设计可以使init.rc脚本简洁,方便系统维护和升级。

一个简单的init.rc语法如下。(基于system/core/rootdir/init.rc裁剪)
# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc
on early-init
    start ueventd

on init
    sysclktz 0
    loglevel 3
    mkdir /system
    mkdir /data 0771 system system
    write /proc/sys/kernel/panic_on_oops 1
# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
    load_all_props
on post-fs
    # once everything is setup, no need to modify /
    mount rootfs rootfs / ro remount
on boot
    # basic network init
    ifup lo
    class_start core
    class_start main
on property:vold.decrypt=trigger_reset_main
    class_reset main
service ueventd /sbin/ueventd
    class core
    critical
on property:ro.debuggable=1
    start console
service debuggerd /system/bin/debuggerd
    class main
service bootanim /system/bin/bootanimation
    class main
    user graphics
    group graphics
    disabled
    oneshot

为了行文方便,下文提及init.rc,通常泛指Android启动脚本。

init.rc可以定义两类结构:Actions与Services。
Actions
Actions是一组命令的集合,定义一个Actions如下,每个Actions都可以定义一个触发器(trigger),Actions格式如下:
on <trigger>
   <command>
   <command>
   <command>
其中<command>类似于shell命令,command对应一个函数,通常执行一条动作,例如创建一个文件夹等。

触发器(Triggers)
触发器只是一段字符串罢了
PS:在android/system/core/rootdir/下执行
grep -h "^on" --include="*.rc" -r .
可以当前init启动脚本所含有的trigger,如下。
on early-init
on init
on property:sys.boot_from_charger_mode=1
on load_all_props_action
on firmware_mounts_complete
on late-init
on post-fs
on post-fs-data
on boot
on nonencrypted
on property:sys.init_log_level=*
on charger
on property:vold.decrypt=trigger_reset_main
on property:vold.decrypt=trigger_load_persist_props
on property:vold.decrypt=trigger_post_fs_data
on property:vold.decrypt=trigger_restart_min_framework
on property:vold.decrypt=trigger_restart_framework
on property:vold.decrypt=trigger_shutdown_framework
on property:sys.powerctl=*
on property:sys.sysctl.extra_free_kbytes=*
on property:sys.sysctl.tcp_def_init_rwnd=*
on property:ro.debuggable=1
on property:ro.kernel.qemu=1
on boot
on post-fs-data
on property:sys.usb.config=none
on property:sys.usb.config=adb
on property:sys.usb.config=accessory
on property:sys.usb.config=accessory,adb
on property:sys.usb.config=audio_source
on property:sys.usb.config=audio_source,adb
on property:sys.usb.config=accessory,audio_source
on property:sys.usb.config=accessory,audio_source,adb
on property:persist.sys.usb.config=*
根据trigger的不同,可以将Actions大致分为两类:
(1) 普通型
这类trigger的的作用仅仅是用于给一个Actions命名,方便查找和引用。如 early-init、init、late-init、early-fs、fs、post-fs、post-fs-data、early-boot、boot、charger等
一般来说,这类Actions将在Android启动时执行,其trigger暗示了执行对应Actions执行的时机。具体的执行流程将在本文最后介绍。

此外,根据readme.txt描述,还有其他几种trigger,但在init.rc以及init源代码中却没有找到相关代码,如下所示,
device-added-<path>
device-removed-<path>
    设备节点被添加或移除时调用,。
service-exited-<name>
   这类trigger将在某service退出时执行。关于什么是service稍后介绍。

(2) 属性型
其trigger为property:<name>=<value>
其trigger不仅唯一标识了这个Actions,同时也设定了这类Actons执行的条件,当property <name>的值为<value>时才会被执行。

Commands

command的格式如下
command-name <parament1> [parament2...]
<>表示必须存在的参数,[]表示可选参数

说明:readme.txt中虽然有大部分commands的介绍,但并不完整。init.rc中所有commands都在keywords.h中定义,可使用如下命令提取。
sed -n "s/KEYWORD(\([^,]\+\),[ \t]\+COMMAND.*/\1/p" keywords.h

目前Android4.4支持的Commands如下:
    chdir <direcotory> 改变工作目录
    chroot <directory> 改变当前进程的root目录
    class_start <serviceclass> 如果serviceclass内所有services尚未启动,则启动它
    class_stop  <serviceclass> 停止serviceclass内所有services
    class_reset <serviceclass> 重启serviceclass内所有services
    domainname <name>  设置domain名称
    enable  <servicename>
    exec <path> [ <argument> ]*  fork后执行path所执行的程序,该语句会阻塞直到path指定的程序执行完毕。
    export <name> <value> 设置全局环境变量,将会被该命令后所启动的进程继承。
    hostname <name> 设置主机名
    ifup  <interface> 启动interface所指定的网络接口
    insmod <path> 安装path所指定的内核模块
    mkdir <path> [mode] [owner] [group] 创建path制定的目录,并根据可选的mode、owner、group设定参数。如果未指定可选参数,则创建的文件夹权限将会设置为0755,而owner与group都为root
    mount_all
    mount  <type> <device> <dir> [ <mountoption] *
    powerctl
    restart <service>
    restorecon
    restorecon_recursive
    rm <path> 删除path指定的文件
    rmdir <path> 删除path指定的目录(目录为空才能删除)
    setcon
    setenforce
    setkey
    setprop
    setrlimit
    setsebool
    start <service>
    stop <service>
    swapon_all
    trigger
    symlink
    sysclktz
    wait
    write
    copy
    chown
    chmod
    loglevel
    load_persist_props
    load_all_props

Service
这里的service仅仅是init.rc中的概念,与通常意义上的“服务”概念无关。一个Service对应一个可执行程序,并且可以设定该程序的一些执行性质,如仅仅执行一次、或退出时自动重启。当service所代表的可执行程序在退出时自动重启时,该service通常意味着这是一个守护进程。

service字段由如下格式定义
service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...
<name>字段为service的名字,<pathname>为该service对应的二进制程序的路径,随后是该程序的参数列表。
<option>是该service的属性(笔者注:也许用attribute更合适),目前Android4.4中所支持的option如下所示(ps,由 sed -n "s/KEYWORD(\([^,]\+\),[ \t]\+OPTION.*/\1/p" keywords.h 命令生成)
    capability
    class <name> 设定service的class
    console
    critical
    disabled
    group <groupname> [<groupname>]* 设定进程
    keycodes
    oneshot  service只执行一次
    onrestart 当service终止时自动重启
    seclabeli
    setenv
    socket
    user <username>    
    ioprio

为了方便管理多个service,可为service设定class属性,具有同样class的多个service构成一个组,可以在Actions中通过class_start、class_stop、class_reset等命令启动、停止、重启动。

此外init.rc中还有其他规则
  • 以#号开头的行为注释行
  • import语句导入其他init脚本文件,
  • \可用于转义换行符
  • 空格与Tab字符都可用作空白字符
前面介绍了init.rc的语法,假如要自己实现一个程序读取init.rc并解析其内容,该如何实现呢?读者不妨思考下。
Android中的实现非常的简洁。解析设计思路如下:
  • 引入section的概念。一个Actions、Service、import是一个section,分别实现不同的section解析代码。
  • 基于行解析,行解析函数与当前所在的section有关,使用函数指针实现。
  • 利用空白字符(一个或多个空行)实现分词,当检测到新的一行时,识别关键词为on、service、import,若是则认为一个section开始了,同时也意味着上一个section终结了。import使用递归实现
  • 每个Actions创建一个struct action数据结构,每个command创建一个struct command数据结构,action中有一个command的链表;每个service创建一个struct service数据结构。
  • 创建全局Actions链表,将识别到Actions都加入全局链表中,创建全局Service链表,将识别到的Service加入到全局链表中
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值