Android源码分析 三月 11, 2017 chaossss 2 comments

Android系统进程之父-init进程分析

前言

Android是Linux子集,它的启动流程和Linux区别不大,将系统内核装载完成后会创建一个init进程(初始进程)完成后续初始化工作,再启动其他用于提供系统服务的进程,启动完这些进程后init进程将变成守护进程保证这些系统服务进程的正常运行。而init.rc就是用Android初始化语言写的一个在init进程进行初始化时解析的一个脚本。

有关Android初始化语言的解释,可以打开源码的/system/core/init/readme.txt文件阅读,init.rc文件则在/out/target/product/generic_arm64/root目录下。

本文引用的init进程相关的代码在/system/core/init目录下,有兴趣的话可以自己打开看。

init.rc

Android 初始化语言

Android初始化语言由四大类组成:Actions、Commands、Services和Options。它们都是按行解析的代码,代码中可以通过反斜杠插入空格或者连接下一行代码(以避免一行写不下)。每一行的代码由多个token组成,但我们也可以用双引号将多个被空格分割的token合成一个token。除此以外,我们可以以#开头写注释。

Android 初始化语言会将脚本分为多个section,每一个section都由action和aervice隐式声明。所有的command或option都属于最近声明的section,但声明在第一个section前的command和option都被看作无效(由于不归属于任何Section,因此解析时被忽略)。

action和service都有唯一的命名,命名重复的action或Service都会视作错误而被忽略。

Actions

action是command的命名序列,每一个action都需要有一个trigger以用于判断在何时执行action。当发生一个符合trigger条件的事件,除非action已经被添加,否则action就会被添加到待执行队列的尾部。

待执行队列中的action都将按序出列,而action中的command则会按序执行。

action的格式如下:

on <trigger>
<command>
<command>
<command>

目前可用的trigger如下:


名称 作用
boot init过程第一个触发的trigger,即在init.rc被装载后解析
<name>=<value> 当<name>被设置成<value>时触发,如:on property:vold.decrypt=trigger_reset_main
device-added-<path> 设备节点被添加时触发
device-removed-<path> 设备节点被移除时触发
service-exited-<name> service退出时触发

目前可用的command如下:


名称 作用
exec <path> [<argument> ]* 创建并执行<path>指向的程序,在程序完全执行前将阻塞init过程(尽量避免使用,因为可能导致init过程延时)
export <name> <value> 将<name>变量设为<value>。(全局生效,将被这命令之后运行的所有进程继承,即<name>变量的值为<value>
ifup <interface> 启动网络接口
import <path> 导入其他的配置文件(拓展当前配置文件)
hostname <name> 设置主机名
chdir <directory> 修改工作目录
chmod <octal-mode><path> 修改<path>对应文件的访问权限
chown <owner><group> <path> 修改<path>对应文件的所有者和组
chroot <directory> 修改程序执行时锁参考的根目录位置
class_start<serviceclass> 启动所有指定service管理的所有未启动的
class_stop<serviceclass> 设置主机名

Option

option是service的修饰符,它们决定init将在何时以何种方式运行service。

Services

Services都是程序,它们可以在init过程中被启动,也可以在init退出时重启init(可选)。service的格式如下:

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

目前可用的option如下:


名称 作用
critical 声明service为关键设备服务,如果4分钟内该服务退出次数大于等于4次,设备将重启并进入recover模式
disabled 声明service只能通过服务名显式启动,不能因为trigger被触发而隐式启动
setenv <name><value> 将环境变量<name>设置为<value>
socket <name><type> <perm> [ <user> [ <group> ] ] 创建一个名为/dev/socket/<name>的Unix域套接字,并将它的文件描述符传递给已启动的进程。其中<type>必须为”dgram”,”stream”或”seqpacket”
user <username> 在执行service前切换用户名,当前默认为root
group <groupname> [<groupname> ]* 在执行service前切换组名,除了第一个必须的组名,附加的组名用于设置进程的补充组,当前默认为root
oneshot service退出后不重启
class <name> 为当前service设定一个类别名,相同类别的服务会同时启动或停止,默认为default
priority <priority> 设置service进程的优先级(-20~19),默认值为0
onrestart service重启时执行一个command

init进程

Android作为基于Linux内核的操作系统,其启动机制是没有太大变化的,与Linux相同,它会将编译AOSP得到的zImage内核镜像文件装载到内存的内核空间运行。Android系统内核装载完成后,则创建init进程继续完成Android系统的启动。从代码上看,init进程主要完成了以下工作:

我们打开/system/core/init/init.cpp文件,进入main函数可以知道init主要完成了以下工作:

– 创建守护进程uevent和watchdog
– 初始化属性、创建用户空间目录及挂载文件系统
– 解析init.rc
– 启动其他进程,并转为守护进程通过signal处理子进程的终止
– 提供属性服务

1、创建守护进程

if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}

