问题

在进入到 Android 启动过程之前先让我们思考以下几个问题

  1. Android 系统的启动过程是怎样的?
  2. init 、zygote 进程是什么?在系统启动的过程中各自发挥了什么作用?
  3. AMS、PMS 等这些服务是如何被启动的?
  4. Launcher 是如何被启动的?

此篇文章将针对 init 部分给出分析

启动流程简述

在了解 init 部分之前,首先简单介绍下系统的启动流程以便抓住主线,当我们通过电源键开启系统的时候,系统首先会加载 bootloader 程序到 RAM 中,然后通过 bootloader 将内核程序加载到 RAM 中,之后内核程序会创建init 进程,在 init 进程中会创建 zygote 进程,而 zygote 进程则会创建 DVM 并且启动 SystemServer 进程,通过SystemServer 系统会启动一系列的服务,包括常见的 AMS、PMS 等,最后再通过 AMS 进入到我们熟知的 Launcher 程序。

所以整个流程的关键点在于 init 进程如何创建 zygote、zygote 进程如何创建 SystemServer、SystemServer 进程如何启动 AMS、AMS 如何启动 Launcher。

init 启动流程分析

由于 bootloader 和内核不是关心的重点,所以这里只是简单介绍它们的作用。

加载运行 BootLoader

在电源上电之后,CPU 中的操作控制器将发出控制信号,将程序计数器(PC)的内容送至地址寄存器(AR),之后启动对主存的读操作,最终将 BootLoader 加载到 RAM 当中。然后 BootLoader 开始执行,主要负责硬件的初始化,将内核程序加载到内存。

init 进程的启动

内核启动之后将会初始化软硬件环境,加载驱动程序,挂载根文件系统,然后创建 init 进程,init 作为系统中的第一个用户进程,其进程号为 1,在创建 init 进程之时,系统会执行位于 system/core/init 下的 init.cpp 程序

int main(int argc, char** argv) {...//创建用户空间目录并挂载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);#define MAKE_STR(x) __STRING(x)mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));mount("sysfs", "/sys", "sysfs", 0, NULL);}...signal_handler_init();property_load_boot_defaults();export_oem_lock_status();//启动属性服务start_property_service();...const BuiltinFunctionMap function_map;Action::set_function_map(&function_map);Parser& parser = Parser::GetInstance();parser.AddSectionParser("service",std::make_unique<ServiceParser>());parser.AddSectionParser("on", std::make_unique<ActionParser>());parser.AddSectionParser("import", std::make_unique<ImportParser>());//解析 init.rc 文件parser.ParseConfig("/init.rc");...return 0;
}复制代码

可以看到 init 进程主要做了三件事情:创建用户空间文件夹并挂载、启动属性服务、解析位于 system\core\rootdir 文件夹下的 init.rc 文件

这里我们主要关注解析 in it.rc 文件的过程,因为 zygote 进程就是在这个过程中创建的。

Android Init Language

由于 init.rc 是一个用 Android 初始化语言(AIL)编写的文件,为了更好的理解 rc 文件的解析过程需要了解一部分 AIL 语法。

AIL 主要有五种类型语句 Actions、Commands、Services、Options、Imports,在 AIL 中每个部分表示为一个 Section,比如:

on bootifup lohostname localhostdomainname localdomain复制代码

五种语句中只有 Actions、Services、Import 可以用于确定一个 Section。其中 Actions 由一系列 command 组成,Actions 拥有一个 trigger 用于确定何时执行这些命令。

on <trigger><command><command><command>on early-init //触发器 early-initwrite /proc/1/oom_score_adj -1000 //commandwrite /proc/sys/kernel/sysrq 0复制代码

Services 由一些 option 组成,其在初始化的时候启动,并可以在退出后重启(可选)。

service <name> <pathname> [ <argument> ]*  //服务名、执行路径、参数<option>  <option> service ueventd /sbin/ueventd class corecriticalseclabel u:r:ueventd:s0复制代码

Services 定义了自身的服务名、执行路径以及执行时传入的参数,option 用于指定何时和怎样启动 service,关于何时启动这里进行一下说明,Actions 中有一条命令是 class_start <服务类别名> 用于启动所有未运行的相同类别的 service,而 option 可以通过 class <类别名> 对 service 的类别名进行指定。所以 service 的启动一般是通过 action 触发之后执行 class_start 命令进行启动的。

AIL 的介绍就到这了,如果想要详细了解请阅读 system/core/init 下的 readme.txt 文件

