Android 11(R)启动流程 初版

ops/2024/10/21 7:47:08/

启动流程

bootloader会去启动android第一个进程Idle,pid为0,会对进程 内存管理等进行初始化。Idle还被称作swapper。Idle会去创建两个进程,一个是init,另外一个是kthread。 kthread会去启动内核,用户是由init进行启动。

init进程的启动

在内核common/init/main.c 的kernel_init函数中

	if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;

可以看到这里启动了init进程。并且这个bin是在根目录下的。

static int run_init_process(const char *init_filename)
{argv_init[0] = init_filename;pr_info("Run %s as init process\n", init_filename);return do_execve(getname_kernel(init_filename),(const char __user *const __user *)argv_init,(const char __user *const __user *)envp_init);
}

do_execve 是一个内核函数,用于执行用户空间的程序,最后会调用到__do_execve_file函数

/* 参数解释
int fd:文件描述符,用于指定要执行的文件。如果不使用文件描述符,可以传入 AT_FDCWD。
struct filename *filename:文件名结构体,包含要执行的可执行文件的路径。
struct user_arg_ptr argv:参数指针结构体,指向传递给新进程的命令行参数。
struct user_arg_ptr envp:环境变量指针结构体,指向传递给新进程的环境变量。
int flags:标志位,用于控制执行行为。
struct file *file:文件结构体指针,指向要执行的文件。如果不通过文件描述符打开文件,可以传入 NULL。*/static int __do_execve_file(int fd, struct filename *filename,struct user_arg_ptr argv,struct user_arg_ptr envp,int flags, struct file *file)
{char *pathbuf = NULL;struct linux_binprm *bprm;struct files_struct *displaced;int retval;//检查 filename 是否为错误指针,如果是,则返回相应的错误码。if (IS_ERR(filename))return PTR_ERR(filename);/** We move the actual failure in case of RLIMIT_NPROC excess from* set*uid() to execve() because too many poorly written programs* don't check setuid() return code.  Here we additionally recheck* whether NPROC limit is still exceeded.*///检查当前用户的进程数是否超过了限制,如果超过了限制,则返回 -EAGAIN 错误码。//清除进程标志 PF_NPROC_EXCEEDED。if ((current->flags & PF_NPROC_EXCEEDED) &&atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {retval = -EAGAIN;goto out_ret;}/* We're below the limit (still or again), so we don't want to make* further execve() calls fail. */current->flags &= ~PF_NPROC_EXCEEDED;//调用 unshare_files 函数分离文件结构,以确保进程在执行期间独占文件描述符表retval = unshare_files(&displaced);if (retval)goto out_ret;retval = -ENOMEM;//分配并初始化 linux_binprm 结构体,该结构体用于存储进程执行所需的各种信息bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);if (!bprm)goto out_files;retval = prepare_bprm_creds(bprm);if (retval)goto out_free;//调用 check_unsafe_exec 函数进行安全检查check_unsafe_exec(bprm);current->in_execve = 1;//如果没有提供文件指针,则使用 do_open_execat 函数根据文件描述符和文件名打开文件if (!file)file = do_open_execat(fd, filename, flags);retval = PTR_ERR(file);if (IS_ERR(file))goto out_unmark;/*准备执行环境:调用 sched_exec 函数设置调度相关信息。初始化 bprm 结构体中的文件和文件名信息。调用 bprm_mm_init 函数初始化进程的内存管理。调用 prepare_arg_pages 函数准备参数和环境变量的内存页。调用 prepare_binprm 函数准备可执行文件的二进制参数。*/sched_exec();bprm->file = file;if (!filename) {bprm->filename = "none";} else if (fd == AT_FDCWD || filename->name[0] == '/') {bprm->filename = filename->name;} else {if (filename->name[0] == '\0')pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);elsepathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",fd, filename->name);if (!pathbuf) {retval = -ENOMEM;goto out_unmark;}/** Record that a name derived from an O_CLOEXEC fd will be* inaccessible after exec. Relies on having exclusive access to* current->files (due to unshare_files above).*/if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;bprm->filename = pathbuf;}bprm->interp = bprm->filename;retval = bprm_mm_init(bprm);if (retval)goto out_unmark;retval = prepare_arg_pages(bprm, argv, envp);if (retval < 0)goto out;retval = prepare_binprm(bprm);if (retval < 0)goto out;//复制参数和环境变量://调用 copy_strings_kernel 函数复制文件名到用户栈。//调用 copy_strings 函数依次复制环境变量和命令行参数到用户栈retval = copy_strings_kernel(1, &bprm->filename, bprm);if (retval < 0)goto out;bprm->exec = bprm->p;retval = copy_strings(bprm->envc, envp, bprm);if (retval < 0)goto out;retval = copy_strings(bprm->argc, argv, bprm);if (retval < 0)goto out;/** When argv is empty, add an empty string ("") as argv[0] to* ensure confused userspace programs that start processing* from argv[1] won't end up walking envp. See also* bprm_stack_limits().*/if (bprm->argc == 0) {const char *argv[] = { "", NULL };retval = copy_strings_kernel(1, argv, bprm);if (retval < 0)goto out;bprm->argc = 1;}//调用 exec_binprm 函数执行可执行文件retval = exec_binprm(bprm);if (retval < 0)goto out;//如果执行成功,清理各种临时数据结构,释放资源,并返回成功状态。//如果执行失败,进行错误处理,清理资源,并返回相应的错误码。/* execve succeeded */current->fs->in_exec = 0;current->in_execve = 0;rseq_execve(current);acct_update_integrals(current);task_numa_free(current, false);free_bprm(bprm);kfree(pathbuf);if (filename)putname(filename);if (displaced)put_files_struct(displaced);return retval;out:if (bprm->mm) {acct_arg_size(bprm, 0);mmput(bprm->mm);}out_unmark:current->fs->in_exec = 0;current->in_execve = 0;out_free:free_bprm(bprm);kfree(pathbuf);out_files:if (displaced)reset_files_struct(displaced);
out_ret:if (filename)putname(filename);return retval;
}

