本文将分析 pid = 1 的 Init 进程。Init 是 Linux 系统中用户空间的第一个进程,进程号为1。Kernel 启动后,在用户空间,启动 Init 进程,并调用 Init 中的 main() 方法执行 Init 进程的职责。Init 进程从功能上来说,分成下面四个部分:
- 分析和运行所有的 init.rc 文件。
- 生成设备驱动节点 (通过 rc 文件创建)。
- 处理子进程的终止 (signal 方式)。
- 提供属性服务。
目录:
- main() 分析
- 信号处理
- rc 文件语法
- 服务启动
- 属性服务
1. main() 分析
/system/core/init/init.cpp
int main(int argc, char** argv) {
...
umask(0); //设置文件属性0777
klog_init(); //初始化kernel log,位于设备节点/dev/kmsg【见小节1.2】
klog_set_level(KLOG_NOTICE_LEVEL); //设置输出的log级别
// 输出init启动阶段的log
NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
property_init(); //创建一块共享的内存空间,用于属性服务
signal_handler_init(); //初始化子进程退出的信号处理过程【见小节2.1】
property_load_boot_defaults(); //加载default.prop文件
start_property_service(); //启动属性服务器(通过socket通信)【5.1】
init_parse_config_file("/init.rc"); //解析init.rc文件
//执行rc文件中触发器为 on early-init的语句
action_for_each_trigger("early-init", action_add_queue_tail);
//等冷插拔设备初始化完成
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
//设备组合键的初始化操作
queue_builtin_action(keychord_init_action, "keychord_init");
// 屏幕上显示Android静态Logo
queue_builtin_action(console_init_action, "console_init");
//执行rc文件中触发器为 on init的语句
action_for_each_trigger("init", action_add_queue_tail);
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
char bootmode[PROP_VALUE_MAX];
// 当处于充电模式,则charger加入执行队列;否则late-init加入队列。
if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
}
// 触发器为属性是否设置
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
while (true) {
if (!waiting_for_exec) {
execute_one_command();
restart_processes(); //【见小节1.4】
}
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action) {
timeout = 0;
}
epoll_event ev;
// 循环 等待事件发生
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
可以看到上面代码的注释,init.cpp 的 main() 做的工作包括:
- 启动 log 系统。
- 开辟共享内存空间,用于属性服务。
- 初始化信号处理相关服务。
- 解析 init.rc。
- 启动循环,监听事件。
2. 信号处理
在 init.cpp 的 main() 方法中,通过 signal_handler_init() 来初始化信号处理过程。主要工作:
- 初始化 signal 句柄。
- 循环处理子进程。
- 注册 epoll 句柄。
- 处理子进程的终止。
/system/core/init/signal_handler.cpp
void signal_handler_init() {
int s[2];
// 创建socket pair
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
exit(1);
}
signal_write_fd = s[0];
signal_read_fd = s[1];
// 当捕获信号SIGCHLD,则写入signal_write_fd
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIGCHLD_handler;
// SA_NOCLDSTOP使init进程只有在其子进程终止时才会受到SIGCHLD信号
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
reap_any_outstanding_children(); 【见小节2.2】
register_epoll_handler(signal_read_fd, handle_signal);
}
每个进程在处理其他进程发送的 signal 信号时都需要先注册,当进程的运行状态改变或终止时会产生某种 signal 信号,
Init 进程是所有用户空间进程的父进程,当其子进程终止时产生 SIGCHLD 信号,Init 进程调用信号安装函数 sigaction(),
传递参数给 sigaction 结构体,便完成信号处理的过程。
3. rc 文件语法
rc 文件语法是以行尾单位,以空格间隔的语法,以 # 开始代表注释行。rc 文件主要包含 Action、Service、Command、Options,其中对于 Action 和 Service 的名称都是唯一的,对于重复的命名视为无效。
- 3.1 Action
Action:通过 trigger,即以 on 开头的语句,决定何时执行相应的 service。
- on early-init:在初始化早期阶段触发;
- on init:在初始化阶段触发;
- on late-init:在初始化晚期阶段触发;
- on boot/charger:当系统启动/充电时触发,还包含其他情况,此处不一一列举;
- on property:<key>=<value>: 当属性值满足条件时触发;
- 3.2 Service
服务 Service,以 service 开头,由 Init 进程启动,一般运行于另外一个 Init 的子进程,所以启动 service 前需要判断对应的可执行文件是否存在。Init 生成的子进程,定义在 rc 文件,其中每一个 service,在启动时会通过 fork 方式生成子进程。
例如: service servicemanager /system/bin/servicemanager 代表的是服务名为 servicemanager,服务的路径,
也就是服务执行操作时运行 /system/bin/servicemanager。
- 3.3 Command
下面列举常用的命令:
- class_start <service_class_name>:启动属于同一个 class 的所有服务。
- start <service_name>:启动指定的服务,若已启动则跳过。
- stop <service_name>:停止正在运行的服务。
- setprop <name> <value>:设置属性值。
- mkdir <path>:创建指定目录。
- symlink <target> <sym_link>:创建连接到 <target> 的 <sym_link> 符号链接。
- write <path> <string>:向文件 path 中写入字符串。
- exec: fork 并执行,会阻塞 Init 进程直到程序完毕。
- exprot <name> <name>:设定环境变量。
- loglevel <level>:设置 log 级别。
- 3.4 Options
Options 是 Services 的可选项,与 service 配合使用:
- disabled::不随 class 自动启动,只有根据 service 名才启动。
- oneshot:service 退出后不再重启。
- user/group: 设置执行服务的用户/用户组,默认都是 root。
- class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为 default。
- onrestart:当服务重启时执行相应命令。
- socket: 创建名为 /dev/socket/<name> 的 socket。
- critical:在规定时间内该 service 不断重启,则系统会重启并进入恢复模式。
- default:意味着 disabled=false,oneshot=false,critical=false。
4. 服务启动
- 4.1 启动顺序
on early-init
on init
on late-init //挂载文件系统,启动核心服务
trigger post-fs
trigger load_system_props_action
trigger post-fs-data //挂载data
trigger load_persist_props_action
trigger firmware_mounts_complete
trigger boot
on post-fs
start logd
mount rootfs rootfs / ro remount
mount rootfs rootfs / shared rec
mount none /mnt/runtime/default /storage slave bind rec
...
on post-fs-data
start logd
start vold //启动vold
...
on boot
...
class_start core //启动core class
由on early-init -> init -> late-init -> boot。
先启动core class, 至于main class的启动是由vold.decrypt的以下4个值的设置所决定的, 该过程位于system/vold/cryptfs.c文件。
on nonencrypted
class_start main
class_start late_start
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_reset_main
class_reset main
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
- 4.2 服务启动 (Zygote)
在 init.zygote.rc 文件中,zygote 服务定义如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
通过 init_parser.cpp 完成整个 service 解析工作,此处就不详细展开讲解析过程,该过程主要是创建一个名 "zygote" 的 service结构体,一个 socketinfo 结构体 (用于 socket 通信),以及一个包含4个 onrestart 的 action 结构体。
Zygote 服务会随着 main class 的启动而启动,退出后会由 init 重启 zygote,即使多次重启也不会进入 recovery 模式。zygote 所对应的可执行文件是 /system/bin/app_process,通过调用 pid =fork() 创建子进程,通过 execve(svc->args[0], (char**)svc->args, (char**) ENV),进入 App_main.cpp 的 main() 函数。故 zygote 是通过 fork 和 execv 共同创建的。
流程如下:
- 4.3 服务重启
当 Init 子进程退出时,会产生 SIGCHLD 信号,并发送给 Init 进程,通过 socket 套接字传递数据,调用到 wait_for_one_process()方法,根据是否是 oneshot,来决定是重启子进程,还是放弃启动。
所有的 Service 里面只有 servicemanager ,zygote ,surfaceflinger 这3个服务有 onrestart 关键字来触发其他 service 启动过程。
//zygote可触发media、netd重启
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
//servicemanager可触发healthd、zygote、media、surfaceflinger、drm重启
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
//surfaceflinger可触发zygote重启
service surfaceflinger /system/bin/surfaceflinger
class core
user system
group graphics drmrpc
onrestart restart zygote
由上可知:
- zygote:触发 media、netd 以及子进程 (包括 system_server 进程) 重启。
- system_server:触发 zygote 重启。
- surfaceflinger:触发 zygote 重启。
- servicemanager: 触发 zygote、healthd、media、surfaceflinger、drm 重启。
所以,surfaceflinger, servicemanager, zygote 自身以及 system_server 进程被杀都会触发 Zygote 重启。
5. 属性服务
当某个进程 A,通过 property_set() 修改属性值后,init 进程会检查访问权限,当权限满足要求后,则更改相应的属性值,属性值一旦改变则会触发相应的触发器 (即 rc 文件中的 on 开头的语句),在 Android Shared Memmory (共享内存区域) 中有一个_system_property_area_ 区域,里面记录着所有的属性值。对于进程 A 通过 property_get() 方法,获取的也是该共享内存区域的属性值。
- 5.1 初始化
/system/core/init/property_service.cpp
void property_init() {
//用于保证只初始化_system_property_area_区域一次
if (property_area_initialized) {
return;
}
property_area_initialized = true;
if (__system_property_area_init()) {
return;
}
pa_workspace.size = 0;
pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (pa_workspace.fd == -1) {
ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
return;
}
}
在 properyty_init 函数中,先调用 init_property_area 函数,创建一块用于存储属性的共享内存,而共享内存是可以跨进程的。
- 5.2 属性类别
属性名以 ctl. 开头,则表示是控制消息,控制消息用来执行一些命令。例如:
- setprop ctl.start bootanim 查看开机动画。
- setprop ctl.stop bootanim 关闭开机动画。
- setprop ctl.start pre-recovery 进入 recovery 模式。
属性名以 ro. 开头,则表示是只读的,不能设置,所以直接返回。
属性名以 persist. 开头,则需要把这些值写到对应文件中去。
比如上一篇文章的开机动画:
/**
* 启动开机动画
*
* 通过控制ctl.start属性,设置成bootanim值,则触发init进程来创建开机动画进程bootanim,
* 到此,则开始显示开机过程的动画。
*/
void SurfaceFlinger::startBootAnim() {
// start boot animation
property_set("service.bootanim.exit", "0");
property_set("ctl.start", "bootanim");
}