[Android Basics] - Android system init process startup and init.rc full analysis

This is a blog written by heart, and I hope you will read it carefully and help you find improvements in the article, thank you.

Service startup mechanism

  1.  parse_config_file(init.rc) in the main function of the system/core/init/init.c file reads and parses the contents of the init.rc file. Put the service information in the service_list of system/core/init/init_parser.cpp;
  2. The main function of the system/core/init/init.c file continues to execute restart_service_if_needed(...) -> service_start(...) -> Execve(...) to establish the service process.

In order to let everyone see more clearly, the last picture first "Overall Startup Framework":

 

Introduction to init.rc

At present, Linux has many communication mechanisms to interact between user space and kernel space, such as device driver files (located in the /dev directory), memory files (/proc, /sys directories, etc.). Students who know Linux should know that one of the important features of Linux is that everything exists in the form of files. For example, a device usually corresponds to one or more device files. These files that interact with the kernel space are in the user space, so after loading in the Linux kernel, you need to first create the directory where these files are located. The program that completes these tasks is the init that this article will introduce. init is a command line program. One of its main tasks is to create a directory where these files that interact with the kernel space are located. When the Linux kernel is loaded, the first thing to do is to call the init program, that is, init is the first program executed in the user space.

Although the work done by init is not much, the code is still very complicated. The Init program is not composed of a source code file, but is formed by linking a set of object files of source code files. These files are located in the following directories.

It should be understood that these init.rc are just grammar files, not programs. The real entry point is system/core/init/init.c mentioned above. Because the init.rc file is relatively large, in the second part of the article, I will briefly analyze the init startup process through the main function;

There are two init.rc, located in:

./system/core/rootdir/init.rc

./bootable/recovery/etc/init.rc

It can be guessed from the catalog that the two init.rc usage scenarios are different. One is used for flashing, that is, entering recovery mode, and the other is used for normal startup; our focus here is the above one, which is The one associated with init.c;

init.rc grammatical structure analysis

To understand how init.rc is parsed, we need to look at the documentation first.

init.rc is located in /bootable/recovery/etc/init.rc.

The Android initialization language contains four types of declarations:

  • Actions
  • Commands
  • Services
  • Options

All of these are in units of lines, and the various numbers are separated by spaces.

C-style backslashes can be used to insert spaces between tokens.

Double quotes can also be used to prevent a string from being split into multiple tokens by spaces.

The backslash at the end of the line is used to wrap the line, and the comment line starts with a "#" sign (spaces are allowed).

It should be noted that this is only a grammar file, just like an xml file, without execution order, the parser obtains the desired data by reading this file, including service, action, etc.

Actions and Services declare a new grouping Section. All commands or options belong to the most recently declared group. Commands or options before the first group will be ignored.

Actions and Services have unique names. If there is a duplicate name, the second statement will be ignored as an error.

Actions

Actions represent some Actions. Action represents a set of commands (Commands), Actions have a trigger (trigger) that determines when to execute this Action, that is, under what circumstances can the defined command in the Action be executed. When some conditions meet the conditions of the trigger, the command defined in the Action will be added to the end of the command queue to be executed (if this group of commands should be in the queue, it will not be added).

Each action in the queue is extracted once, and each command (command) in this action is executed in turn when an Action is removed from the queue.

The format of Action is as follows:

on <trigger> [&& <trigger>]*
    <command1>
    <command2>
    <command3>
    ...

On is followed by a trigger. When the trigger is triggered, command1, command2, and command3 will be executed in sequence until the next Action or the next Service.

To put it simply, Actions is a startup script defined by Android at startup. When the conditions are met, the script will be executed. There are some commands in the script. Different scripts are distinguished by on.

Trigger

Trigger is the trigger we mentioned above. It is essentially a string that can match a certain event containing the string.

Triggers are subdivided into event triggers and property triggers.

A trigger is a string used to match a specific event type to make Actions happen.

Event triggers can be triggered by the "trigger" command or via QueueEventTrigger() during initialization. They are usually simple strings defined in advance, such as boot, late-init.

Property trigger is triggered when the variable value of the specified property becomes the specified value, and its format is property := *.

An Action can have multiple attribute triggers, but there is at most one event trigger. Let's look at two examples below:

on boot && property: a=b

This Action will only be triggered when the boot event occurs and the attributes a and b are equal.

on property:a=b && c=d

The Action will be triggered in the following three situations:

  • At startup, if the value of attribute a is equal to b and the value of attribute c is equal to d;
  • When the value of attribute c is already d, the value of attribute a is updated to b;
  • When the value of attribute a is already b, the value of attribute c is updated to d;

