Android 系统启动流程(一)解析 init 进程启动过程,安卓移动开发基础案例教程
set RLIMIT_NICE to allow priorities from 19 to -20
setrlimit 13 40 40
...
这里只截取了一部分代码,其中 #是注释符号。on init 和 on boot 是 Action 类型语句,它的格式为:
on <trigger> [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
为了分析如何创建 zygote,我们主要查看 Services 类型语句,它的格式如下所示:
service <name> <pathname> [ <argument> ]* //<service 的名字><执行程序路径><传递参数>
<option> //option 是 service 的修饰词,影响什么时候、如何启动 services
<option>
...
需要注意的是在 Android 7.0 中对 init.rc 文件进行了拆分,每个服务一个 rc 文件。我们要分析的 zygote 服务的启动脚本则在 init.zygoteXX.rc 中定义,这里拿 64 位处理器为例,init.zygote64.rc 的代码如下所示。
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -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 audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
其中 service 用于通知 init 进程创建名 zygote 的进程,这个 zygote 进程执行程序的路径为/system/bin/app_process64,后面的则是要传给 app_process64 的参数。class main 指的是 zygote 的 class name 为 main,后文会用到它。?
5、解析 service
===============
接下来我们来解析 service,会用到两个函数,一个是 ParseSection,它会解析 service 的 rc 文件,比如上文讲到的 init.zygote64.rc,ParseSection 函数主要用来搭建 service 的架子。另一个是 ParseLineSection,用于解析子项。代码如下所示。
system/core/init/service.cpp
bool ServiceParser::ParseSection(const std::vectorstd::string& args,
std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) {
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vectorstd::string str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, "default", str_args);//1
return true;
}
bool ServiceParser::ParseLineSection(const std::vectorstd::string& args,
const std::string& filename, int line,
std::string* err) const {
return service_ ? service_->HandleLine(args, err) : false;
}
注释 1 处,根据参数,构造出一个 service 对象,它的 classname 为”default”。当解析完毕时会调用 EndSection:
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::GetInstance().AddService(std::move(service_));
}
}
接着查看 AddService 做了什么:
void ServiceManager::AddService(std::unique_ptr<Service> service) {
Service* old_service = FindServiceByName(service->name());
if (old_service) {
ERROR("ignored duplicate definition of service '%s'",
service->name().c_str());
return;
}
services_.emplace_back(std::move(service));//1
}
注释 1 处的代码将 service 对象加入到 services 链表中。上面的解析过程总体来讲就是根据参数创建出 service 对象,然后根据选项域的内容填充 service 对象,最后将 service 对象加入到 vector 类型的 services 链表中。?
6、init 启动 zygote
==================
讲完了解析 service,接下来该讲 init 是如何启动 service,在这里我们主要讲解启动 zygote 这个 service。在 zygote 的启动脚本中我们得知 zygote 的 class name 为 main。在 init.rc 有如下配置代码:
system/core/rootdir/init.rc
...
on nonencrypted
A/B update verifier that marks a successful boot.
exec - root -- /system/bin/update_verifier nonencrypted
class_start main
class_start late_start
...
其中 class_start 是一个 COMMAND,对应的函数为 do_class_start。我们知道 main 指的就是 zygote,因此 class_start main 用来启动 zygote。do_class_start 函数在 builtins.cpp 中定义,如下所示。
system/core/init/builtins.cpp
static int do_class_start(const std::vectorstd::string& args) {
/* Starting a class does not start services
which are explicitly disabled. They must
be started individually.
*/
ServiceManager::GetInstance().
ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
return 0;
}
来查看 StartIfNotDisabled 做了什么:
system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
}
接着查看 Start 方法,如下所示。
bool Service::Start() {
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
time_started_ = 0;
if (flags_ & SVC_RUNNING) {//如果 Service 已经运行,则不启动
return false;
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console && !have_console) {
ERROR("service '%s' requires console\n", name_.c_str());
flags_ |= SVC_DISABLED;
return false;
}
//判断需要启动的 Service 的对应的执行文件是否存在,不存在则不启动该 Service
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
ERROR("cannot find '%s' (%s), disabling '%s'\n",
args_[0].c_str(), strerror(errno), name_.c_str());
flags_ |= SVC_DISABLED;
return false;
}
...
pid_t pid = fork();//1.fork 函数创建子进程
if (pid == 0) {//运行在子进程中
umask(077);
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
}
for (const auto& si : sockets_) {
int socket_type = ((si.type == "stream" ? SOCK_STREAM :
(si.type == "dgram" ? SOCK_DGRAM :
SOCK_SEQPACKET)));
const char* socketcon =
!si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
int s = create_socket(si.name.c_str(), socket_type, si.perm,
si.uid, si.gid, socketcon);
if (s >= 0) {
PublishSocket(si.name, s);
}
}
...
//2.通过 execve 执行程序
if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
}
_exit(127);
}
...
return true;
}
通过注释 1 和 2 的代码,我们得知在 Start 方法中调用 fork 函数来创建子进程,并在子进程中调用 execve 执行 system/bin/app_process,这样就会进入 framework/cmds/app_process/app_main.cpp 的 main 函数,如下所示。
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);//1
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
从注释 1 处的代码可以得知调用 runtime(AppRuntime)的 start 来启动 zygote。
7、属性服务
==========
Windows 平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,它还是能够根据之前在注册表中的记录,进行相应的初始化工作。Android 也提供了一个类似的机制,叫做属性服务。
在本文的开始,我们提到在 init.cpp 代码中和属性服务相关的代码有:
system/core/init/init.cpp
property_init();
start_property_service();
这两句代码用来初始化属性服务配置并启动属性服务。首先我们来学习服务配置的初始化和启动。
属性服务初始化与启动
property_init 函数具体实现的代码如下所示。
system/core/init/property_service.cpp
void property_init() {
if (__system_property_area_init()) {
ERROR("Failed to initialize property area\n");
exit(1);
}
}
__system_property_area_init 函数用来初始化属性内存区域。接下来查看 start_property_service 函数的具体代码:
void start_property_service() {
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);//1
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8);//2
register_epoll_handler(property_set_fd, handle_property_set_fd);//3
}
注释 1 处用来创建非阻塞的 socket。注释 2 处调用 listen 函数对 property_set_fd 进行监听,这样创建的 socket 就成为了 server,也就是属性服务;listen 函数的第二个参数设置 8 意味着属性服务最多可以同时为 8 个试图设置属性的用户提供服务。注释 3 处的代码将 property_set_fd 放入了 epoll 句柄中,用 epoll 来监听 property_set_fd:当 property_set_fd 中有数据到来时,init 进程将用 handle_property_set_fd 函数进行处理。
在 linux 新的内核中,epoll 用来替换 select,epoll 最大的好处在于它不会随着监听 fd 数目的增长而降低效率。因为内核中的 select 实现是采用轮询来处理的,轮询的 fd 数目越多,自然耗时越多。
** 属性服务处理请求**
从上文我们得知,属性服务接收到客户端的请求时,会调用 handle_property_set_fd 函数进行处理:
system/core/init/property_service.cpp
static void handle_property_set_fd()
{
...
if(memcmp(msg.name,"ctl.",4) == 0) {
close(s);
if (check_contro
l_mac_perms(msg.value, source_ctx, &cr)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
//检查客户端进程权限
if (check_mac_perms(msg.name, source_ctx, &cr)) {//1
property_set((char*) msg.name, (char*) msg.value);//2
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
cr.uid, msg.name);
}
close(s);
}
freecon(source_ctx);
break;
default:
close(s);
break;
}
}
注释 1 处的代码用来检查客户端进程权限,在注释 2 处则调用 property_set 函数对属性进行修改,代码如下所示。
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set("%s", "%s") failed\n", name, value);
}
return rc;
}
property_set 函数主要调用了 property_set_impl 函数:
static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1;
评论