init.rc 解析

现在接着分析 init.rc 文件,在文件的首部可以看到

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc复制代码

在这里可以看到需要启动的 zygote,但是与其它引入的 rc 文件相比 zygote 部分并没有使用确定的值,而是使用 ${ro.zygote} 变量去替代,这是因为从 Android 在 5.0 以后开始支持 64 位程序,所以需要根据系统中 ro.zygote 属性的值动态引入。ro.zygote 的值可以通过 adb shell getprop 进行查询,我的手机查询结果是:

这说明手机会启动两个 zygote 进程,对应的执行程序分别是 app_process64 (主模式)、app_process32,通过 adb shell ps | grep zygote 可以看到确实存在两个 zygote 进程

接着查看与 init.rc 同一目录下的 init.zygote64_32.rc 文件

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygoteclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdwritepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasksservice zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondaryclass mainsocket zygote_secondary stream 660 root systemonrestart restart zygotewritepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks复制代码

前文说过 service 的启动和类别名相关,这里两个 zygote service 的类别名都是 main,所以要想知道 zygote 怎么被启动的我们可以在 init.rc 中搜索 class_start main,可以发现

on nonencrypted# A/B update verifier that marks a successful boot.exec - root -- /system/bin/update_verifier nonencryptedclass_start mainclass_start late_start复制代码

从这里我们可以了解到当 nonencrypted 这个触发器被触发的时候 zygote 就会被启动,所以启动 zygote 的问题就转变为这个触发器什么时候执行?class_start 对应的处理函数是什么?要想知道答案,必须回到 init.cpp 的解析过程当中

Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
parser.ParseConfig("/init.rc");复制代码

init.rc 交由一个 Parser 对象进行解析,而 Parser 的实现在 system/core/init/init_parser.cpp 文件中,让我们进入到 init_parser.cpp 中查看 AddSectionParser 的实现

void Parser::AddSectionParser(const std::string& name,std::unique_ptr<SectionParser> parser) {section_parsers_[name] = std::move(parser);
}init_parser.h 中 section_parsers_ 的定义
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;复制代码

可以看到每一个 parser 最终被保存在 section_parsers_ 中,section_parsers_ 是什么?查看 init_parser.h 中的定义可以知道,sectionparsers 是一个 map 集合,所以 section_parsers_ 的作用是将 parser 与对应的 Section 进行绑定。

在添加完所有的 parser 之后就会调用 Parser 的 ParseConfig 方法

bool Parser::ParseConfig(const std::string& path) {if (is_dir(path.c_str())) {return ParseConfigDir(path);}return ParseConfigFile(path);
}复制代码

ParseConfig 中会对 path 进行判断,如果是目录则调用 ParseConfigDir 进行递归然后再通过 ParseConfigFile 进行解析。

bool Parser::ParseConfigDir(const std::string& path) {INFO("Parsing directory %s...\n", path.c_str());std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);if (!config_dir) {ERROR("Could not import directory '%s'\n", path.c_str());return false;}dirent* current_file;while ((current_file = readdir(config_dir.get()))) {std::string current_path =android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);// Ignore directories and only process regular files.if (current_file->d_type == DT_REG) {if (!ParseConfigFile(current_path)) { //调用 ParseConfigFile 进行解析ERROR("could not import file '%s'\n", current_path.c_str());}}}return true;
}复制代码

最终在 ParseConfigFile 中通过 ParseData 进行解析

bool Parser::ParseConfigFile(const std::string& path) {INFO("Parsing file %s...\n", path.c_str());Timer t;std::string data;//从 rc 文件中读取内容保存在 data 中if (!read_file(path.c_str(), &data)) {return false;}data.push_back('\n'); // TODO: fix parse_config.ParseData(path, data); //调用 ParseData 进行解析...return true;
}复制代码

ParseData 解析过程如下

