本文共 3307 字,大约阅读时间需要 11 分钟。
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启动过程上层实现。转载地址:http://quqta.baihongyu.com/