if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}

Uevent守护进程是内核通知Android有状态发生变化的一种方法,用于通知USB插拔、电池电量变化等状态变化信息。Uevent可以通过两种方法将内核的通知发给用户空间的程序:

1. 通过kmod模块,直接调用用户空间的可执行文件
2. 通过netlink通信机制(socket),将事件从内核空间传递给用户空间

Watchdog守护进程用于监测系统进程,系统进程往往会维护着大量提供系统服务的关键对象,这些服务的正常运行直接影响系统的运行状态,而这些关键对象可能同时被多个线程使用,因此系统需要在操作这些对象的地方给它们加上同步锁以确保对象的一致性。但锁对象往往就意味着可能出现死锁的状况,因此,为了避免系统服务对象死锁,Watchdog守护进程应运而生。

2、挂载文件系统

// 创建文件的权限默认为0777
umask(0);

// 初始化PATH环境变量,PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
add_environment("PATH", _PATH_DEFPATH);

// 根据argc,argv[1]判断是否是first_stage
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);

// 创建系统目录并挂载文件系统
if (is_first_stage) {
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();
// 初始化kerner log,位于/dev/kmsg,并设置输出的log级别
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);

NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");

if (!is_first_stage) {
// 在/dev目录下创建.booting文件表示正在初始化
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

// 创建用于属性服务的共享内存空间
property_init();

// 读取/proc/device-tree/firmware/android下的目录以得到ro.boot.*前缀的属性,并设置
process_kernel_dt();
// 读取/proc/cmdline,并设置cmdline文件中前缀androidboot.*对应的ro.boot.*的属性值
process_kernel_cmdline();

// 根据prop_map中的src_prop设置dst_prop的值
export_kernel_boot_props();
}

– 阅读system/core/init/Android.mk可知,Uevent和Watchdog都链接到init

# Create symlinks
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd

– is_first_stage用于判断init是在内核空间还是用户空间启动,true代表内核空间
– 重定向IO流是为了屏蔽它们的输出,输出将被重定向到/sys/fs/selinux/null或/dev/null)

void open_devnull_stdio(void)
{
// Try to avoid the mknod() call if we can. Since SELinux makes
// a /dev/null replacement available for free, let's use it.
int fd = open("/sys/fs/selinux/null", O_RDWR);
if (fd == -1) {
// OOPS, /sys/fs/selinux/null isn't available, likely because
// /sys/fs/selinux isn't mounted. Fall back to mknod.
static const char *name = "/dev/__null__";
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
fd = open(name, O_RDWR);
unlink(name);
}
if (fd == -1) {
exit(1);
}
}

dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) {
close(fd);
}
}

– 标记是否处于初始化过程的/dev/.booting文件会在init.rc的late-init阶段触发firmware_mounts_complete被删除

# 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
......
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
.......

4、初始化SELinux

// 在内核空间初始化SELinux并读取SELinux策略
selinux_initialize(is_first_stage);

// If we're in the kernel domain, re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (is_first_stage) {
if (restorecon("/init") == -1) {
ERROR("restorecon failed: %s\n", strerror(errno));
security_failure();
}
char* path = argv[0];
char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
if (execv(path, args) == -1) {
ERROR("execv(\"%s\") failed: %s\n", path, strerror(errno));
security_failure();
}
}

// 按file_contexts文件的策略恢复文件的安全上下文
INFO("Running restorecon...\n");
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");

5、处理子进程信号

// 创建epoll实例处理子进程退出及属性服务事件,epoll_fd对应关联/proc/1/fd下eventpoll的fd
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
ERROR("epoll_create1 failed: %s\n", strerror(errno));
exit(1);
}

// 子进程信号处理
signal_handler_init();

// 初始化default属性并启动属性服务
property_load_boot_defaults();
start_property_service();

signal_handler_init()用于:

1. 初始化signal句柄
2. 循环处理子进程
3. 注册epoll句柄
4. 处理子进程的终止

