#### step1: boot rom

执行固化在rom中的指令，把boot loader加载到内存，跳转到boot loader继续执行。

#### step2: boot loader

源代码在bootable目录。也是OEM厂商或运营商加锁和限制的地方。分为两个阶段。

第一阶段：检测ram，加载第二阶段的程序。

第二阶段：设置网络，内存等。

#### step3: Linux kernel

源代码在kernel目录。内核启动时，设置缓存、被保护存储器、计划列表，加载驱动。当内核完成系统设置，它首先在系统文件中寻找”init”文件，然后启动root进程或者系统的第一个进程。

- init/main.c : start_kernel

- init/main.c : rest_init

- init/main.c : kernel_init

  `ramdisk_execute_command`是内核参数`rdinit`的值，默认为`/init`

- init/main.c : run_init_process

  init进程分两个阶段，第一阶段在内核态，通过执行`kernel_execve`函数强行转到用户态，第二阶段在用户态继续执行。

#### step4: init process

源代码在system/core/init/init.c，init.rc在system/core/rootdir/init.rc。

init是第一个进程，也叫root进程或所有进程的父进程。在此阶段可以看到屏幕上的"Android" Logo。

##### init进程的职责

1. 挂载目录，比如/sys, /dev, /porc。

2. 运行init.rc脚本，这会创建系统中的几个关键进程，包括java世界的开创者zygote进程也是在这里创建的。脚本语言的介绍详见system/core/init/README.md。

3. 作为守护进程。

4. 属性服务。

5. 处理子进程终止。

   详见`queue_builtin_action(signal_init_action, "signal_init")`

##### init进程第一阶段

1. 依据程序名进行跳转，就是先决定要跑哪个程序

   argv[0]是当前运行程序的名称，当前运行程序的名称有可能是init, modprobe, uenventd或watchdogd。其中，modprobe、uenventd和watchdogd都是指向init的软链接。

   如果当前运行的程序名是modprobe，则执行`modprobe_main`，应该是用来加载内核模块的。

   如果当前运行的程序名是ueventd，则执行`ueventd_main`，进行设备节点的创建和权限设定。对于ueventd_main，首先把umask修改为0；然后使用InitKernelLogging函数初始化内核的Log系统，这样ueventd就能通过内核的Log系统输出到串口了；然后注册selinux的回调函数用于打印Log；CreateDeviceHandler使用parser.ParseConfig来解析rc文件，。

   如果当前运行的程序名是watchdogd，则执行`watchdogd_main`，用于系统出问题时重启系统。

   如果REBOOT_BOOTLOADER_ON_PANIC，则`install_reboot_signal_handlers`，将一些信号的标志设置为SA_RESTART，一旦监听到这些信号则执行重启操作。

   在当前环境中加入PATH变量。

   由于INIT_SECOND_STAGE是在is_first_stage里定义的，这就确保了is_first_stage只能执行一次。

2. 挂载文件系统并创建目录，Linux需要有这些文件系统才能运行

   `umask(0)`用于清空文件默认权限。这样才能保证后面代码里对文件权限的设置生效了，否则有可能被umask()的设置给屏蔽掉。

   在根目录上建立initramdisk所需的基本文件系统（文件系统的其它部分是在随后的rc文件里建立的）。

   > `mount`的第一个参数是源(通常是一个设备名)，第二个参数是所要挂载的目标目录，第三个参数是文件系统类型，第四个参数是读写标记(MS_NOSUID表示执行时不遵照set-user-id位和set-group-id位)，第五个参数是要挂载的文件系统可能会有的特有参数。

   > `mount`挂载了如下五个源：**tmpfs**是内存中的文件系统，读写速度快。**proc**是内存中内核的数据结构。**devpts**为伪终端提供了一个标准接口。**sysfs**是把proc, devfs, devpts统一起来。**selinuxfs**也是虚拟文件系统，用来存放SELinux安全策略文件。

   > `mknode`用于创建设备文件。第一个参数是设备所在目录，第二个参数是设备类型和读写访问标志（S_IFCHR表示字符设备文件），第三个参数是设备(设备由makedev(a,b)创建，其中a为次设备号，b为主设备号)。