void Parser::ParseData(const std::string& filename, const std::string& data) {//TODO: Use a parser with const input and remove this copystd::vector<char> data_copy(data.begin(), data.end()); //将 rc 中的内容保存在 vector 中便于逐个字符进行解析data_copy.push_back('\0');parse_state state;state.filename = filename.c_str();state.line = 0;state.ptr = &data_copy[0];state.nexttoken = 0;SectionParser* section_parser = nullptr;std::vector<std::string> args; //存放的是每行的内容for (;;) {switch (next_token(&state)) {case T_EOF:if (section_parser) {section_parser->EndSection();}return;case T_NEWLINE:state.line++;//如果 args 为空则不进行解析(rc 文件中间存在空行,所以需要判断)if (args.empty()) {break;}//判断是不是一个 section 的起始位置(通过能不能获得解析器,可以判断 args[0] 是不是 service、on、import 其中一个)if (section_parsers_.count(args[0])) {if (section_parser) {//如果上次存在解析则结束解析section_parser->EndSection();}//取出对应的解析器section_parser = section_parsers_[args[0]].get();std::string ret_err;//进行 Section 解析if (!section_parser->ParseSection(args, &ret_err)) {parse_error(&state, "%s\n", ret_err.c_str());section_parser = nullptr;}} else if (section_parser) { //不是的话说明 args 中是一个 section 的子块,则进行 Line 解析std::string ret_err;if (!section_parser->ParseLineSection(args, state.filename,state.line, &ret_err)) {parse_error(&state, "%s\n", ret_err.c_str());}}//解析完成后清空args.clear();break;case T_TEXT:args.emplace_back(state.text);break;}}
}复制代码

ParseData 中通过调用 system/core/init/parser.cpp 中的 next_token 函数对 rc 内容进行分析,如果是 T_TEXT 则会保存在 args 中,如果是 T_NEWLINE 则会交由对应的解析器进行解析。init.rc 的大致解析过程如此,但是到这里我们依旧没能找到所需要的答案,所以需要继续查看 ActionParser 和 ServiceParser 的解析过程。

ActionParser 解析过程

ActionParser 位于 system/core/init/ 的 action.cpp 中,从前面的解析过程来看,最后的解析总是调用了对应 parser 的 ParseSection、ParseLineSection 以及 EndSection,所以我们主要看下这两个部分

ParseSection 的主要工作是创建 Action 对象,为对象添加触发器,并将 action_ 移动至当前 Action 对象

bool ActionParser::ParseSection(const std::vector<std::string>& args,std::string* err) {std::vector<std::string> triggers(args.begin() + 1, args.end());...auto action = std::make_unique<Action>(false);//为 action 增加触发器if (!action->InitTriggers(triggers, err)) {return false;}//将 aciton_ 指针移动到当前的 actionaction_ = std::move(action);return true;
}//action_ 在 action.h 中的定义
private:std::unique_ptr<Action> action_;复制代码

ParseLineSection 的主要工作为查找对应 command 的处理函数,将创建的 Command 对象添加到到commands_,由于 commands_ 是 Action 的一个域,所以实际上 ParseLineSection 在填充当前 Action 对象的域。

bool ActionParser::ParseLineSection(const std::vector<std::string>& args,const std::string& filename, int line,std::string* err) const {//将解析的 command 增加到当前 action 的 commands_ 中return action_ ? action_->AddCommand(args, filename, line, err) : false;
}bool Action::AddCommand(const std::vector<std::string>& args,const std::string& filename, int line, std::string* err) {...//查找对应的 command 的处理函数auto function = function_map_->FindFunction(args[0], args.size() - 1, err);if (!function) {return false;}AddCommand(function, args, filename, line);return true;
}void Action::AddCommand(BuiltinFunction f,const std::vector<std::string>& args,const std::string& filename, int line) {//commands_ 增加 command 和对应的处理函数commands_.emplace_back(f, args, filename, line);
}//commands_ 在 action.h 中的定义
std::vector<Command> commands_;//Command 在 action.h 中的定义
class Command {public:Command(BuiltinFunction f, const std::vector<std::string>& args,const std::string& filename, int line);int InvokeFunc() const;std::string BuildCommandString() const;std::string BuildSourceString() const;private:BuiltinFunction func_;std::vector<std::string> args_;std::string filename_;int line_;
};复制代码

EndSection 的主要工作是将解析完成的 action (域填充完毕的 Action 对象)添加到 ActionManager 的 acitons_ 中