The following types of event triggers are commonly used in AIL:

类型                    说明
-------------------------------------------------------------------------
boot                    init.rc 被装载后触发
device-added-<path>     指定设备被添加时触发
device-removed-<path>   指定设备被移除时触发
service-exited-<name>   在特定服务(service)退出时触发
early-init              初始化之前触发
late-init               初始化之后触发
init                    初始化时触发(在 /init.conf(启动配置文件)被装载之后)

The trigger of Init is determined by the function action_for_each_trigger in init.c (called in the main function).

Services

Services (service) is a program that starts with service and is started by the init process. Generally, it runs with another init subprogram. Therefore, it is necessary to determine whether the corresponding executable file exists before starting the service. The child process generated by init is defined in the rc file, and each service in it will generate a child process by fork when it is started. The form of Services is as follows:

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

among them:

  • name: service name;
  • pathname: options set by the current service;
  • option: the option set by the service;
  • argument: optional parameter;

Detailed init.rc file

In order to facilitate understanding, I will parse the entire init.rc so that you can understand the entire process; if you want to understand the init syntax analysis under recovery, please refer to this article "Init.rc syntax analysis under recovery".

The amount of code is relatively large, if you think it looks strenuous, you can pick the green part to see:

# 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 <filename>一个init配置文件,扩展当前配置。】"
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc

"【触发条件early-init,在early-init阶段调用以下行】"
on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000
	"【打开路径为<path>的一个文件,并写入一个或多个字符串】"
    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
    write /sys/fs/selinux/checkreqprot 0

    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    "【这段脚本的意思是init进程启动之后就马上调用函数setcon将自己的安全上下文设置为“u:r:init:s0”,即将init进程的domain指定为init。】"
    setcon u:r:init:s0

    # Set the security context of /adb_keys if present.
    "【恢复指定文件到file_contexts配置中指定的安全上线文环境】"
    restorecon /adb_keys

	"【执行start ueventd的命令。ueventd是一个service后面有定义】 "
    start ueventd

	"【mkdir <path> [mode] [owner] [group]   //创建一个目录<path>,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和root组。】"
    # create mountpoints
    mkdir /mnt 0775 root system

on init
	"【设置系统时钟的基准,比如0代表GMT,即以格林尼治时间为准】"
    sysclktz 0

"【设置kernel日志等级】"
loglevel 6 ####
    write /proc/bootprof "INIT: on init start" ####
	
	"【symlink <target> <path>    //创建一个指向<path>的软连接<target>。】"
    # Backward compatibility
    symlink /system/etc /etc
    symlink /sys/kernel/debug /d

    # Right now vendor lives on the same filesystem as system,
    # but someday that may change.
    symlink /system/vendor /vendor

	"【创建一个目录<path>,可以选择性地指定mode、owner以及group。】"
    # Create cgroup mount point for cpu accounting
    mkdir /acct
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

	"【mount <type> <device> <dir> [ <mountoption> ]   //在目录<dir>挂载指定的设备。<device> 可以是以 mtd@name 的形式指定一个mtd块设备。<mountoption>包括 ro、rw、remount、noatime、 ...】"
    # Create cgroup mount point for memory
    mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000
    mkdir /sys/fs/cgroup/memory 0750 root system
    mount cgroup none /sys/fs/cgroup/memory memory
    write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1
    "【chown <owner> <group> <path>   //改变文件的所有者和组。】"
    
    "【后面的一些行因为类似,就省略了】"
    .....

# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
	"【停止指定类别服务类下的所有已运行的服务】"
    class_stop charger
    "【触发一个事件,将该action排在某个action之后(用于Action排队)】"
    trigger late-init

# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
	"【从/system,/vendor加载属性。默认包含在init.rc】"
    load_all_props

# Indicate to fw loaders that the relevant mounts are up.
on firmware_mounts_complete
	"【删除指定路径下的文件】"
    rm /dev/.booting

# Mount filesystems and start core system services.
on late-init
	"【触发一个事件。用于将一个action与另一个 action排列。】"
    trigger early-fs
    trigger fs
    trigger post-fs
    trigger post-fs-data

    # Load properties from /system/ + /factory after fs mount. Place
    # this in another action so that the load will be scheduled after the prior
    # issued fs triggers have completed.
    trigger load_all_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot


on post-fs
	...
    "【一些创造目录,建立链接,更改权限的操作,这里省略】"

