Android系统启动流程--init进程的启动流程

news2024/11/24 1:38:27

        这可能是个系列文章,用来总结和梳理Android系统的启动过程,以加深对Android系统相对全面的感知和理解(基于Android11)。

 1.启动电源,设备上电

引导芯片代码从预定义的地方(固化在ROM,全称Read Only Memory,是一种只能读出事先所存的数据的固态半导体存储器)开始执行,加载引导程序BootLoader到RAM(RAM 是随机存取存储器,它的特点是易挥发性,即掉电失忆)并执行该程序。

2.引导程序BootLoader

BootLoader是用来引导Android操作系统启动的程序,相当于windows中的bose程序,它创建了Android系统中第一个进程idle(pid=0)。

3.Linux内核启动

idle进程创建后中做了初始化进程管理、内存管理、加载Binder Driver   Display、Camera Driver等相关工作,并创建了以下两个进程(线程),干完这些工作之后idle进程就空闲下来了。

①创建kthread(pid=2)线程,在其中创建kworker线程,软中断线程ksoftirqd,thermal守护线程(kernel中没有进程线程之分),内核线程鼻祖。

②init(pid=1)进程,它是用户空间的第一个进程,由此开始后面创建的进程都是由init或其子进程fork而来。

以上3步简单了解即可,重点从启动init进程开始。

4. init进程启动

当kernel启动后会开始启动init进程:

//文件路径: kernel/common/init/main.c

static int __ref kernel_init(void *unused)
{
	int ret;

    //执行/bin/init程序
	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;
}

"/bin/init"是在Android系统源码编译时编译出的一个可执行程序,路径为Android设备上的"system/bin/init",而这个init程序则是由system/core/init/main.cpp文件编译生成的(可查看同目录下的Android.bp,其中描述了编译规则),所以执行这个init程序其实就是运行到了main.cpp的main()方法。

//文件路径 system/core/init/main.cpp

//此方法会执行多次
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif

    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);
        }

        //SELinux 是由美国NSA(国安局)和 SCC 开发的 Linux的一个扩张强制访问控制安全模块
        if (!strcmp(argv[1], "selinux_setup")) { //(是从FirstStageMain()中调用到这里)
            //step2:启动selinux
            return SetupSelinux(argv);
        }

        //step3:
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    //step1:第一次进入argv为空
    return FirstStageMain(argc, argv);
}

我们先看第一步会做哪些操作:

//文件路径:system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    //安全处理,如果init进程挂掉,会重启引导加载程序
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    
    //创建和挂载启动所需的目录文件
    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(mount("devpts", "/dev/pts", "devpts", 0, NULL));
    ...
    
    //把标准输入输出重定向到dev/null文件
    SetStdioToDevNull(argv);
    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    // 初始化日志
    InitKernelLogging(argv);

    ...
    //启动selinux
    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)); //这里执行就会运行到上面提到的main()中selinux_setup分支
    ...
    return 1
}
    

总结:FirstStageMain()中主要做了以下几件事

  1. 挂载创建系统所需的文件和目录
  2. 重定向输入输出
  3. 初始化内核日志打印

接下来是第二步:

//文件路径: system/core/init/selinux.cpp

int SetupSelinux(char** argv) {
    //重定向(初始化)输入输出
    SetStdioToDevNull(argv);
	//初始化kernel日志打印
    InitKernelLogging(argv);

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    boot_clock::time_point start_time = boot_clock::now();

    MountMissingSystemPartitions();

    // Set up SELinux, loading the SELinux policy.
    // 启动selinux安全策略
    SelinuxSetupKernelLogging();
    SelinuxInitialize();

    // We're in the kernel domain and want to transition to the init domain.  File systems that
    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
    // but other file systems do.  In particular, this is needed for ramdisks such as the
    // recovery image for A/B devices.
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
    }

    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);

    //再次执行init程序,这次走到second_stage分支
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

总结:SetupSelinux()中主要做的几件事:

  1. 重定向输入输出
  2. 初始化内核日志打印
  3. 初始化selinux相关内容

接下来是第三步:

//文件路径:system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    //重启判断,是否重新启动引导程序
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
  
    ...

    //重定向(初始化)输入输出
    SetStdioToDevNull(argv);
	//初始化kernel日志打印
    InitKernelLogging(argv);

    //初始化属性服务,property_service.cpp
    PropertyInit();

    // 再次初始化selinux
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();
 
    //处理子进程的终止信号,及时清除挂掉的进程,防止出现僵尸进程
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    StartPropertyService(&property_fd);

    //建立linux命令与实现其功能的函数间的关系
    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
    Action::set_function_map(&function_map);

    //解析init.rc文件,将解析出的内容存储到下面两个对象中
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    LoadBootScripts(am, sm);

    //循环处理init.rc脚本中的command命令,处理完就进入等待
    while (true) {
      
       if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            //内部遍历执行每个action中携带的command对应的执行函数
			am.ExecuteOneCommand();
        }
    }
    
}