void ActionParser::EndSection() {if (action_ && action_->NumCommands() > 0) {ActionManager::GetInstance().AddAction(std::move(action_));}
}void ActionManager::AddAction(std::unique_ptr<Action> action) {...if (old_action_it != actions_.end()) {(*old_action_it)->CombineAction(*action);} else {//将解析之后的 action 对象增加到 actions_ 链表中,用于遍历执行。actions_.emplace_back(std::move(action));}
}//ActionManager 在 action.h 中的定义
class ActionManager {public:static ActionManager& GetInstance();void AddAction(std::unique_ptr<Action> action);void QueueEventTrigger(const std::string& trigger);void QueuePropertyTrigger(const std::string& name, const std::string& value);void QueueAllPropertyTriggers();void QueueBuiltinAction(BuiltinFunction func, const std::string& name);void ExecuteOneCommand();bool HasMoreCommands() const;void DumpState() const;private:ActionManager();ActionManager(ActionManager const&) = delete;void operator=(ActionManager const&) = delete;std::vector<std::unique_ptr<Action>> actions_; //actions_ 的定义std::queue<std::unique_ptr<Trigger>> trigger_queue_;std::queue<const Action*> current_executing_actions_;std::size_t current_command_;
};复制代码

通过以上分析我们能够知道 class_start 的处理函数和 function_map_ 相关,查看 function_map_ 在 action.h 中的定义可以看到

static const KeywordMap<BuiltinFunction>* function_map_;static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {function_map_ = function_map;
}复制代码

所以要想知道处理函数是什么只需要知道 set_function_map 在哪里调用?让我们再次回到 init.cpp 中,可以看到

const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);复制代码

接下来需要找到 BuiltinFunctionMap,BuiltinFunctionMap 的实现我们可以在 builtins.cpp 中找到,其具体的实现

BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();static const Map builtin_functions = {...{"class_start",             {1,     1,    do_class_start}},{"class_stop",              {1,     1,    do_class_stop}},...};return builtin_functions;
}复制代码

到这里就可以确定 class_start 对应的处理函数是 do_class_start,do_class_start 也可在 builtins.cpp 中找到。

简单总结下解析 Action 的过程,实际上是创建一个 Action 对象,然后为 Action 对象添加 Trigger 以及对应的 Command,其中在添加 Command 的过程中还为 Command 指定了处理函数,最后在将 Action 对象增加到 ActionManager vector 类型的 actions_ 链表当中去。

ServiceParser 解析过程

与前文一致,接着看那三个函数

ParseSection 的主要工作是创建 Service 对象,将 service_ 移动至当前 Service 对象

bool ServiceParser::ParseSection(const std::vector<std::string>& args,std::string* err) {...//获取服务名const std::string& name = args[1];...//保存服务名外的参数(如执行路径等)std::vector<std::string> str_args(args.begin() + 2, args.end());//将 service_ 指针指向当前 Service 对象service_ = std::make_unique<Service>(name, "default", str_args);return true;
}//service_ 在 service.h 中的定义
std::unique_ptr<Service> service_;复制代码

ParseLineSection 的主要工作是为 Service 中每个 option 指定处理函数

bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,const std::string& filename, int line,std::string* err) const {//为 Service 中的每一个 Option 指定处理函数return service_ ? service_->HandleLine(args, err) : false;
}bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {...static const OptionHandlerMap handler_map;//寻找对应 option 的处理函数auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);...return (this->*handler)(args, err);
}复制代码

EndSection 的主要工作是将解析完成的 service (域填充完毕的 Service 对象)添加到 ServiceManager 的 services_ 中

void ServiceParser::EndSection() {if (service_) {ServiceManager::GetInstance().AddService(std::move(service_));}
}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;}//将解析之后 service 对象增加到 services_ 链表中services_.emplace_back(std::move(service));
}//ServiceManager 在 service.h 中的定义
class ServiceManager {
public:static ServiceManager& GetInstance();void AddService(std::unique_ptr<Service> service);Service* MakeExecOneshotService(const std::vector<std::string>& args);Service* FindServiceByName(const std::string& name) const;Service* FindServiceByPid(pid_t pid) const;Service* FindServiceByKeychord(int keychord_id) const;void ForEachService(std::function<void(Service*)> callback) const;void ForEachServiceInClass(const std::string& classname,void (*func)(Service* svc)) const;void ForEachServiceWithFlags(unsigned matchflags,void (*func)(Service* svc)) const;void ReapAnyOutstandingChildren();void RemoveService(const Service& svc);void DumpState() const;private:ServiceManager();bool ReapOneProcess();static int exec_count_; // Every service needs a unique name.std::vector<std::unique_ptr<Service>> services_; //services_ 的定义
};复制代码