on post-fs-data
	...
	"【一些创造目录,建立链接,更改权限的操作,这里省略】"

	"【恢复指定文件到file_contexts配置中指定的安全上线文环境】"
    restorecon /data/mediaserver

	"【将系统属性<name>的值设置为<value>,即以键值对的方式设置系统属性】"
    # Reload policy from /data/security if present.
    setprop selinux.reload_policy 1

	"【以递归的方式恢复指定目录到file_contexts配置中指定的安全上下文中】"
    # Set SELinux security contexts on upgrade or policy update.
    restorecon_recursive /data

    # If there is no fs-post-data action in the init.<device>.rc file, you
    # must uncomment this line, otherwise encrypted filesystems
    # won't work.
    # Set indication (checked by vold) that we have finished this action
    #setprop vold.post_fs_data_done 1

on boot
	"【初始化网络】"
    # basic network init
    ifup lo
    "【设置主机名为localhost】"
    hostname localhost
    "【设置域名localdomain】"
    domainname localdomain

	"【设置资源限制】"
    # set RLIMIT_NICE to allow priorities from 19 to -20
    setrlimit 13 40 40

	"【这里省略了一些chmod,chown,等操作,不多解释】"
   ...
   

    # Define default initial receive window size in segments.
    setprop net.tcp.default_init_rwnd 60
	
	"【重启core服务】"
    class_start core

on nonencrypted
    class_start main
    class_start late_start

on property:vold.decrypt=trigger_default_encryption
    start defaultcrypto

on property:vold.decrypt=trigger_encryption
    start surfaceflinger
    start encrypt

on property:sys.init_log_level=*
    loglevel ${sys.init_log_level}

on charger
    class_start charger

on property:vold.decrypt=trigger_reset_main
    class_reset main

on property:vold.decrypt=trigger_load_persist_props
    load_persist_props

on property:vold.decrypt=trigger_post_fs_data
    trigger post-fs-data

on property:vold.decrypt=trigger_restart_min_framework
    class_start main

on property:vold.decrypt=trigger_restart_framework
    class_start main
    class_start late_start

on property:vold.decrypt=trigger_shutdown_framework
    class_reset late_start
    class_reset main

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

# system server cannot write to /proc/sys files,
# and chown/chmod does not work for /proc/sys/ entries.
# So proxy writes through init.
on property:sys.sysctl.extra_free_kbytes=*
    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}

# "tcp_default_init_rwnd" Is too long!
on property:sys.sysctl.tcp_def_init_rwnd=*
    write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}

"【守护进程】"
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

"【日志服务进程】"
service logd /system/bin/logd
    class core
    socket logd stream 0666 logd logd
    socket logdr seqpacket 0666 logd logd
    socket logdw dgram 0222 logd logd
    seclabel u:r:logd:s0

"【Healthd是android4.4之后提出来的一种中介模型,该模型向下监听来自底层的电池事件,向上传递电池数据信息给Framework层的BatteryService用以计算电池电量相关状态信息】"
service healthd /sbin/healthd
    class core
    critical
    seclabel u:r:healthd:s0

"【控制台进程】"
service console /system/bin/sh
	"【为当前service设定一个类别.相同类别的服务将会同时启动或者停止,默认类名是default】"
    class core
    "【服务需要一个控制台】"
    console
    "【服务不会自动启动,必须通过服务名显式启动】"
    disabled
    "【在执行此服务之前切换用户名,当前默认的是root.自Android M开始,即使它要求linux capabilities,也应该使用该选项.很明显,为了获得该功能,进程需要以root用户运行】"
    user shell
    seclabel u:r:shell:s0

on property:ro.debuggable=1
    start console

# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd --root_seclabel=u:r:su:s0
    class core
    "【创建一个unix域下的socket,其被命名/dev/socket/<name>. 并将其文件描述符fd返回给服务进程.其中,type必须为dgram,stream或者seqpacke,user和group默认是0.seclabel是该socket的SELLinux的安全上下文环境,默认是当前service的上下文环境,通过seclabel指定】"
    socket adbd stream 660 system system
    disabled
    seclabel u:r:adbd:s0

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

"【内存管理服务,内存不够释放内存】"
service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system