总结:SecondStageMain()中主要做的几件事:

  1. 重定向输入输出
  2. 初始化内核日志打印
  3. 初始化属性服务
  4. 监听并处理挂掉的子进程
  5. 建立linux命令与实现方法的联系
  6. 初始化selinux相关内容
  7. 解析init.rc文件
  8. 执行init.rc中的action

init进程总结:

init进程会走main.cpp,然后分阶段去执行main()函数,这个调用是循环调用的方式,最后一个阶段是SecondStageMain(),里面会执行一个非常重要的方法LoadBootScripts(am,sm),这个方法解析了一个init.rc文件,并将这些命令写到了am与sm中,在while循环里通过ExecuteOneCommand去执行这些命令。其中就包含了启动zygote进程的命令。当然这个while是个死循环以保证init进程的存活,那要是没事做怎么办?那就睡眠等待,用epoll.wait.

init做的事:

  1. 挂载文件
  2. 设置selinux
  3. 启动属性服务
  4. 解析init.rc,执行脚本中一行一行的Linux命令来启动脚本
  5. 循环处理脚本--包括启动zygote、serviceManager进程
  6. 守护系统关键进程:如蓝牙、铃声、接打电话、应用安装等进程名结尾带d的系统进程。

到这里Android系统init进程的启动就完成了,本篇的最后提到了解析init.rc文件,从这里会引出后续两个比较重要的进程的启动流程:

1.ServiceManager进程的启动流程

2.zygote进程的启动流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

hive 入门 一般用于正式环境 修改元数据(二)

安装配置可参考 https://blog.csdn.net/weixin_43205308/article/details/130020674 1、如果启动过derby&#xff0c;最小初始化过 在安装路径下删除 derby.log metastore_db rm -rf derby.log metastore_db此处省略安装mysql数据库 2、配置MySQL 登录mysql mysql -uroot …

EightCap易汇:外汇投资入门需要了解哪些必要知识?

外汇市场是国际投资市场&#xff0c;日内交易量巨大&#xff0c;盈利机会极多。外汇是一种含有杠杆的投资产品&#xff0c;杠杆带来了高收益&#xff0c;也会带来高风险&#xff0c;对于外汇新手来说存在一定难度。新手投资者要如何交易&#xff0c;才能抓住外汇市场的盈利机会…

C++标准库 -- 关联容器 (Primer C++ 第五版 · 阅读笔记)