对应的可以看下 option 的处理函数,虽然 OptionHandlerMap 与启动 zygote 无关,但是还是看下

Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();static const Map option_handlers = {{"class",       {1,     1,    &Service::HandleClass}},{"console",     {0,     0,    &Service::HandleConsole}},{"critical",    {0,     0,    &Service::HandleCritical}},{"disabled",    {0,     0,    &Service::HandleDisabled}},{"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},{"ioprio",      {2,     2,    &Service::HandleIoprio}},{"keycodes",    {1,     kMax, &Service::HandleKeycodes}},{"oneshot",     {0,     0,    &Service::HandleOneshot}},{"onrestart",   {1,     kMax, &Service::HandleOnrestart}},{"seclabel",    {1,     1,    &Service::HandleSeclabel}},{"setenv",      {2,     2,    &Service::HandleSetenv}},{"socket",      {3,     6,    &Service::HandleSocket}},{"user",        {1,     1,    &Service::HandleUser}},{"writepid",    {1,     kMax, &Service::HandleWritepid}},};return option_handlers;
}复制代码

与解析 Action 类似,在整个过程中先创建 Service 对象,解析出 Service 的名字和对应的参数添加到对象当中,并且给每个 Option 指定了相应的处理函数

到这里 rc 文件的解析就结束了,通过 rc 文件的解析使得每个 action 都有了对应的执行函数,所以接下来的问题是这些 action 是如何被触发的(也即是 command 命令是如何被执行的)?

Actions 的触发

让我们继续回到 init.cpp 中

ActionManager& am = ActionManager::GetInstance();
....while (true) {if (!waiting_for_exec) {am.ExecuteOneCommand();restart_processes();}
...
}复制代码

可以看到 action 的 command 的执行是通过 ActionManager 的 ExecuteOneCommand 函数,而ActionManager 的 ExecuteOneCommand 最终调用了 Action 的 ExecuteOneCommand

void ActionManager::ExecuteOneCommand() {while (current_executing_actions_.empty() && !trigger_queue_.empty()) {//遍历 actions_for (const auto& action : actions_) {if (trigger_queue_.front()->CheckTriggers(*action)) {//将 action 加入到 current_executing_actions_ 中current_executing_actions_.emplace(action.get());}}trigger_queue_.pop();}...//每次只执行一个 action,下次 init 进程 while 循环时,跳过上面的 while 循环,接着执行auto action = current_executing_actions_.front();if (current_command_ == 0) {std::string trigger_name = action->BuildTriggersString();INFO("processing action (%s)\n", trigger_name.c_str());}//执行 action 的 commandaction->ExecuteOneCommand(current_command_);++current_command_;...
}void Action::ExecuteOneCommand(std::size_t command) const {//执行 action 对象中保存的 commandExecuteCommand(commands_[command]);
}void Action::ExecuteCommand(const Command& command) const {Timer t;//调用 command 对应的处理函数int result = command.InvokeFunc();...
}复制代码

通过上述分析可以知道,init 进程最终进入到无限循环中,然后按照 ActionManager 中 actions_ 保存的 action 顺序依次对每个 Action 进行处理,而在这个过程中 system/core/init/builtins.cpp 下用于启动 zygote 的 do_class_start 函数将会被执行

static int do_class_start(const std::vector<std::string>& args) {ServiceManager::GetInstance().ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });return 0;
}复制代码

do_class_start 函数中调用 Service 的 StartIfNotDisabled,StartIfNotDisabled 在 service.cpp 的实现如下

bool Service::StartIfNotDisabled() {if (!(flags_ & SVC_DISABLED)) {return Start();} else {flags_ |= SVC_DISABLED_START;}return true;
}复制代码

StartIfNotDisabled 最终调用了 Service 的 Start 函数,Start 函数创建了 zygote 的进程,并且执行了 init.zygote64_32.rc 中定义的执行路径下的文件

bool Service::Start() {....//创建子进程pid_t pid = fork();if (pid == 0) {...//执行对应 service 对应的执行文件,args_[0].c_str() 就是执行路径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;
}复制代码

在完成这一切之后将会进入 frameworks/base/cmds/app_process/app_main.cpp 中