3. 初始化日志输出，挂载分区设备

   InitKernelLogging用于初始化日志系统，它首先把stdin,stdout,stderr重定向到/sys/fs/selinux/null，这样printf或其它打印的函数都输出不了，就不会引发异常了(因为在这个阶段，printf函数是不能用的，会引发错误)。然后调用InitLogging初始化日志系统，这是把init进程的log打印到内核printk的log buffer，因为这个时候没有shell，不能进行通用的log输出，借助内核的功能是较好的选择。

   DoFirstStageMount用于初始化特定设备并挂载。

4.  启用SELinux安全策略

   SetInitAvbVersionInRecovery()在刷机模式下初始化avb的版本,不是刷机模式直接跳过。

   > Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dm-verity, 原理都是对二进制文件进行签名，在系统启动时进行认证，确保系统运行的是合法的二进制镜像文件。其中认证的范围涵盖：bootloader，boot.img，system.img

   selinux_initialize(true)加载SELinux policy，也就是安全策略。

   > SELinux是「Security-Enhanced Linux」的简称，是美国国家安全局和SCC开发的 Linux的一个扩张强制访问控制安全模块。 在这种访问控制体系的限制下，进程只能访问那些在他的任务中所需要文件。

   现在是在内核态，所以在SELinux policy加载后，需要再次执行init转换到用户态。函数selinux_android_restorecon("/init", 0)就是做这个工作的。

5. 开始第二阶段前的准备

   设置环境变量INIT_SECOND_STAGE和INIT_STARTED_AT，为第二阶段做准备。

   execv(path,args)再次执行init，进入用户态的init进程。

##### init进程第二阶段

1. 创建进程会话密钥并初始化系统属性

   InitKernelLogging用于初始化日志系统（第一阶段在内核态，而第二阶段在用户态，所以要再次初始化日志系统）。

   keyctl_get_keyring_ID用于初始化进程会话密钥。KEY_SPEC_SESSION_KEYRING表示获取当前进程的会话密钥，1表示如获取不到则创建一个。

   在`/dev`目录下创建文件`.booting`，这个文件仅仅起到标志的作用，表示系统正在启动(可以在init.rc里看到，当booting结束会删除此文件)。

   property_init用于初始化属性系统，并从指定文件读取属性。

   > property_init调用__system_property_area_init实现其功能。详见bionic/libc/include/sys和bionic/libc/bionic/system_properties.cpp

   process_kernel_dt和process_kernel_cmdline分别处理dt(设备树)和命令行属性，设备树中参数的优先级总是高于命令行。

   export_kernel_boot_props用于处理其它的一些属性。

   分别将INIT_STARTED_AT、INIT_SELINUX_TOOK和INIT_AVB_VERSION的值设置为系统属性，都是通过property_set实现的（property_set就是用来设置系统属性的）。

   使用unsetenv清空若干环境变量，因为这些环境变量已经存入到系统属性中去了。

2. 设置第二阶段的SELinux

   selinux_initialize(false)执行第二阶段的selinux初始化，在这里参数false只起个开关的作用，表明执行的是第二阶段的初始化。

   selinux_restore_context用于恢复安全上下文。

3. 新建epoll并初始化子进程终止信号处理函数

   epoll_create1用于创建epoll实例，并返回epoll的文件描述符。epoll是Linux用来做事件触发的。

   signal_handler_init且来注册signal信号的处理函数。

4. 设置其他系统属性并开启系统属性服务

   property_load_boot_defaults从文件中加载一些属性。

   export_oem_lock_status设置ro.boot.flash.locked属性。

   start_property_service开启一个socket监听系统属性的设置。

   set_usb_controller设置sys.usb.controller属性。

##### init进程第二阶段之解析rc文件

1. 解析rc文件

   set_function_map把function_map存放到Action中作为成员属性。

   创建对server、action、import的解析器。parser.AddSectionParser用来new一个Parser(解析器)，然后把它们放到一个map里存起来。

   获取ro.boot.init_rc的属性值，如其值为空则解析`/init.rc`和`/{system,vendor,odm}/etc/init`下的rc文件，如非空则解析其属性值。

   > parser.ParseConfig()是用来解析rc文件的，其定义在init/init_parser.cpp文件。它就是判断参数是不是目录，如果是目录则调用ParseConfigDir()，如果是文件则调用ParseConfigFile()。
   >
   > ParseConfigFile()，首先用ReadFile()把数据读取到data，然后调用ParseData来解析data，最后遍历section_parsers_并依次调用其EndFile()。

   