接下来就正式进入init的启动流程,init代码路径:system/core/init/main.cpp  main函数代码较短,直接贴上来。

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)__asan_set_error_report_callback(AsanReportCallback);
#endifif (!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);
}

FirstStageMain

在FirstStageMain中,主要是挂载文件目录,创建一些文件,做一些初始化等的阶段。

还包括初始化日志系统,将输入输出重定向。

最后,会去启动init 并且带一个selinux_setup

SelinuxInitialize

在SelinuxInitialize中会初始化一些安全策略

selinux_setup之后会去second_stage

SecondStageMain

PropertyInit();  初始化属性域selinux初始化
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();//恢复安全上下文//处理子进程的终止信号,判断子进程有没有挂掉 (僵尸进程) 回收资源。
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
StartPropertyService(&property_fd);//匹配命令和函数之间的关系,ls 等命令和函数的关系
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

/********************************/
//解析init.rc
LoadBootScripts(am, sm);
/********************************/
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {Parser parser = CreateParser(action_manager, service_list);std::string bootscript = GetProperty("ro.boot.init_rc", "");if (bootscript.empty()) {parser.ParseConfig("/system/etc/init/hw/init.rc");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");if (!parser.ParseConfig("/product/etc/init")) {late_import_paths.emplace_back("/product/etc/init");}if (!parser.ParseConfig("/odm/etc/init")) {late_import_paths.emplace_back("/odm/etc/init");}if (!parser.ParseConfig("/vendor/etc/init")) {late_import_paths.emplace_back("/vendor/etc/init");}} else {parser.ParseConfig(bootscript);}
}

在LoadBootScripts中会去创建三种对应解释器

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {Parser parser;parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt));parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));return parser;
}

init处理的重要事情

1.挂载文件

2.设置selinux

3.开启属性服务

4.解析init.rc

5.循环处理脚本  -》 启动zygote

