PC启动一般会通过BIOS或者EFI引导程序启动,Android一般作为移动设备,没有PC的BIOS或者EFI,取而代之的是BootLoader。
按下电源键CPU上电完成后,会从固定地址加载一段程序,就是BootLoader,不通的CPU可能地址段会有差异,BootLoader是一段引导程序,常见的就是U-boot。
U-boot程序启动后,一般会先检测是否同时按下了触发U-boot功能的按键,比如进入Recovery模式等, 这些按键一般是在U-boot中事先编码好的。
如果没有按下这些按键,U-boot会从nand flash中装载linux内核,装载的地址也是在编译uboot时预定好的。linux内核启动后,会创建第一个进程:idle。
idle进程是linux进程启动后创建的第一个进程,其pid=0,idle进程被创建后会创建init进程和kthreadd进程,然后idle会在cpu没有任务时进行空转。
kthreadd被创建后,其pid=2,运行在内核空间,负责所有内核线程的调度和管理。
init进程是linux系统的第一个用户进程,其pid=1,是系统所有用户进程的根进程,其源码在/system/core/init/main.cpp中,以main方法为入口:
- using namespace android::init;
-
- int main(int argc, char** argv) {
- #if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
- #endif
- // Boost prio which will be restored later
- setpriority(PRIO_PROCESS, 0, -20);
- if (!strcmp(basename(argv[0]), "ueventd")) {
- return ueventd_main(argc, argv);
- }
-
- if (argc > 1) {
- if (!strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
-
- return SubcontextMain(argc, argv, &function_map);
- }
-
- if (!strcmp(argv[1], "selinux_setup")) {
- return SetupSelinux(argv);
- }
-
- if (!strcmp(argv[1], "second_stage")) {
- return SecondStageMain(argc, argv);
- }
- }
-
- return FirstStageMain(argc, argv);
- }
默认启动无参数,会进入init的第一阶段,会调用FirstStageMain函数,通过命令搜索FirstStageMain函数所在文件:
- //输入以下搜索命令,然后点击Enter
- grep FirstStageMain ./ -rn
搜索结果如下:
- ./first_stage_main.cpp:20: return android::init::FirstStageMain(argc, argv);
- ./first_stage_init.h:22:int FirstStageMain(int argc, char** argv);
- ./main.cpp:78: return FirstStageMain(argc, argv);
- ./first_stage_init.cpp:183:int FirstStageMain(int argc, char** argv) {
可以知道是在./first_stage_init.cpp第183行,输入如下命令定位到函数所在行
vi ./first_stage_init.cpp +183
FirstStageMain函数中可以看到,会先对文件系统进行一系列的权限设定,然后通过执行execv再次调用init,传入参数selinux_setup,进入到init的第二阶段
- int FirstStageMain(int argc, char** argv) {
- ...
-
- // Clear the umask.
- //权限掩码设置为0,则都是111,其他授权时最终的规则就是:(xxx & ~掩码 = 最终权限)
- umask(0);
-
- //文件系统授权
- CHECKCALL(clearenv());
- CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
- // Get the basic filesystem setup we need put together in the initramdisk
- // on / and then we'll let the rc file figure out the rest.
- CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
- CHECKCALL(mkdir("/dev/pts", 0755));
- CHECKCALL(mkdir("/dev/socket", 0755));
- CHECKCALL(mkdir("/dev/dm-user", 0755));
- CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
- #define MAKE_STR(x) __STRING(x)
- CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
- #undef MAKE_STR
- // Don't expose the raw commandline to unprivileged processes.
- CHECKCALL(chmod("/proc/cmdline", 0440));
- std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
- // Don't expose the raw bootconfig to unprivileged processes.
- chmod("/proc/bootconfig", 0440);
- std::string bootconfig;
- android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
- gid_t groups[] = {AID_READPROC};
- CHECKCALL(setgroups(arraysize(groups), groups));
- CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
- CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
-
- ...
-
- //通过execv再次执行init,传入参数selinux_setup,进入到init的第二阶段
- const char* path = "/system/bin/init";
- const char* args[] = {path, "selinux_setup", nullptr};
- auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
- execv(path, const_cast<char**>(args));
- }
再次回看/system/core/init/main.cpp源码,由于本次传入的参数是selinux_setup,结合代码可以看到,会进入到SetupSelinux这个函数中
- using namespace android::init;
-
- int main(int argc, char** argv) {
- ...
- if (argc > 1) {
- if (!strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
-
- return SubcontextMain(argc, argv, &function_map);
- }
-
- if (!strcmp(argv[1], "selinux_setup")) {
- return SetupSelinux(argv);
- }
-
- if (!strcmp(argv[1], "second_stage")) {
- return SecondStageMain(argc, argv);
- }
- }
- ...
- }
通过命令搜索
grep SetupSelinux ./ -rn
搜索结果如下
- ./selinux.cpp:730:int SetupSelinux(char** argv) {
- ./main.cpp:70: return SetupSelinux(argv);
- ./selinux.h:23:int SetupSelinux(char** argv);
使用命令定位到行
vi ./selinux.cpp +730
代码如下,此阶段主要是启动Selinux安全机制,初始化selinux,加载SELinux规则和配置SELinux相关log输出,然后通过执行execv再次调用init,传入参数selinux_setup,进入到init的第三阶段
- int SetupSelinux(char** argv) {
- SetStdioToDevNull(argv);
- InitKernelLogging(argv);
- if (REBOOT_BOOTLOADER_ON_PANIC) {
- InstallRebootSignalHandlers();
- }
- boot_clock::time_point start_time = boot_clock::now();
- MountMissingSystemPartitions();
- SelinuxSetupKernelLogging();
- LOG(INFO) << "Opening SELinux policy";
- ...
- LoadSelinuxPolicy(policy);
- if (snapuserd_helper) {
- snapuserd_helper->FinishTransition();
- snapuserd_helper = nullptr;
- }
- SelinuxSetEnforcement();
- setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
-
- //通过execv再次执行init,传入参数second_stage,进入到init的第三阶段
- const char* path = "/system/bin/init";
- const char* args[] = {path, "second_stage", nullptr};
- execv(path, const_cast<char**>(args));
- ...
- return 1;
- }
再次回看/system/core/init/main.cpp源码,本次传入的参数是second_stage,结合代码可以看到,会进入到SecondStageMain这个函数中,同样的方式,搜索
grep SecondStageMain ./ -rn
搜索结果
- ./init.h:47:int SecondStageMain(int argc, char** argv);
- ./main.cpp:74: return SecondStageMain(argc, argv);
- ./init.cpp:742:int SecondStageMain(int argc, char** argv) {
使用命令定位到行
vi ./init.cpp +742
代码如下,此阶段主要是初始化了一些属性和解析init.rc,以及守护服务
- int SecondStageMain(int argc, char** argv) {
- ...
- // Indicate that booting is in progress to background fw loaders, etc.
- //设置启动中标记
- close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- ...
- //初始化系统属性
- PropertyInit();
- ...
- // Mount extra filesystems required during second stage init
- //挂载额外的文件系统
- MountExtraFilesystems();
- // Now set up SELinux for second stage.
- //设置SElinux相关
- SelinuxSetupKernelLogging();
- SelabelInitialize();
- SelinuxRestoreContext();
- //使用epoll注册信号处理函数,守护进程服务
- Epoll epoll;
- if (auto result = epoll.Open(); !result.ok()) {
- PLOG(FATAL) << result.error();
- }
- InstallSignalFdHandler(&epoll);
- InstallInitNotifier(&epoll);
- //启动系统属性服务
- StartPropertyService(&property_fd);
- ...
- //设置commands指令对应的函数map,用于在后面解析init.rc文件中的指令查找
- const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
- Action::set_function_map(&function_map);
- ...
- //解析init.rc脚本
- ActionManager& am = ActionManager::GetInstance();
- ServiceList& sm = ServiceList::GetInstance();
- LoadBootScripts(am, sm);
- ...
- while (true) {
- // By default, sleep until something happens.
- auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
- ...
- //执行从init.rc脚本解析出来的每条指令
- if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
- am.ExecuteOneCommand();
- }
- ...
- if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
- // If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout = 0ms;
- }
- auto pending_functions = epoll.Wait(epoll_timeout);
- if (!pending_functions.ok()) {
- LOG(ERROR) << pending_functions.error();
- } else if (!pending_functions->empty()) {
- // We always reap children before responding to the other pending functions. This is to
- // prevent a race where other daemons see that a service has exited and ask init to
- // start it again via ctl.start before init has reaped it.
- //子进程退出后的处理
- ReapAnyOutstandingChildren();
- for (const auto& function : *pending_functions) {
- (*function)();
- }
- }
- ...
- }
- return 0;
- }
其他的可先不做分析,重点分析一下两点:
1.先看一下GetBuiltinFunctionMap函数做了什么,源码在/system/core/init/builtins.cpp,这个函数保存了指令与函数的对应关系,当发起key的调用时,会调用value对应的函数。
-
- // Builtin-function-map start
- const BuiltinFunctionMap& GetBuiltinFunctionMap() {
- constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
- // clang-format off
- static const BuiltinFunctionMap builtin_functions = {
- {"bootchart", {1, 1, {false, do_bootchart}}},
- {"chmod", {2, 2, {true, do_chmod}}},
- {"chown", {2, 3, {true, do_chown}}},
- {"class_reset", {1, 1, {false, do_class_reset}}},
- {"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},
- {"class_restart", {1, 1, {false, do_class_restart}}},
- {"class_start", {1, 1, {false, do_class_start}}},
- {"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
- {"class_stop", {1, 1, {false, do_class_stop}}},
- {"copy", {2, 2, {true, do_copy}}},
- {"copy_per_line", {2, 2, {true, do_copy_per_line}}},
- {"domainname", {1, 1, {true, do_domainname}}},
- {"enable", {1, 1, {false, do_enable}}},
- {"exec", {1, kMax, {false, do_exec}}},
- {"exec_background", {1, kMax, {false, do_exec_background}}},
- {"exec_start", {1, 1, {false, do_exec_start}}},
- {"export", {2, 2, {false, do_export}}},
- {"hostname", {1, 1, {true, do_hostname}}},
- {"ifup", {1, 1, {true, do_ifup}}},
- {"init_user0", {0, 0, {false, do_init_user0}}},
- {"insmod", {1, kMax, {true, do_insmod}}},
- {"installkey", {1, 1, {false, do_installkey}}},
- {"interface_restart", {1, 1, {false, do_interface_restart}}},
- {"interface_start", {1, 1, {false, do_interface_start}}},
- {"interface_stop", {1, 1, {false, do_interface_stop}}},
- {"load_exports", {1, 1, {false, do_load_exports}}},
- {"load_persist_props", {0, 0, {false, do_load_persist_props}}},
- {"load_system_props", {0, 0, {false, do_load_system_props}}},
- {"loglevel", {1, 1, {false, do_loglevel}}},
- {"mark_post_data", {0, 0, {false, do_mark_post_data}}},
- {"mkdir", {1, 6, {true, do_mkdir}}},
- // TODO: Do mount operations in vendor_init.
- // mount_all is currently too complex to run in vendor_init as it queues action triggers,
- // imports rc scripts, etc. It should be simplified and run in vendor_init context.
- // mount and umount are run in the same context as mount_all for symmetry.
- {"mount_all", {0, kMax, {false, do_mount_all}}},
- {"mount", {3, kMax, {false, do_mount}}},
- {"perform_apex_config", {0, 0, {false, do_perform_apex_config}}},
- {"umount", {1, 1, {false, do_umount}}},
- {"umount_all", {0, 1, {false, do_umount_all}}},
- {"update_linker_config", {0, 0, {false, do_update_linker_config}}},
- {"readahead", {1, 2, {true, do_readahead}}},
- {"remount_userdata", {0, 0, {false, do_remount_userdata}}},
- {"restart", {1, 1, {false, do_restart}}},
- {"restorecon", {1, kMax, {true, do_restorecon}}},
- {"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
- {"rm", {1, 1, {true, do_rm}}},
- {"rmdir", {1, 1, {true, do_rmdir}}},
- {"setprop", {2, 2, {true, do_setprop}}},
- {"setrlimit", {3, 3, {false, do_setrlimit}}},
- {"start", {1, 1, {false, do_start}}},
- {"stop", {1, 1, {false, do_stop}}},
- {"swapon_all", {0, 1, {false, do_swapon_all}}},
- {"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
- {"symlink", {2, 2, {true, do_symlink}}},
- {"sysclktz", {1, 1, {false, do_sysclktz}}},
- {"trigger", {1, 1, {false, do_trigger}}},
- {"verity_update_state", {0, 0, {false, do_verity_update_state}}},
- {"wait", {1, 2, {true, do_wait}}},
- {"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
- {"write", {2, 2, {true, do_write}}},
- };
- // clang-format on
- return builtin_functions;
- }
- // Builtin-function-map end
2.init.rc中的服务是如何被启动的
首先以ActionManager和ServiceList通过GetInstance分别创建单例,然后调用LoadBootScripts函数传入参数,LoadBootScripts代码如下,可以看到会先去ro.boot.init_rc中读取配置,如果为空,会进入解析init.rc的流程,否则解析配置
- static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
- //创建解析器
- Parser parser = CreateParser(action_manager, service_list);
-
- //获取ro.boot.init_rc配置
- std::string bootscript = GetProperty("ro.boot.init_rc", "");
- if (bootscript.empty()) {
- //解析init.rc
- parser.ParseConfig("/system/etc/init/hw/init.rc");
- //如果init分区没有挂载,添加到late_import_paths
- if (!parser.ParseConfig("/system/etc/init")) {
- late_import_paths.emplace_back("/system/etc/init");
- }
- // late_import is available only in Q and earlier release. As we don't
- // have system_ext in those versions, skip late_import for system_ext.
-
- parser.ParseConfig("/system_ext/etc/init");
- //如果vender分区没有挂载,添加到late_import_paths
- if (!parser.ParseConfig("/vendor/etc/init")) {
- late_import_paths.emplace_back("/vendor/etc/init");
- }
- //如果odm分区没有挂载,添加到late_import_paths
- if (!parser.ParseConfig("/odm/etc/init")) {
- late_import_paths.emplace_back("/odm/etc/init");
- }
- //如果product分区没有挂载,添加到late_import_paths
- if (!parser.ParseConfig("/product/etc/init")) {
- late_import_paths.emplace_back("/product/etc/init");
- }
- } else {
- //解析ro.boot.init_rc
- parser.ParseConfig(bootscript);
- }
- }
init.rc文件是用Android Init Language编写的特殊文件,使用这种语法编写的文件,统一用.rc作为文件后缀,其语法指令规则可参考源码system/core/init/README.md文档,其中包含Commands、Options等,
Commands定义在system/core/init/builtins.cpp中
Options定义在system/core/init/service_parser.cpp中
后续会单独篇幅说明这个语法规则,本篇暂不展开说明。
init.rc文件在源码中的位置:system/core/rootdir/init.rc
回到SecondStageMain方法中,am.ExecuteOneCommand()函数会执行从.rc文件中解析出来的指令,跟一下这个函数,函数在./action_manager.cpp第67行:
- void ActionManager::ExecuteOneCommand() {
- {
- auto lock = std::lock_guard{event_queue_lock_};
- // Loop through the event queue until we have an action to execute
- while (current_executing_actions_.empty() && !event_queue_.empty()) {
- for (const auto& action : actions_) {
- if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
- event_queue_.front())) {
- current_executing_actions_.emplace(action.get());
- }
- }
- event_queue_.pop();
- }
- }
-
- if (current_executing_actions_.empty()) {
- return;
- }
-
- //从队列头获得一个action
- auto action = current_executing_actions_.front();
-
- if (current_command_ == 0) {
- std::string trigger_name = action->BuildTriggersString();
- LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
- << ":" << action->line() << ")";
- }
-
- //调用到action的ExecuteOneCommand中,current_command_代表行数
- action->ExecuteOneCommand(current_command_);
-
- // If this was the last command in the current action, then remove
- // the action from the executing list.
- // If this action was oneshot, then also remove it from actions_.
- ++current_command_;
- if (current_command_ == action->NumCommands()) {
- current_executing_actions_.pop();
- current_command_ = 0;
- if (action->oneshot()) {
- auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
- actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
- actions_.end());
- }
- }
- }
会调用到action->ExecuteOneCommand()函数,继续跟./action.cpp:146行:
- void Action::ExecuteOneCommand(std::size_t command) const {
- // We need a copy here since some Command execution may result in
- // changing commands_ vector by importing .rc files through parser
- Command cmd = commands_[command];
- //继续跟
- ExecuteCommand(cmd);
- }
-
- void Action::ExecuteCommand(const Command& command) const {
- android::base::Timer t;
- //执行了InvokeFunc
- auto result = command.InvokeFunc(subcontext_);
- auto duration = t.duration();
-
- // Any action longer than 50ms will be warned to user as slow operation
- if (!result.has_value() || duration > 50ms ||
- android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
- std::string trigger_name = BuildTriggersString();
- std::string cmd_str = command.BuildCommandString();
-
- LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
- << ":" << command.line() << ") took " << duration.count() << "ms and "
- << (result.ok() ? "succeeded" : "failed: " + result.error().message());
- }
- }
调用到command.InvokeFunc()函数中,看看做了什么:
- Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
- //从vendor 或 oem 解析出来的rc文件都会走这里,会有一定限制
- if (subcontext) {
- if (execute_in_subcontext_) {
- return subcontext->Execute(args_);
- }
-
- auto expanded_args = subcontext->ExpandArgs(args_);
- if (!expanded_args.ok()) {
- return expanded_args.error();
- }
- return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
- }
- //原生init.rc文件命令会走这里
- return RunBuiltinFunction(func_, args_, kInitContext);
- }
-
-
- Result<void> RunBuiltinFunction(const BuiltinFunction& function,
- const std::vector<std::string>& args, const std::string& context) {
- auto builtin_arguments = BuiltinArguments(context);
-
- builtin_arguments.args.resize(args.size());
- builtin_arguments.args[0] = args[0];
- for (std::size_t i = 1; i < args.size(); ++i) {
- auto expanded_arg = ExpandProps(args[i]);
- if (!expanded_arg.ok()) {
- return expanded_arg.error();
- }
- builtin_arguments.args[i] = std::move(*expanded_arg);
- }
- //执行这个函数
- return function(builtin_arguments);
- }
-
function就是上面第一点提到的,当触发map中元素key调用时,会对应调用value对应的函数,这里以init.rc中一段脚本举例,先不看启动条件,仅看一下单纯指令,看一下服务是如何启动的:
- on zygote-start && ...
- ...
- start statsd
- start netd
- start zygote
- start zygote_secondary
结合上面map中的key-value对应关系,发现start对应着do_start函数,源码位置/system/core/init/builtins.cpp:
- static Result<void> do_start(const BuiltinArguments& args) {
- //通过传入的arg,查找对应服务
- Service* svc = ServiceList::GetInstance().FindService(args[1]);
- if (!svc) return Error() << "service " << args[1] << " not found";
- //通过调用start执行
- if (auto result = svc->Start(); !result.ok()) {
- return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
- }
- return {};
- }
通过命令搜索
- grep "::Start()" ./ -rn
-
-
- 搜索结果
- ./service.cpp:397:Result<void> Service::Start() {
函数如下
-
- Result<void> Service::Start() {
- ...
-
- pid_t pid = -1;
- //通过克隆或者fork子进程
- if (namespaces_.flags) {
- pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
- } else {
- pid = fork();
- }
-
- //子进程创建成功
- if (pid == 0) {
- //设置权限掩码
- umask(077);
-
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
- !result.ok()) {
- LOG(FATAL) << "Service '" << name_
- << "' failed to set up namespaces: " << result.error();
- }
-
- for (const auto& [key, value] : environment_vars_) {
- setenv(key.c_str(), value.c_str(), 1);
- }
-
- for (const auto& descriptor : descriptors) {
- descriptor.Publish();
- }
-
- if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
- LOG(ERROR) << "failed to write pid to files: " << result.error();
- }
-
- if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
- LOG(ERROR) << "failed to set task profiles";
- }
-
- // As requested, set our gid, supplemental gids, uid, context, and
- // priority. Aborts on failure.
- SetProcessAttributesAndCaps();
-
- //内部调用的execv来启动的程序
- if (!ExpandArgsAndExecv(args_, sigstop_)) {
- PLOG(ERROR) << "cannot execv('" << args_[0]
- << "'). See the 'Debugging init' section of init's README.md for tips";
- }
-
- _exit(127);
- }
-
- if (pid < 0) {
- pid_ = 0;
- return ErrnoError() << "Failed to fork";
- }
- ...
-
- return {};
- }
追一下ExpandArgsAndExecv函数:
-
- static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
- std::vector<std::string> expanded_args;
- std::vector<char*> c_strings;
-
- expanded_args.resize(args.size());
- c_strings.push_back(const_cast<char*>(args[0].data()));
- for (std::size_t i = 1; i < args.size(); ++i) {
- auto expanded_arg = ExpandProps(args[i]);
- if (!expanded_arg.ok()) {
- LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
- }
- expanded_args[i] = *expanded_arg;
- c_strings.push_back(expanded_args[i].data());
- }
- c_strings.push_back(nullptr);
-
- if (sigstop) {
- kill(getpid(), SIGSTOP);
- }
- //有没有很熟悉,最终还是通过执行execv来执行程序文件的
- return execv(c_strings[0], c_strings.data()) == 0;
- }
先通过克隆或者fork子进程,然后调用execv函数启动程序文件,由此一个服务就启动起来了。
至此一个完整的init进程启动服务就分析完成了,下一篇分析一下zygote的启动及zygote启动后做了哪些事情!