2. 加入一些事件和Action

   am.QueueEventTrigger("early-init")用于把early-init事件放到事件队列里。

   am.QueueBuiltinAction用于添加Action，第一个参数是要执行的command，第二个参数是trigger。

   am.QueueEventTrigger("init")用来把init事件放到事件队列里，init事件的作用触发所有的boot actions。

   bootmode是个全局静态变量，用来标示启动模式，android的启动模式分为正常开机流程和关机充电流程。如果bootmode为"charge"则是关机充电流程，则把事件charge放到事件队列里；否则就是就是正常开机流程，这会把事件late-init放到事件队列里。
   
3. 事件队列里Action的执行过程

   Action的执行次序是early-init, init, late-init。

   在early-init里，首先是设置init进程及其子进程的oom_adj，然后禁用键盘中断，然后是创建一些目录挂载一些设备，最后启动uenventd服务。uenventd在/sbin/uenventd，是指向init的软链接，在init的main函数里可见最终调用的是ueventd_main函数。

   在init里干了大部分的初始化工作。

   在late-init里是把更多的Action加到了事件队列里。

##### init进程第二阶段之服务处理

init进程作为1号进程是永不退出的，这里的while(true)循环就保证了这一点。

之前的工作是建立各种各样的事件，并没有去触发。只有在本while循环里才真正的触发所有的事件，并用epoll不断监听新的事件。

1. 如要执行关机命令，则通过`HandlePowerctlMessage()`来执行之。

2. 当没有属性设置且没有Server开始运行时，才通过`am.ExecuteOneCommand()`从current_executing_actions\_列表里执行一个command，由于外部是一个无限循环，所以current_executing_actions\_列表里的每个command都会执行一遍。这样的条件设定是为了保证属性设置和Server启动的完整性。

3. 当没有属性设置且没有Server开始运行时：
   - 如系统不是正在关机，执行`restart_processes()`来判断当前Server是否需要重启，如需重启则执行重启操作。
   - 如有进程需要重启，设置`epoll_timeout_ms`的值
   - 如有其它command需要执行，则将`epoll_timeout_ms`设置为0以立即唤醒。

4. `epoll_wait`用于监听事件的产生，参数`epoll_fd`是由前面的`epoll_create1()`创建的epoll的文件描述符，参数`ev`用于保存事件信息，参数`epoll_timeout_ms`是等待时间

5. 如未监听到事件，则向log系统添加一条监听失败的信息；如监听到事件，则直接执行`ev.data.ptr()`（即epoll_ctl注册时的回调函数）。

#### step5: Zygote

源代码在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

Zygote是一个虚拟机进程，由init进程启动。Zygote预加载以及初始化核心库类。

在此阶段可以看到启动画面。

##### Zygote进程触发过程

- init.rc里`import /init.{ro.zygote}.rc`引入了Zygote进程

  ro.zygote可能取4个值：zygote32为纯32位模式，zygote64为纯64位模式，zygote32_64以32位为主模式，zygote64_32以64位为主模式

- init.zygote64_32.rc定义了两个进程：zygote和zygote_secondary

  zygote程序是app_process64，zygote_secondary程序是app_process32。两者的参数和属性基本相同。

- 在init.cpp的main里把late-init加入运行队列，在init.rc的late-init里把zygote-start加入运行队列，在init.rc的zygote-start里启动zygote进程和zygote_secondary进程。

- 在init/builtins.cpp的builtin_functions里定义了start命令对应的函数do_start，do_start函数最终是调用对应server的Start方法，Start方法主要是fork一个新进程然后执行对应server的二进制文件。

- zygote进程的源码在frameworks/base/cmds/app_process/app_main.cpp

##### Zygote进程之参数解析