6.循环等待

init.rc -》zygote

# Now we can start zygote for devices with file based encryption
trigger zygote-start

这段 init.rc 脚本配置了在不同加密状态下启动 zygote 及其相关服务的逻辑。根据设备的加密状态(未加密、不支持加密或已加密)

init.rc解析时会导入import /system/etc/init/hw/init.${ro.zygote}.rc

这里大括号{}中的ro.zygote表示它会用 ro.zygote 系统属性的值来替换 ${ro.zygote}

所以这里是  import /system/etc/init/hw/init.zygote32.rc

init.zygote32.rc类似的文件有四个,对应32位系统,64位系统,还有主32次64或者主64的。这种会在32执行失败之后再执行64位的。

启动一个服务 zygote   后面是路径, 在后面是参数

zygote的作用之一是要进入java层,为Android启动运行时环境。

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); -》class AppRuntime : public AndroidRuntime

app_main.cpp中 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);去启动android运行时环境。

app_main.cpp   runtime.start -》

AndroidRuntime.cpp    AndroidRuntime::start -》

startVm 启动虚拟机-》

startReg  注册jni-》

env->CallStaticVoidMethod(startClass, startMeth, strArray); 

启动com.android.internal.os.ZygoteInit  -》ZygoteInit.java . main

AndroidRuntime 中包括对虚拟机一系列的初始化,这里包括heapsize的初始化为16M,

进程和虚拟机是什么关系

虚拟机实现了进程中内存管理的功能

注册jni

startReg-》

register_jni_procs-》

register_com_android_internal_os_RuntimeInit (env)

register_com_android_internal_os_ZygoteInit_nativeZygoteInit(env)

register_com_android_internal_os_......

Zygote的java启动

runtime.start("com.android.internal.os.ZygoteInit", args, zygote);  -》

env->CallStaticVoidMethod(startClass, startMeth, strArray);-》

ZygoteInit.java  main-》

preload(bootTimingsTraceLog);//加快进程的启动-》

zygoteServer = new ZygoteServer(isPrimaryZygote);-》

Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);// 启动systemserver进程-》

caller = zygoteServer.runSelectLoop(abiList);进入loop死循环,接收AMS传过来的消息

preload加载的时间

Zygote总结:

native:

1.初始化运行环境,创建jvm

2.注册jni

3.调用zygoteinit.main

java

1.预加载 --  加快进程启动

2.socket  服务器

3.循环等待

Zygote  fork SystemServer进程

SystemServer的主要工作是管理服务,AMS  WMS都和SystemServer属于同一进程,这些服务都是在SystemServer运行起来的。

ZygoteInit.java 

-》

Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

-》

Zygote.forkSystemServer-》

Zygote.java  nativeForkSystemServer

-》

com_android_internal_os_Zygote.cpp   com_android_internal_os_Zygote_nativeForkSystemServer 

-》

ForkAndSpecializeCommon-》

fork(); -》

pid == 0  handleSystemServerProcess-》

ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);

-》

RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);

-》

RuntimeInit.commonInit();//初始化运行环境

-》

ZygoteInit.nativeZygoteInit //启动binder,方法在androidRuntime.cpp中注册

-》

RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);//ActivityThread.main();

-》

findStaticMain //app启动流程也是走这里,因此这里不仅返回AMS通过socket传过来的ActivityThread、还有systemserver

-》cl.getMethod("main", new Class[] { String[].class });//通过反射拿到对应类的main方法的Method对象

-》MethodAndArgsCaller implements Runnable  封装成Runnable对象

-》

mMethod.invoke(null, new Object[] { mArgs });  执行run时通过invoke函数执行类所对应的main函数

-》r.run();

SystemServer基本流程

main-》new SystemServer().run();-》

startBootstrapServices

startCoreServices

startOtherServices -》

createSystemContext //创建系统上下文

SystemServer如何管理服务

systemserver基本都通过mSystemServiceManager.startService 来启动服务,可以看出SystemServer是通过SystemServiceManager来管理service的,服务都必须封装systemservice类,

mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();

mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();

public class ActivityManagerService extends IActivityManager.Stub

        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

..............} 从这里看出ActivityManagerService 是一个binder。但是因为只能有一个父类,为了能够实现继承自systemservice的效果,ActivityManagerService 实现了一个静态内部类Lifecycle拓展自SystemService。

public static final class Lifecycle extends SystemService {.......}

public Lifecycle(Context context) {super(context);mService = new ActivityManagerService(context);
}

并且mSystemServiceManager.startService传递的是类名,可以通过反射创建实例,通过实例调用service.onStart();可以调用到继承自SystemService 的Lifecycle里面的onStart函数。

public <T extends SystemService> T startService(Class<T> serviceClass) {}

在通过

ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);

或者

publishBinderService(Context.USER_SERVICE, mUms); 将服务注册到servicemanager里面去,尽管publishBinderService最后也是调用ServiceManager.addService函数。


http://www.ppmy.cn/ops/91772.html

相关文章

使用Rust编写一个web todolist应用

使用rust编写一个web服务不如使用java提供的spring boot一样简单&#xff0c;需要手工去添加依赖&#xff0c;目前rust web生态已趋近成熟&#xff0c;可以尝试进行web开发。 本次开发的服务使用的依赖有 axum&#xff1a;一个专注于生态和模块化的web应用开发框架serde&…

我在高职教STM32——I2C通信入门(1)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件分…

一、安装go环境以及编译输出HelloWorld

目前的热门技术方向从分布式微服务开始转向云原生而云原生方向需要掌握GO语言&#xff0c;基于此决定利用平时的时间来完成GO语言的学习。 安装&#xff08;基于mac m1&#xff09; &#xff08;翻看了网上很多的资料&#xff0c;发现很多人记录的有很多问题&#xff0c;一个…

常见的框架漏洞

Thinkphp5x远程命令执行及getshell 远程命令执行 打开环境&#xff0c;拼接以下目录&#xff0c;执行系统命令 ?sindex/think\app/invokefunction&functioncall_user_func_array&vars[0]system&vars[1][]whoami 远程代码执行 打开环境&#xff0c;拼接以下目录…

DNS安全概述

一、DNS的解析过程 1.递归解析 递归解析是一种由DNS客户端&#xff08;通常是用户的应用程序&#xff0c;如一个浏览器&#xff09;向本地DNS解析器发出解析请求&#xff0c;然后本地DNS解析器负责查询最终结果并将结果返回给客户端&#xff0c;而中间的所有查询请求都由本地D…

书生大模型实战营-基础关卡-2-8G 显存玩转书生大模型 Demo

Cli Demo 部署 InternLM2-Chat-1.8B 模型 先简单试试 InternLM2-Chat-1.8B 模型的能力 生成 300 字的小故事 我的prompt&#xff1a;写一个300字以内的小故事,故事符合中国特色. 模型输出&#xff1a;故事整体性还是很好的。 在一个小镇上&#xff0c;住着一只名叫小明的猫咪…

河南萌新联赛2024第(四)场:河南理工大学 C 岗位分配 I 马拉松

文章目录 8.7 2024第&#xff08;四&#xff09;场&#xff1a;河南理工大学C 岗位分配思路代码 I 马拉松思路代码 8.7 2024第&#xff08;四&#xff09;场&#xff1a;河南理工大学 [河南萌新联赛2024第&#xff08;四&#xff09;场&#xff1a;河南理工大学 ](河南萌新联赛…

基于Java中的SSM框架实现校园图书在线阅读系统项目【项目源码+论文说明】

基于Java中的SSM框架实现校园图书在线阅读系统演示 摘要 随着计算机的广泛应用和推广&#xff0c;越来越多的传统人工管理模式逐渐被线上化平台系统所替代&#xff0c;高校作为知识的学府在计算机的应用上非常广泛&#xff0c;同时随着近年来高校信息化建设的不断提升&#xf…