int main(int argc, char* const argv[])
{....if (zygote) {//启动 zygoteruntime.start("com.android.internal.os.ZygoteInit", args, zygote);} 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;}
}复制代码

可以看到 zygote 最终在 app_main 的 main 函数中被启动。

总结

到这里我们就能够回答对 init 部分相关的问题了

  1. init 的作用

    init 是系统中的第一个用户进程,它的主要作用是创建用户空间文件夹并挂载、启动属性服务、解析 init.rc 文件并启动 zygote 进程

  2. init 启动 zygote 的过程

    init 进程通过解析 init.rc 文件将 action 保存在 ActionManager 的 actions_ 链表中,然后通过遍历 actions_ 链表,执行 action 命令对应的处理函数,从而转至 builtins.cpp 的 do_class_start 函数,之后通过 Service 的 StartIfNotDisabled 调用 Service 的 Start 函数,最终通过 Start 函数创建 zygote 进程,执行对应的 app_main.cpp 文件启动 zygote。

Thanks

  1. Android7.0 init进程源码分析
  2. Android Init Language(android初始化语言)
  3. Android系统启动流程(一)解析init进程启动过程

转载于:https://juejin.im/post/59f4592e51882529405991cb

Android 启动过程简析(一)之 init 进程相关推荐

  1. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  2. android uboot启动过程,Android启动流程简析(一)

    最近一时兴起,想对Android的启动流程进行一次分析,经过一番整理,从以下几个方面进行总结,代码部分只讨论思路,不论细节. Android架构介绍 Android启动概述 BootLoader介绍 ...

  3. Android开机启动流程简析

    Android开机启动流程简析 (一) 文章目录 Android开机启动流程简析 (一) 前言 一.开机启动的流程概述 二.Android的启动过程分析 (1).总体流程 init简述 Zygote简 ...

  4. Linux的启动流程简析(以Debian为例)

    Linux的启动流程简析(以Debian为例) 正文: 前面的文章探讨BIOS和主引导记录的作用.那篇文章不涉及操作系统,只与主板的板载程序有关.今天,我想接着往下写,探讨操作系统接管硬件以后发生的事 ...

  5. Android启动过程深入解析【转】

    转自:http://www.open-open.com/lib/view/open1403250347934.html 当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么 ...

  6. Android启动过程深入解析

    当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么样的? 什么是Linux内核? 桌面系统linux内核与Android系统linux内核有什么区别? 什么是引导装载程序 ...

  7. Android启动过程以及各个镜像的关系

    Android启动过程 Android在启动的时候,会由UBOOT传入一个init参数,这个init参数指定了开机的时候第一个运行的程序,默认就是init程序,这个程序在ramdisk.img中.可以 ...

  8. Nutch学习笔记二——抓取过程简析

    在上篇学习笔记中http://www.cnblogs.com/huligong1234/p/3464371.html 主要记录Nutch安装及简单运行的过程. 笔记中 通过配置抓取地址http://b ...

  9. Android启动过程中执行一个C语言的应用程序或者执行shell脚本

    Android启动过程中执行一个C语言的应用程序或者执行shell脚本 一.Android启动过程中去对系统做一些设置,如修改某些文件夹的属性和文件的权限,可在init.rc中增加service执行s ...

最新文章

  1. JS仿淘宝关闭二维码案例
  2. web渗透漏洞实例讲解视频课程
  3. linux7添加两个网关,RHEL7设置IP地址、网关和DNS
  4. 雅虎欲出售核心业务 忠诚用户何去何从
  5. epoll模型与select模型的区别(宿管大妈的例子)
  6. java 回调函数实现_Java实现的回调函数
  7. MySQL编码转换防止SQL注入_防止SQL注入和XSS注入的方法总结
  8. HCP数据的下载,以及头动文件和CSF文件的所在位置
  9. 关于站内搜索的那些事儿
  10. C#时间格式化显示AM/PM
  11. android 圆形渐变背景,android实现圆形渐变进度条
  12. 蓝牙耳机哪个品牌最好?2023最新学生平价蓝牙耳机推荐
  13. 网络安全初创企业Illumio融资1.25亿美元 摩根领投
  14. 最火后台管理系统 RuoYi 项目探秘,之二
  15. 电脑下方的任务栏点不动了怎么办???
  16. VII html+css
  17. 《全球数字经济白皮书》发布:产业数字化是数字经济发展的主引擎
  18. QT在线简体中文文档
  19. SAP工具箱 行表转列表的通用类
  20. Response to preflight request doesn‘t pass access control check: No ‘Access-Control-Allow-Origin‘ he

热门文章

  1. RHEL5下构建Snort网络***检测系统
  2. 应用程序架构指导袖珍版
  3. CSS 再学习,基础篇
  4. 不得不学的http协议
  5. Android多媒体整体架构图
  6. linux最小化原则
  7. 第四课-Log的使用
  8. poj 3461 Oulipo (KMP)
  9. 360容器平台监控实践
  10. Docker 深入理解概念