app_main.cpp里的main函数主要做的就是参数解析。此函数有两种启动模式，一是作为Zygote进程进行初始化，一是作为普通应用程序启动。两者最终都是调用AppRuntime对象的start函数，加载对应的java类(ZygoteInit或RuntimeInit)，并将之前整理的参数传入进去。

1. 将参数argv[]放入字符串argv_string
2. 创建AppRuntime对象runtime，并传入除argv[0]的所有参数
3. '--'之前或以非'-'开头的参数都将传入vm(即/system/bin)，vm参数后的第一个参数(即Xzygote)不再使用。--zygote的意思是开启zygote模式，--start-system-server的意思是开启系统服务，--socket-name指定socket的名称。必须复制参数字符串，但进入vm的spaced commands是个例外。
4. spaced_commands里定义的两个参数是Java程序需要依赖的Jar包
5. 使用runtime.addOption方法加入参数，要加入的都是'--'开头的参数，但若包含spaced_commands里的参数则直接加入
6. 判断zygote, niceName, startSystemServer, application, niceName.setTo, className.setTo的值，它们主要决定了启动模式。
7. 如果className非空，表明不是zygote模式，只需传送程序参数即可。否则就是zygote模式，则创建Dalvik缓存，加入start-system-server参数，加入abiFlag参数，加入剩下的参数。
8. 如果niceName非空，则设置进程别名。
9. 如果是zygote模式，则加载ZygoteInit；如果是application模式，则加载RuntimeInit；否则打印错误。

##### 创建虚拟机

frameworks/base/core/jni/AndroidRuntime.cpp里的start方法

1. 打印一些日志，设置环境变量ANDROID_ROOT。

2. jni_invocation.Init初始化JNI

   首先通过dlopen加载libart.so获得其句柄，然后调用dlsym从libart.so中找到相关函数用于后续的虚拟机创建。

3. startVm创建虚拟机

   一是从各种系统属性中读取一些参数，然后通过addOption设置到AndroidRuntime的mOptions数组；二是使用libart.so中的JNI_CreateJavaVM创建虚拟机，将这些参数传入。

4. onVmCreated表示创建完成

5. startReg为startVm创建的虚拟机注册JNI函数

   首先设置了Android创建线程的处理函数，然后创建了一个局部引用作用域，最后调用register_jni_procs进行JNI注册

6. JNI方式调用ZygoteInit类的main函数

   即`env->CallStaticVoidMethod(startClass, startMeth, strArray);`

##### JNI反射调用Java

frameworks/base/core/jni/AndroidRuntime.cpp里的start方法

1. 首先把参数保存在strArray里
2. 启动VM，此线程将成为VM的主线程，此线程不会返回除非VM退出。

##### ZygoteInit

framework/base/core/java/com/android/internal/os/ZygoteInit.java的main方法

1. 创建一个ZygoteServer对象
2. 设置标记不允许新建线程
3. 设置Zygote进程的组id为zygote的pid
4. 创造Java的世界(在一个try代码块里的内容)
   - 把Zygote启动时间报告给tron(使用`MetricsLogger.histogram`)
   
   - 跟踪调试ZygoteInit(`bootTimingsTraceLog.traceBegin`)
   
   - 注册Ddms的处理类(`RuntimeInit.enableDdms`)
   
   - 在一个for循环里进行参数解析
   
   - 注册zygote用的socket，用以响应子孙后代的请求(`zygoteServer.registerServerSocket`)
   
     > zygote与其它程序的IPC通信使用的不是Binder，而是Socket。本函数的作用就是建立IPC通信的服务端。首先，从环境变量中获取Socket的fd，然后用LocalServerSocket创建服务端的Socket，这个Socket将监听并访问client。
   
   - 在某些配置里，要避免过早地加载资源和类。因此，在第一次fork之前要预加载一些东西。为Java世界添砖加瓦。(`preload`)
   
     > preloadClasses预加载一些类，这些类在framework/base/config/preloaded-classes里。preloadResources预加载一些常用资源。
   
   - 初始化垃圾回收(`gcAndFinalize`)
   
   - 禁止追踪
   
   - Zygote解除挂载根空间
   
   - 设置seccomp策略
   
   - 启动SystemServer进程(`forkSystemServer`)
   
     > zygote工作压力太大，创造一个SystemServer来为Java世界服务。
   
   - 至此，Java世界的初创已完成。zygote将永远进行select循环，当收到子孙后代的请求时再醒来工作(`zygoteServer.runSelectLoop`)
   
     > 客户在zygote中用ZygoteConnection对象来表示，runSelectLoop的作用就是处理客户连接和客户请求。当i==0的时候就表示有一人客户端连接上了，否则就表示客户端发送了请求。客户端的请求由connection.processOneCommand来处理。
