3.6 init循环监听处理事件
init触发所有Action后,便进入一个无限循环,在这个无限循环里首先执行两条指令:
execute_one_command()和restart_processes()。
其中execute_one_command()已经分析过,用来启动Action和Service;restart_processes()也容易理解,就是重启这些Action和Service。此后便在init中调用了系统函数poll等待一些事件发生,代码如下:
nr = poll(ufds, fd_count, timeout);//poll返回
……//省略部分内容
for (i = 0; i < fd_count; i++) {//轮询这几个Socket
if (ufds[i].revents == POLLIN) {//可读事件
/如果可读事件发生在property fd这个Socket上,便执行handle_property_set_fd 函数/
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();//处理子进程退出信号
}
}
poll可以监听多个fd上的事件,那么它都监听了哪些fd呢?这些fd都是由ufds指定的,代码如下:
if (!property_set_fd_init && get_property_set_fd() > 0) {
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
if (!signal_fd_init && get_signal_fd() > 0) {
ufds[fd_count].fd = get_signal_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
signal_fd_init = 1;
}
if (!keychord_fd_init && get_keychord_fd() > 0) {
……
}
分别调用get_property_set_fd、get_signal_fd、get_keychord_fd监听了3个fd,其中get_property
set_fd便是属性服务里启动的那个Socket,后续处理中用于属性设置等操作;get_signal_fd用于获得子进程退出信号,后续处理中用于回收子进程资源或者重启子进程(Service)。下面以get_property_set_fd为例分析这部分内容的处理机制。
当start_property_service中创建的Socket上有可读事件发生时,init中的poll函数监控到可读事件发生,便开始执行handle_property_set_fd函数,该函数位于property_service.c中,代码如下:
void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
int res;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
/接收property_set_fd上的连接请求,accept是标准的Socket编程函数/
if ((s = accept(property_set_fd, (struct sockaddr ) &addr, &addr_size)) < 0) {
return;
}
……//省略部分内容
/recv接收请求数据/
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
……//省略部分内容
switch(msg.cmd) {
case PROP_MSG_SETPROP://属性客户端设置的消息码
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
/如果消息名以ctl.开头,则认为是control message(控制消息),调用
check_control_perms函数检查权限,如果满足权限要求则执行
handle_control_message设置消息。这部分消息其实控制的是Service
的开启和关闭/
if(memcmp(msg.name,"ctl.",4) == 0) {
close(s);
if (check_control_perms(msg.value, cr.uid, cr.gid)) {
handle_control_message((char) msg.name + 4, (char) msg.value);
} else {
……//省略部分内容
}
} else {
/其他类型消息,需要通过check_perms 检查权限,然后直接调用
property_set函数设置属性 /
if (check_perms(msg.name, cr.uid, cr.gid)) {
property_set((char) msg.name, (char) msg.value);
} else {
……//省略部分内容
}
close(s);
}
break;
default:
close(s);
break;
}
}
可见当属性服务器接收到客户端请求后,init中的poll函数监控到可读事件,这时候它会返回,然后判断是property fd这个Socket上发生了可读事件,之后便会在init的handle_property_set_fd方法中处理请求。
handle_property_set_fd函数中主要做了两部分工作:首先通过accept和recv这两个标准的Socket编程函数接受并取得客户端消息,然后根据消息类型分别调用鉴权函数和属性设置函数,设置属性或启动相应的服务。这样做的好处是避免了客户端只能通过init修改属性值,也避免了直接操作属性服务带来的安全问题。
至此init的启动过程就分析完了。
3.7 本章小结
本章详细分析了Android启动过程的底层实现。首先以Kernel启动过程为入口点,分析了start_kernel如何被调用,init如何被启动;接着将init启动过程分为四大部分,重点分析了其中三个部分:解析init.rc、触发并启动Action和Service、循环处理事件;然后围绕这三大部分详细分析了Android初始化语言,如何解析Service和Action,如何触发Action,如何启动Action和Service,如何启动属性服务,以及init如何在循环中等待处理属性系统和信号系统的事件。
第4章将接着本章的内容继续分析Android启动过程上层实现。