写点什么

Android 系统启动流程(一)解析 init 进程启动过程,安卓移动开发基础案例教程

用户头像
Android架构
关注
发布于: 刚刚

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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android系统启动流程(一)解析init进程启动过程,安卓移动开发基础案例教程