5. try代码块执行到最后，要执行finally代码块的内容，关闭zygoteServer的套接字。(`zygoteServer.closeServerSocket`)
6. 已经退出了select循环，进入了子进程，继续执行命令`caller.run()`。

#### step6: System Servers

Zygote启动系统服务，即ZygoteInit类里的forkSystemServer方法。

1. 从`pid=Zygote.forkSystemServer`可见system-server是从`Zygote.forkSystemServer`函数fork出来的。`Zygote.forkSystemServer`的nativeForkSystemServer实现在哪里呢？
2. `if(pid==0)`表明system_server被成功创建，通过`handleSystemServerProcess`来执行system_server的使命。其中`zygoteServer.closeServerSocket`用于关闭从zygote那里继承来的Socket。
3. `handleSystemServerProcess`函数的核心在于最后一句，`ZygoteInit.zygoteInit`。
4. 在`ZygoteInit.zygoteInit`函数里，`RuntimeInit.commonInit`进行一些常规的初始化，`ZygoteInit.nativeZygoteInit`进行native层的初始化，`RuntimeInit.applicationInit`的核心则是调用`findStaticMain`函数，`findStaticMain`函数调用`MethodAndArgsCaller`，`MethodAndArgsCaller`在其`run`函数里通过抛出异常的方式调用SystemServer类的main函数。
5. SystemServer类的main函数就一句话`new SystemServer().run()`。
6. 

#### step7: 引导完成

系统服务运行起来后，引导完成，此时会发送开机启动广播`ACTION_BOOT_COMPLETED`

#### 附录：init语言

详见：system/core/init/README.md

由五类语句构成：Actions, Commands, Services, Options and Imports。

从整体来看，一个rc文件是由若干个段组成的。一个段只能是Actions或Server其中之一：

- Actions，由trigger和它下面的一堆Commands组成。
- Servers，由一堆Options组成。需要注意的是，Servers的启动也是在Actions里指定的(使用class_start命令或者start命令).

注意：Actions和Servers不能重名。如果重名了，后出现的定义将会作为错误而忽略掉。

##### .rc文件

init language用在.rc文件中，它可能出现在系统中的多个位置。

- /init.rc是最初的.rc文件，它会随着init的执行而执行，用于系统的最初设置。

##### Actions

Actions就是触发器(trigger)加一串命令(commands)，触发器用来决定是否执行一个Action。如果满足了触发器的条件且action还不在运行队列里，则这个action会被加到运行队列的尾部。

运行的格式如下：

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