void signal_handler_init() {
// 创建一对已经连接的非阻塞的可双工通信的socket,init进程将s[0]作为输入端,s[1]作为输出端,用于线程间通信
int s[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
ERROR("socketpair failed: %s\n", strerror(errno));
exit(1);
}
signal_write_fd = s[0];
signal_read_fd = s[1];

struct sigaction act;
memset(&amp;act, 0, sizeof(act));
// 当init进程接收到SIGCHLD信号,调用此处注册的SIGCHLD_handler对signal_write_fd执行写操作
act.sa_handler = SIGCHLD_handler;
//SA_NOCLDSTOP使init进程只有在其子进程终止时才会收到SIGCHLD信号
act.sa_flags = SA_NOCLDSTOP;
// 调用信号安装函数sigaction并通过sigaction结构体配置,以完成信号处理器的安装
sigaction(SIGCHLD, &amp;act, 0);
// 处理子进程的退出以及重启服务
reap_any_outstanding_children();
将signal_read_fd和handle_signal注册到epoll
register_epoll_handler(signal_read_fd, handle_signal);
}

#define TEMP_FAILURE_RETRY(exp)      \
({                    \
decltype(exp) _rc;           \
do {                  \
_rc = (exp);             \
} while (_rc == -1 &amp;&amp; errno == EINTR); \
_rc;                  \
})

static void SIGCHLD_handler(int) {
// 重复写入“1”直到成功
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
}
}

static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));

ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}

void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = reinterpret_cast&lt;void*&gt;(fn);
//epoll_fd增加一个监听对象fd,fd上有数据到来时,调用fn处理
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &amp;ev) == -1) {
ERROR("epoll_ctl failed: %s\n", strerror(errno));
}
}

static void reap_any_outstanding_children() {
while (wait_for_one_process()) {
}
}

static bool wait_for_one_process() {
int status;
// 监听子进程,若子进程发出SIGCHIL信号则获得它的pid,waitpid采用非阻塞处理方式
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &amp;status, WNOHANG));
// 无子进程退出
if (pid == 0) {
return false;
} else if (pid == -1) {
// waitpid执行失败
ERROR("waitpid failed: %s\n", strerror(errno));
return false;
}
// 根据pid遍历init.rc生成的service_list,得到对应的service
service* svc = service_find_by_pid(pid);
std::string name;

// 没找到service,则结束本次调用,去去处理下一个进程的信号
if (!svc) {
return true;
}

// 若服务进程没有设置SVC_ONESHOT标志,或设置了SVC_RESTART标志,后面重启进程时,该服务进程可能已经存在
// 因此杀掉服务进程,并创建新的进程
if (!(svc-&gt;flags &amp; SVC_ONESHOT) || (svc-&gt;flags &amp; SVC_RESTART)) {
kill(-pid, SIGKILL);
}

// 清除该服务进程创建国的所有socket
for (socketinfo* si = svc-&gt;sockets; si; si = si-&gt;next) {
char tmp[128];
snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si-&gt;name);
unlink(tmp);
}

// 当flags为SVC_EXEC时,表示服务完全退出,则清除所有信息,并将服务从svc的slist中移除
if (svc-&gt;flags &amp; SVC_EXEC) {
waiting_for_exec = false;
list_remove(&amp;svc-&gt;slist);
free(svc-&gt;name);
free(svc);
return true;
}
svc-&gt;pid = 0;
svc-&gt;flags &amp;= (~SVC_RUNNING);

// 若服务进程有SVC_ONESHOT标志,且没有SVC_RESTART标志,表示服务不需要重启,则使其进入disabled状态
if ((svc-&gt;flags &amp; SVC_ONESHOT) &amp;&amp; !(svc-&gt;flags &amp; SVC_RESTART)) {
svc-&gt;flags |= SVC_DISABLED;
}
// 若服务进程有SVC_DISABLED标志或SVC_RESET标志,表示服务不需要自动重启
if (svc-&gt;flags &amp; (SVC_DISABLED | SVC_RESET))  {
svc-&gt;NotifyStateChange("stopped"); //设置相应的service状态为stopped
return true;
}

// 若服务进程有SVC_CRITICAL标志,且没有SVC_RESTART标志,那么当它崩溃或重启的次数超过4次,
// 系统就会自动重启并进入recovery模式
time_t now = gettime();
if ((svc-&gt;flags &amp; SVC_CRITICAL) &amp;&amp; !(svc-&gt;flags &amp; SVC_RESTART)) {
if (svc-&gt;time_crashed + CRITICAL_CRASH_WINDOW &gt;= now) {
if (++svc-&gt;nr_crashed &gt; CRITICAL_CRASH_THRESHOLD) {
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
return true;
}
} else {
svc-&gt;time_crashed = now;
svc-&gt;nr_crashed = 1;
}
}
// 为服务进程添加重启标志表示它需要重启
svc-&gt;flags &amp;= (~SVC_RESTART);
svc-&gt;flags |= SVC_RESTARTING;

