Android 框架中 init 进程的相关知识。

init 进程是 Android 启动后,由内核启动的第一个用户级进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 内核启动过程:
# start_kernel ()
# init_post ()
# run_init_process ()
#
# static in noinline init_post (void)
# {
# if (execute_command){
# run_init_process (execute_command);
# }
# run_init_process ("/sbin/init");
# run_init_process ("/etc/init");
# run_init_process ("/bin/init");
# run_init_process ("/bin/sh");
# }

init_post () 函数调用 run_init_process () 函数,获取注册在 execute_command 中的进程文件路径,执行 execve () 系统调用,execve () 函数执行参数传递过来的文件路径下的进程 。
当跟文件系统顶层目录中不存在 init 进程或未指定启动选项 "init=" 时,内核会到 /sbin,/etc,/bin 目录下查找 init 文件,如果仍未找到,则 init 进程停止,引发 Kernel Panic。

# init 工作流程

# SIGCHLD 注册信号处理器

当子进程终止时,会产生 SIGCHLD 信号,init 进程调用信号安装函数 sigaction (),并通过参数传递至 sigaction 结构体中,完成信号处理器安装。
init 进程通过相关代码注册与子进程相关的 SIGCHLD 信号处理器,并把 sigaction 结构体中的 sa_flags 设置为 SA_NOCLDSTOP,该值表示仅当进程终止时才接受 SIGCHLD 信号。sigchld_handler 函数用于通知全局变量 signal_fd,SIGCHLD 信号已经发生。

# main () 目录生成与挂载

init 在注册完信号处理器后,创建并挂在启动所需目录。

# 初始化 log 输出设备

通过调用 log_init () 函数初始化 log 输出设备。

# 解析 init.rc 文件

init.rc 文件是 init 启动后执行的启动脚本,文件中记录着 init 进程执行的功能。在 Linux 系统中,它被定义在根文件系统的 /etc/rc.d/ 目录下,是启动时的可执行文件。
init.rc 文件在 Android 系统运行过程中用于通用的环境设置以及进程相关的定义,init.{hardware}.rc 用于定义 Android 在不同平台下的特定进程和环境设置等。
parse_config_file () 文件用于分析.rc 配置文件,参数为文件路径。读取 init.rc 文件后,生成动作列表与服务列表。根据 init.{hardware}.rc 生成的动作列表和服务列表会被添加到已生成的对应列表中去。

# 执行 early-init 动作

init 进程会依次执行 "early-init , init ,early-boot , boot" 片段中的命令。
通过 action_for_each_trigger () 将 early-init 中的命令保存到队列 action_add_queue_tail 中,之后通过 drain_action_queue () 函数将运行队列中的命令逐一取出执行。

# 创建定义好的设备节点文件

通过 device_init () 生成静态设备节点。

# 初始化属性服务

调用 property_init () 函数,在共享内存区域中,创建并初始化属性域。而后通过执行中的进程所提供的 API,访问属性域中的设置值。但更改属性值操作只能在 init 进程中运行,当修改属性值时,要预先向 init 进程提交值变更申请,然后 init 进程处理该申请,并修改属性值。

使用 load_565rle_image () 函数将文件显示在屏幕上,只需修改 INIT_IMAGE_FILE 即可更改启动 Logo。

# 属性初始设置

通过 property_set () 函数向属性域设置系统所需的一些初始值。这些设置的属性值由执行中的多种进程通过 property_get () API 来访问。

# 执行 init 动作

同 early-init。

# 启动属性服务

除了先前设置的属性外,start_property_service () 还会读取几个设置文件,并对属性进行初始化。在根文件系统的 /data/property 目录下,保存着进程生成或修改的属性值。向 init 提交修改申请后,init 进程生成 /dev/socket/property_service 来接受其它进程提交的申请。

# 创建套接字

用于 init 进程在收到子进程终止的信号时调用相应的 handler。

# 执行与 Action List 的 early-boot,boot,property 相关的命令

在 init.rc 文件的 boot 区段有一条 class_start 命令,用来逐一执行存在于服务列表中的进程列表。

# 设置事件处理循环的监视事件

注册在 POLL 中的文件描述符会在 poll () 函数中等待事件发生,若事件发生,则从 poll 函数中跳出并处理事件。
在确认事件发生前,先要在 action list 的命令中确认是否有尚未执行的命令,并执行这些命令。起初,在事件处理循环中,action list 与 service list 未含有需要执行的事情,但是在处理过注册的事件后,init 进程要做的事情会重新注册到 action list 与 service list 中。
当事件发生时,事件信息会保存在 pollfd 结构体的 udfs.revents 变量中,当 poll () 函数返回后,可以在 udfs 数组中的 revents 中查看哪些事件已经发生。
当子进程终止时,会产生 SIGCHLD 信号,POLLIN 事件会被注册到 udfs [2],revents 中。
在 Android 系统运行过程中,插入热的拔插设备时,将生成设备节点文件。
这部分函数还会处理属性变更请求。