C标准库 -- 关联容器(Primer C 第五版 阅读笔记&#xff09;第11章 关联容器------(持续更新)11.1、使用关联容器11.2、关联容器概述11.3、关联容器操作11.4、无序容器第11章 关联容器------(持续更新) 关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和…

助力AI语音开发者的社区-语音之家

语音之家简介 语音之家成立于2021年4月&#xff0c;是一家助力AI语音开发者的社区&#xff0c;我们希望通过知识传播、在线学习、资源分享、各类活动等方式提供全生命周期的服务&#xff0c;帮助全球的AI语音开发者获得成长&#xff0c;洞见AI语音技术领域的发展。目前&#x…

TiDB实战篇-TiDB Lightning 导入数据

简介 使用TiDB Lightning 导入数据。 原理 TiKV进入导入模式 它是使用物理导入的模式&#xff0c;将SQL文件直接导入到TiKV中&#xff0c;它是一种初始化的导入&#xff0c;也就是说目标的数据库和表都是不能够存在的&#xff08;注意事项&#xff0c;在这种方式导入的时候T…

论文笔记 U-Net: Convolutional Networks for Biomedical Image Segmentation

摘要&#xff1a;人们普遍认为&#xff0c;深度网络的成功训练需要数千个带注释的训练样本。在本文中&#xff0c;我们提出了一种网络和训练策略&#xff0c;该策略依赖于大量使用数据增强来更有效地使用可用的注释样本。该体系结构包括用于捕获上下文的收缩路径和用于实现精确…

计算机组件介绍

1. CPU 1.1 主频 1.2 CPU缓存 注&#xff1a;越高越好 2. Memory 注&#xff1a;只有内存是主存&#xff08;因为CPU只能和内存打交道&#xff09;&#xff0c;硬盘这种就是外存&#xff08;因为硬盘太慢了&#xff0c;跟不上cpu的运行速度&#xff09; 3. I/O 注&#xff1a;输…

Segment Anything论文翻译,SAM模型,SAM论文,SAM论文翻译;一个用于图像分割的新任务、模型和数据集;SA-1B数据集

【论文翻译】- Segment Anything / Model / SAM论文 论文链接&#xff1a; https://arxiv.org/pdf/2304.02643.pdfhttps://ai.facebook.com/research/publications/segment-anything/ 代码连接&#xff1a;https://github.com/facebookresearch/segment-anything 论文翻译&…

微软 AI 作图上线完全免费,“奖励自己”可提升速度

ChatGPT 的横空出世应该已经让大家意识到了 AI 的恐怖。 称不上啥都能干&#xff0c;但给东西它真学&#xff0c;学得还比你快。 最近一段时间 AI 在作图领域又一次人气暴涨。 什么小姐姐写真、突破时间线的历史古图、甚至是抽象的表情包都可能源于 AI 之手。 看着手痒想玩玩…

滴滴滴,请看MYSQL事务的四大特征(ACID)的实现原理:晓其原理而通其实现。

一.什么是事务的四特征 原子性&#xff08;Atomicity&#xff0c;或称不可分割性&#xff09;一致性&#xff08;Consistency&#xff09;隔离性&#xff08;Isolation&#xff09;持久性&#xff08;Durability&#xff09; 接下来&#xff0c;我们将对四大特性的具体概念以及…

本地快速搭建Kubernetes单机版实验环境(含问题解决方案)

Kubernetes是一个容器编排系统&#xff0c;用于自动化应用程序部署、扩展和管理。本指南将介绍Kubernetes的基础知识&#xff0c;包括基本概念、安装部署和基础用法。 一、什么是Kubernetes&#xff1f; Kubernetes是Google开发的开源项目&#xff0c;是一个容器编排系统&…

长沙基层公务员待遇调查结果

之前发放了1000份调查问卷&#xff0c;统计过长沙各个行业&#xff08;其中一半是信息产业从业人员&#xff09;的待遇情况&#xff0c;发现很多人对长沙公务员&#xff08;包含有编制/合同工&#xff09;的待遇很感兴趣。我随手翻了翻几个基层政府单位的财政决算公开说明&…

MySQL笔记2

MySQL笔记2一、CRUD操作1.修改数据&#xff1a;update2.删除数据&#xff1a;delete二、数据库中表的约束1.非空约束 not null2.唯一性约束 unique3.主键约束 primary key4.外键约束三、索引1**什么是索引**&#xff1f;2**MySQL中最经典的两种存储引擎**3**为何需要索引**&…

Linux项目日志管理log4cpp的安装与使用【结合sample】

文章目录前言log4cpp安装log4cpp的使用设置类别输出的&#xff08;category&#xff09;和日志优先级&#xff08;priority&#xff09;定义一个宏用于输出日志配置文件使用log4cpp的栗子结语前言 我们都清楚对于一个项目来说它的日志信息是非常重要的&#xff0c;那么我们应该…

springcloud微服务架构搭建过程

项目地址&#xff1a;源代码 仅作为学习用例使用&#xff0c;是我开发过程中的总结、实际的一部分使用方式 开发环境&#xff1a; jdk11 springboot2.7.6 springcloud2021.0.5 alibabacloud 2021.0.4.0 redis6.0 mysql8.0 一、项目搭建 wdz-api&#xff1a;存放远程服务调用相关…

VS for Qt 向MySql 数据库中插入中文

问题&#xff1a; 今天我想向数据库中插入中文&#xff0c;但是&#xff0c;插入的时候会报错&#xff1a; 首先先看报错&#xff1a; QSqlError("1054", "QMYSQL: Unable to execute query", "Unknown column 韩红 in field list")如果错误码…

将Linux服务器上的项目上传至Github

使用git上传项目到github常规的步骤继续上传注意事项参考文章常规的步骤 初始化git空间 git init向缓冲区添加想要上传的文件 git add -f /data/xuhongbo/xuhongbo.code/unbiased_sgg_xuhongbo_BCL/maskrcnn_benchmark/*添加备注信息告诉机器&#xff0c;你真的要添加上述文…

elasticsearch-7.17.9

1、ElasticSearch 1.1、概念 1.1.1、分片(shard) 1、分片 在ES中所有数据的文件块&#xff0c;也是数据的最小单元块&#xff0c;整个ES集群的核心就是对所有分片的分布、索引、负载、路由等达到惊人的速度。 实列场景&#xff1a; 假设 IndexA 有2个分片&#xff0c;向 I…

数据结构 - 计数排序 | C

什么是计数排序 如上图&#xff0c;统计数组中值的个数&#xff1a; 2个[1]&#xff1a;1&#xff0c;1 1个[2]&#xff1a;2 3个[3]&#xff1a;3&#xff0c;3&#xff0c;3 2个[4]&#xff1a;4&#xff0c;4 传给原数组&#xff1a;&#xff08;即完成排序↓&#xff09; …

Fast R-CNN

目录 1. 关于 R-CNN 的缺点 2. Fast R-CNN 3. Fast R-CNN 算法的流程 3.1 CNN 特征提取 3.2 ROI pooling 3.3 Fast R-CNN 的输出 3.4 损失函数 4. Fast R-CNN的不足 1. 关于 R-CNN 的缺点 RCNN算法流程如下 RCNN算法分为四个步骤&#xff1a; SS 算法生成2000个候选框…