// 遍历该服务在.rc文件中onrestart下所有的command并执行
struct listnode* node;
list_for_each(node, &amp;svc-&gt;onrestart.commands) {
command* cmd = node_to_item(node, struct command, clist);
cmd-&gt;func(cmd-&gt;nargs, cmd-&gt;args);
}
// 将service的状态改为restarting
svc-&gt;NotifyStateChange("restarting");
return true;
}

– 通过sigaction()函数注册信号处理器后,当有信号到来,就会调用这里注册的SIGCHLD_handler处理
– Linux中信号是一种软中断,会终止进程正在处理的操作,此外,Linux不会对信号进行排队处理,因此在信号处理期间不论收到多少信号,当前信号处理完毕后内核指挥发送一个信号给进程,也就意味着存在信号丢失的可能。因此我们注册的信号处理函数应该尽可能高效、快速,避免调用不可冲入的函数
– 当子进程终止产生SIGCHLD信号,触发信号处理器SIGCHLD_handler,使得signal_write_fd会被写入数据“1”,由于signal_write_fd和signal_read_fd是相互绑定的,signal_read_fd会收到数据,从而使epoll监听到变化,从而调用handle_signal处理子进程的终止
– init进程调用signal_handler_init()函数后的处理过程如下图:

6、解析init.rc,启动子进程

// 解析init.rc
init_parse_config_file("/init.rc");

// 将trigger为early-init的action添加到action_queue的尾部等待执行
action_for_each_trigger("early-init", action_add_queue_tail);

// 创建名(trigger)为wait_for_coldboot_done、mix_hwrng_into_linux_rng、keychord_init、console_init的action并添加到action_list、action_queue等待执行
// 冷插拔设备初始化
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");

// 将trigger为init的action添加到action_queue的尾部等待触发
action_for_each_trigger("init", action_add_queue_tail);

// 再次创建名(trigger)为mix_hwrng_into_linux_rng的action,并添加到action_list、action_queue,避免wait_for_coldboot_done对应的action执行完成时/dev/hw_random或/dev/random设备节点还没就绪
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");

// 当处于充电模式,将trigger为charger的action添加到action_queue等待执行;否则将trigger为late-init的action添加到action_queue
char bootmode[PROP_VALUE_MAX];
if (property_get("ro.bootmode", bootmode) &gt; 0 &amp;&amp; strcmp(bootmode, "charger") == 0) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
}

// 创建名(trigger)为queue_property_triggers的action并添加到action_list、action_queue等待执行
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

– action_queue中存放即将要执行(但还未执行)的action,action执行后会从action_queue中删除;action_list存放init.rc中解析出来的所有action结构体

– action_for_each_trigger会遍历action_list得到action名和trigger匹配的action,然后才会添加到action_queue

void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node;
struct action *act;
list_for_each(node, &amp;action_list) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act-&gt;name, trigger)) {
func(act);  // 匹配则回调参数传入的函数指针
}
}
}

void action_add_queue_tail(struct action *act)
{
if (list_empty(&amp;act-&gt;qlist)) {
list_add_tail(&amp;action_queue, &amp;act-&gt;qlist);
}
}

– queue_builtin_action会创建init.rc中没有的action,并将新建的action添加到action_list和action_queue中

void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
struct action *act;
struct command *cmd;

act = calloc(1, sizeof(*act));
act-&gt;name = name;
list_init(&amp;act-&gt;commands);
list_init(&amp;act-&gt;qlist);

cmd = calloc(1, sizeof(*cmd));
cmd-&gt;func = func;
cmd-&gt;args[0] = name;
list_add_tail(&amp;act-&gt;commands, &amp;cmd-&gt;clist);

list_add_tail(&amp;action_list, &amp;act-&gt;alist);
action_add_queue_tail(act);
}

7、init进程转为守护进程保证子进程正常运行

while (true) {
if (!waiting_for_exec) {
// 按序执行action中的command
execute_one_command();
// 重启退出的进程
restart_processes();
}

int timeout = -1;
// 如果有进程需要重启,则等待其重启
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout &lt; 0)
timeout = 0;
}

// 还有action需要执行则不等待
if (!action_queue_empty() || cur_action) {
timeout = 0;
}

bootchart_sample(&amp;timeout);

epoll_event ev;
// 在timeout时间内等待epoll_event事件
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &amp;ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}

Android系统进程之父-init进程分析》有2个想法

发表评论