"【ServiceManager是一个守护进程,它维护着系统服务和客户端的binder通信。
在Android系统中用到最多的通信机制就是Binder,Binder主要由Client、Server、ServiceManager和Binder驱动程序组成。其中Client、Service和ServiceManager运行在用户空间,而Binder驱动程序运行在内核空间。核心组件就是Binder驱动程序了,而ServiceManager提供辅助管理的功能,无论是Client还是Service进行通信前首先要和ServiceManager取得联系。而ServiceManager是一个守护进程,负责管理Server并向Client提供查询Server的功能。】"
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    "【servicemanager 服务启动时会重启zygote服务】"
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm

"【Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程】"
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

"【Netd是Android系统中专门负责网络管理和控制的后台daemon程序】"
service netd /system/bin/netd
    class main
    socket netd stream 0660 root system
    socket dnsproxyd stream 0660 root inet
    socket mdns stream 0660 root system
    socket fwmarkd stream 0660 root inet

"【debuggerd是一个daemon进程,在系统启动时随着init进程启动。主要负责将进程运行时的信息dump到文件或者控制台中】"
service debuggerd /system/bin/debuggerd
    class main

service debuggerd64 /system/bin/debuggerd64
    class main

"【Android RIL (Radio Interface Layer)提供了Telephony服务和Radio硬件之间的抽象层】"
# for using TK init.modem.rc rild-daemon setting
#service ril-daemon /system/bin/rild
#    class main
#    socket rild stream 660 root radio
#    socket rild-debug stream 660 radio system
#    user root
#    group radio cache inet misc audio log

"【提供系统 范围内的surface composer功能,它能够将各种应用 程序的2D、3D surface进行组合。】"
service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

"【DRM可以直接访问DRM clients的硬件。DRM驱动用来处理DMA,内存管理,资源锁以及安全硬件访问。为了同时支持多个3D应用,3D图形卡硬件必须作为一个共享资源,因此需要锁来提供互斥访问。DMA传输和AGP接口用来发送图形操作的buffers到显卡硬件,因此要防止客户端越权访问显卡硬件。】"
#make sure drm server has rights to read and write sdcard ####
service drm /system/bin/drmserver
    class main
    user drm
    # group drm system inet drmrpc ####
    group drm system inet drmrpc sdcard_r ####

"【媒体服务,无需多说】"
service media /system/bin/mediaserver
    class main
    user root ####
#   google default ####
#   user media    ####
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm media sdcard_r system net_bt_stack ####
#   google default ####
#   group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ####

    ioprio rt 4

"【设备加密相关服务】"
# One shot invocation to deal with encrypted volume.
service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
    disabled
    "【当服务退出时,不重启该服务】"
    oneshot
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption) or trigger_restart_min_framework (other encryption)

# One shot invocation to encrypt unencrypted volumes
service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default
    disabled
    oneshot
    # vold will set vold.decrypt to trigger_restart_framework (default
    # encryption)

"【开机动画服务】"
service bootanim /system/bin/bootanimation
    class core
    user graphics
#    group graphics audio ####
    group graphics media audio ####
    disabled
    oneshot

"【在Android系统中,PackageManagerService用于管理系统中的所有安装包信息及应用程序的安装卸载,但是应用程序的安装与卸载并非PackageManagerService来完成,而是通过PackageManagerService来访问installd服务来执行程序包的安装与卸载的。】"
service installd /system/bin/installd
    class main
    socket installd stream 600 system system

service flash_recovery /system/bin/install-recovery.sh
    class main
    seclabel u:r:install_recovery:s0
    oneshot

"【vpn相关的服务】"
service racoon /system/bin/racoon
    class main
    socket racoon stream 600 system system
    # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
    group vpn net_admin inet
    disabled
    oneshot

"【android中有mtpd命令可以连接vpn】"
service mtpd /system/bin/mtpd
    class main
    socket mtpd stream 600 system system
    user vpn
    group vpn net_admin inet net_raw
    disabled
    oneshot

service keystore /system/bin/keystore /data/misc/keystore
    class main
    user keystore
    group keystore drmrpc

"【可以用dumpstate 获取设备的各种信息】"
service dumpstate /system/bin/dumpstate -s
    class main
    socket dumpstate stream 0660 shell log
    disabled
    oneshot

"【mdnsd 是多播 DNS 和 DNS 服务发现的守护程序。】"
service mdnsd /system/bin/mdnsd
    class main
    user mdnsr
    group inet net_raw
    socket mdnsd stream 0660 mdnsr inet
    disabled
    oneshot

"【触发关机流程继续往下走】"
service pre-recovery /system/bin/uncrypt
    class main
    disabled
    "【当服务退出时,不重启该服务】"
    oneshot

Guess you like

Origin blog.csdn.net/u014674293/article/details/105957405