以on开头，后面的trigger是判断条件(详见[Triggers](#triggers))，command是具体要执行的操作。

##### Services

Services是程序，它由init进程启动，并且可以退出后重启(可选)。

服务的格式如下：

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

name是程序名，pathname是程序所在的路径，argument是程序的参数，option是服务配置(详见[Options](#options))。

##### Options

Options是Services的修改器，它们影响init进程如何运行service以及何时运行service。

- animation，将会包含所有启动和关闭animation所必须的服务。

- capabilities，在执行服务的时候设置capabilities.

- class，为服务指定所属的类型。在同一个类型里的服务可以同时打开或关闭。如不使用此选项明确指定服务所属的类型，则默认在default里。

- console，此服务需要一个console。默认设备为`/dev/console`。也可自己指定，如`console /dev/tty0`，其中`/dev/`是可忽略的，即等同于`console tty0`。

- critical，此服务是一个设备关键服务。如果在4分钟内退出4次，设备将会重启进入recovery模式。

- disabled，此服务无法通过它所属的类型(class)来启动，必须通过它的名字来启动。这就意味着启动一个类型的服务时这种服务是不会被启动的，也就是它被disable掉了。

- file，打开一个文件并将它的fd传给启动的进程。

- group，在执行服务前改变组名，默认的组是root。

- memcg.swappiness，设置子进程的变量值。

- memcg.soft_limit_in_bytes，设置子进程的变量值。

- memcg.limit_in_bytes，设置子进程的变量值。

- namespace，当forking一个服务的时候，进入一个新的PID或挂载namespace。

- oneshot，当服务退出后不要重启它。如果一个程序不是守护进程，应该使用这个选项。

- onrestart当服务重启的时候执行一条命令。

- oom_score_adjust，设置子进程的变量值。

- priority，调度service进程的优先级。

- seclabel，在执行服务前改变为seclabel。

- setenv，在启动的进程里设置环境变量。

- shutdown，设置关机的时候此service进程的行为。默认是关机进程通过传递SIGTERM和SIGKILL来杀死此服务。

- socket \<name> \<type> \<perm> [\<user> [\<group>]]

  创建一个unix域socket，它的名字叫/dev/socket/\<name>，并将它的fd传给启动的进程。

- user，在执行服务前改变用户(user)，默认的用户是root。

- writepid，当子进程forks的时候将其pid写到给定文件中。

##### Triggers

Triggers就是个字符串，用来匹配特定事件和触发一个action。分为事件触发器和权限触发器。

事件触发器就是个简单的字符串，如"boot"或"late-init"。

权限触发器就是个键值对，其格式为`property:<name>=<value>`。

一个动作可以有多个权限触发器但只能有一个事件触发器。

##### Commands

`bootchart [start|stop]`

> Start/stop bootcharting. These are present in the default init.rc files,
> but bootcharting is only active if the file /data/bootchart/enabled exists;
> otherwise bootchart start/stop are no-ops.

`chmod <octal-mode> <path>`
> Change file access permissions.

`chown <owner> <group> <path>`
> Change file owner and group.

`class_start <serviceclass>`
> Start all services of the specified class if they are
> not already running.  See the start entry for more information on
> starting services.

`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
> currently running.

`class_reset <serviceclass>`
> Stop all services of the specified class if they are
> currently running, without disabling them. They can be restarted
> later using `class_start`.

`class_restart <serviceclass>`
> Restarts all services of the specified class.

`copy <src> <dst>`
> Copies a file. Similar to write, but useful for binary/large
> amounts of data.
> Regarding to the src file, copying from symbolic link file and world-writable
> or group-writable files are not allowed.
> Regarding to the dst file, the default mode created is 0600 if it does not
> exist. And it will be truncated if dst file is a normal regular file and
> already exists.

`domainname <name>`
> Set the domain name.

`enable <servicename>`
> Turns a disabled service into an enabled one as if the service did not
> specify disabled.
> If the service is supposed to be running, it will be started now.
> Typically used when the bootloader sets a variable that indicates a specific
> service should be started when needed. E.g.
>
>    on property:ro.boot.myfancyhardware=1
> ​       enable my_fancy_service_for_my_fancy_hardware
> ​       

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

> Fork and execute command with the given arguments. The command starts
> after "--" so that an optional security context, user, and supplementary
> groups can be provided. No other commands will be run until this one
> finishes. _seclabel_ can be a - to denote default. Properties are expanded
> within _argument_.
> Init halts executing commands until the forked process exits.

`exec_start <service>`
> Start a given service and halt the processing of additional init commands
> until it returns.  The command functions similarly to the `exec` command,
> but uses an existing service definition in place of the exec argument vector.

`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
> global environment (which will be inherited by all processes
> started after this command is executed)

`hostname <name>`
> Set the host name.

`ifup <interface>`
> Bring the network interface _interface_ online.

`insmod [-f] <path> [<options>]`
> Install the module at _path_ with the specified options.
> -f: force installation of the module even if the version of the running kernel
> and the version of the kernel for which the module was compiled do not match.

`load_all_props`
> Loads properties from /system, /vendor, et cetera.
> This is included in the default init.rc.

`load_persist_props`
> Loads persistent properties when /data has been decrypted.
> This is included in the default init.rc.

`loglevel <level>`
> Sets the kernel log level to level. Properties are expanded within _level_.

`mkdir <path> [mode] [owner] [group]`
> Create a directory at _path_, optionally with the given mode, owner, and
> group. If not provided, the directory is created with permissions 755 and
> owned by the root user and root group. If provided, the mode, owner and group
> will be updated if the directory exists already.

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

> Attempt to mount the named device at the directory _dir_
> _flag_s include "ro", "rw", "remount", "noatime", ...
> _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
> a comma separated string, eg: barrier=1,noauto\_da\_alloc

`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
> restarting, otherwise, it just starts the service.

`restorecon <path> [ <path>\* ]`
> 将<path>所代表的文件恢复到安全上下文里，这个安全上下文是在file_contexts配置中指定的。init.rc创建的目录不需要执行这个命令，因为init会自动正确地标记它们。

`restorecon_recursive <path> [ <path>\* ]`
> Recursively restore the directory tree named by _path_ to the
> security contexts specified in the file\_contexts configuration.

`rm <path>`
> Calls unlink(2) on the given path. You might want to
> use "exec -- rm ..." instead (provided the system partition is
> already mounted).

`rmdir <path>`
> Calls rmdir(2) on the given path.

`setprop <name> <value>`
> Set system property _name_ to _value_. Properties are expanded
> within _value_.

`setrlimit <resource> <cur> <max>`
> Set the rlimit for a resource.

`start <service>`
> 如果一个服务还没有运行则启动它。
> Note that this is _not_ synchronous, and even if it were, there is
> no guarantee that the operating system's scheduler will execute the
> service sufficiently to guarantee anything about the service's status.

> This creates an important consequence that if the service offers
> functionality to other services, such as providing a
> communication channel, simply starting this service before those
> services is _not_ sufficient to guarantee that the channel has
> been set up before those services ask for it.  There must be a
> separate mechanism to make any such guarantees.

`stop <service>`

> 如果一个服务正在运行则关闭它。

`swapon_all <fstab>`
> Calls fs\_mgr\_swapon\_all on the given fstab file.

`symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_

`sysclktz <mins_west_of_gmt>`
> 设置系统时钟基础(0代表GMT，GMT的意思是格林尼治标准时间)。

`trigger <event>`
> 触发一个事件。用于从其它动作里把一个动作加到队列里。

`umount <path>`
> Unmount the filesystem mounted at that path.

`verity_load_state`
> Internal implementation detail used to load dm-verity state.

`verity_update_state <mount-point>`
> Internal implementation detail used to update dm-verity state and
> set the partition._mount-point_.verified properties used by adb remount
> because fs\_mgr can't set them directly itself.

`wait <path> [ <timeout> ]`
> Poll for the existence of the given file and return when found,
> or the timeout has been reached. If timeout is not specified it
> currently defaults to five seconds.

`wait_for_prop <name> <value>`
> Wait for system property _name_ to be _value_. Properties are expanded
> within _value_. If property _name_ is already set to _value_, continue
> immediately.

`write <path> <content>`

> 把<content>写到<path>里。<content>里的属性将会展开。

##### Imports

`import <path>`

> 解析init配置文件，扩展当前配置。如果path是目录，此目录下的所有文件都将作为配置文件被解析，但这样的解析不是递归的，下级目录将不会被解析。
>
> import是个关键字(keyword)而不是命令(command)，它不会是Action的一部分，它本身就是个独立的段(section)，它的执行逻辑如下：
>
> - 如果是一个文件则解析这个文件，如果是一个目录则按字母序解析这个目录下的所有文件
> - 如果文件里依然有import语句，则继续解析之(这意味着import不管出现在文件的什么位置，它总是最后执行的)

#### 参考文档

- [foxleezh/AOSP](https://github.com/foxleezh/AOSP)
- 《深入理解Android卷1》邓凡平
- [freshui/Android init进程分析](https://blog.csdn.net/freshui/article/details/5747199)
- [freshui/Android init脚本解析](https://blog.csdn.net/freshui/article/details/5772564)
- [zygote和system-server启动分析](https://www.cnblogs.com/samchen2009/p/3294713.html)