功能概述
init进程是Android内核启动的第一个进程,其进程号(pid)为1,是Android系统所有进程的祖先,因此它肩负着系统启动的重要责任。Android的init源代码位于system/core/init/目录下,伴随Android系统多个版本的迭代,init源代码也几经重构。
目前Android4.4源代码中,init目录编译后生成如下Android系统的三个文件,分别是
- /init
- /sbin/ueventd-->/init
- /sbin/watchdogd-->/init
在Android系统早期版本(2.2之前)只有init进程,Android2.2中将创建设备驱动节点文件功能独立到ueventd进程完成,在Android4.1中则添加了watchdogd。
- 解析init.rc初始化Android属性系统,并维护属性服务
- 初始化Android属性系统,并维护属性服务
- 处理子进程启动、停止、重启动
/ueventd用于创建设备驱动节点。
代码分析
分析代码当先抓住主干,了解其大致结构与流程,再逐块深入,分析其实现细节。这样先大局再细节的方法可以让我们在阅读代码时保持头脑的清醒,切忌不可在没有对整体流程了解的情况下深入细节,那很容易导致我们迷失在代码森林中。
接下来分析init.c的main函数。为了方便分析,将main函数代码做了精简,代码如下。
int main(int argc, char **argv)
{
//<part 1>
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
if (!strcmp(basename(argv[0]), "watchdogd"))
return watchdogd_main(argc, argv);
//<part2>
umask(0);
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
....
open_devnull_stdio();
klog_init();
property_init();
....
//<part3>
INFO("reading config file\n");
init_parse_config_file("/init.rc");
...
action_for_each_trigger("early-init", action_add_queue_tail);
....
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
//<part4>
for(;;) {
...
execute_one_command();
restart_processes();
....
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
if (ufds[i].revents & POLLIN) {
if (ufds[i].fd == get_property_set_fd())
handle_property_set_fd();
else if (ufds[i].fd == get_keychord_fd())
handle_keychord();
else if (ufds[i].fd == get_signal_fd())
handle_signal();
}
}
}
return 0;
}
将main函数分为上述4个部分,对应part1到part4,下面分别做具体说明。
代码<part1>
通过命令行判断argv[0]的字符串内容,来区分当前程序是init,ueventd或是watchdogd。C程序的main函数原型为 main(int argc, char* argv[]), ueventd以及watchdogd的启动都在init.rc中描述,由init进程解析后执行fork、exec启动,因此其入口参数的构造在init代码中,将在init.rc解析时分析。此时我们只需要直到argv[0]中将存储可执行文件的名字。
代码<part2>
umaks(0)用于设定当前进程(即/init)的文件模型创建掩码(file mode creation mask),注意这里的文件是广泛意义上的文件,包括普通文件、目录、链接文件、设备节点等。
PS. 以上解释摘自umask的mannual,可在linux系统中执行man 3 umask查看。
Linux C库中mkdir与open的函数运行如下。
int mkdir(const char *pathname, mode_t mode);
int mkdir(const char *pathname, mode_t mode);
int open(const char *pathname, int flags, mode_t mode);
Linux内核给每一个进程都设定了一个掩码,当程序调用op