Android系统10 RK3399 init进程启动(四十八) init第一阶段分析

news2024/11/15 23:45:00

配套系列教学视频链接:

安卓系列教程之ROM系统开发-百问100ask

说明

系统:Android10.0

设备: FireFly RK3399 (ROC-RK3399-PC-PLUS)

前言

本文通过代码梳理的方式,给大家介绍Android init祖先进程第一阶段的工作流程。


第一阶段整理框架:执行在boot.img(ramdisk)中,主要创建必须的文件夹,挂载虚拟文件系统,挂载system,vendor分区。

代码如下:system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

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

    std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
    if (x != 0) errors.emplace_back(#x " failed", errno);

    // Clear the umask.
    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.
    //将tmpfs文件系统挂载到dev目录
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    // 创建dev/pts目录 :是远程登陆(telnet,ssh等)后创建的控制台设备文件所在的目录
    CHECKCALL(mkdir("/dev/pts", 0755));
    //创建dev/socket目录,rc脚本中service启动时,会创建很多域套接字在此目录
    CHECKCALL(mkdir("/dev/socket", 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));
    gid_t groups[] = {AID_READPROC};
    CHECKCALL(setgroups(arraysize(groups), groups));
    //将sysfs文件系统挂载到sys目录,用来访问内核和驱动的信息
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    //将selinuxfs文件系统挂载到目录/sys/fs/selinux ,通过该路径可以完成和内核selinux模块交互
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
    //创建/dev/kmsg设备节点,用于用户空间写入日志到内核日志缓冲区中
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) {
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    }
    //节点/dev/random和/dev/urandom是Linux系统中提供的随机伪设备,
    //这两个设备的任务,是提供永不为空的随机字节数据流。
    //很多解密程序与安全应用程序(如SSH Keys,SSL Keys等)需要它们提供的随机数据流。
    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // This is needed for log wrapper, which gets called before ueventd runs.
    // 创建伪终端
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    // 创建空设备
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    // These below mounts are done in first stage init so that first stage mount can mount
    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,
    // should be done in rc files.
    // Mount staging areas for devices managed by vold
    // See storage config details at http://source.android.com/devices/storage/
    //将tmpfs文件系统到mnt目录,这个目录正常是挂载光驱,usb设备的
    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    // /mnt/vendor is used to mount vendor-specific partitions that can not be
    // part of the vendor partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/vendor", 0755));
    // /mnt/product is used to mount product-specific partitions that can not be
    // part of the product partition, e.g. because they are mounted read-write.
    CHECKCALL(mkdir("/mnt/product", 0755));

    // /apex is used to mount APEXes
    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    // /debug_ramdisk is used to preserve additional files from the debug ramdisk
    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL
    //把标准输出,标准输入,标准错误输出重定向到dev/null
    SetStdioToDevNull(argv);
    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    //初始化init log并输出定向到dev/kmsg
    InitKernelLogging(argv);

    if (!errors.empty()) {
        for (const auto& [error_string, error_errno] : errors) {
            LOG(ERROR) << error_string << " " << strerror(error_errno);
        }
        LOG(FATAL) << "Init encountered errors starting first stage, aborting";
    }
    //打印第一阶段的标志性日志
    LOG(INFO) << "init first stage started!";

    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
    if (!old_root_dir) {
        PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
    }

    struct stat old_root_info;
    if (stat("/", &old_root_info) != 0) {
        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
        old_root_dir.reset();
    }
    //对于将恢复用作 ramdisk 的设备,第一阶段 init 位于恢复 ramdisk 中的 /init。这些设备首先将根切换到
    //first_stage_ramdisk,以便从环境中移除恢复组件,然后执行与具有 boot-ramdisk 的设备一样的操作
    //(即,将 system.img 作为 /system 进行装载,切换根以将该装载移动到 /,然后在装载完成后释放 ramdisk 内容)。
    //如果内核命令行中存在 androidboot.force_normal_boot=1,则设备会正常启动(启动到 Android)而不是启动到恢复模式。
    //介绍:https://source.android.google.cn/devices/bootloader/system-as-root?hl=zh-cn#ramdisk
    if (ForceNormalBoot()) {
        mkdir("/first_stage_ramdisk", 0755);
        // SwitchRoot() must be called with a mount point as the target, so we bind mount the
        // target directory to itself here.
        if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
        }
        SwitchRoot("/first_stage_ramdisk");
    }

    // If this file is present, the second-stage init will use a userdebug sepolicy
    // and load adb_debug.prop to allow adb root, if the device is unlocked.
    if (access("/force_debuggable", F_OK) == 0) {
        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.
        if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
            !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
            LOG(ERROR) << "Failed to setup debug ramdisk";
        } else {
            // setenv for second-stage init to read above kDebugRamdisk* files.
            setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
        }
    }
    // 读取/vendor/etc/fstab.${ro.hardware}的挂载表格进行挂载,主要是挂载system,vendor,odm等镜像分区。
    //在初始化 SElinux 之前必须先装载 /system、/vendor 或 /odm,这个主要是因为打开了Treble的设备上,
    //为了确保init能及时导入SELinux的配置文件(contexts/*.te),需要尽快的将system/vendor等分区挂载上。
    //在Android 8.0之前,selinux的配置文件存放在boot.img(包含ramdisk)中,在内核初始化过程中,
    //boot.img中的ramdisk已经挂载到rootfs了,相应的,配置文件也就可以从rootfs读取到。
    //而Android 8.0开始,selinux配置文件放到了vendor/system分区了
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }
    //根目录发生变化,则释放ramdisk
    struct stat new_root_info;
    if (stat("/", &new_root_info) != 0) {
        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
        old_root_dir.reset();
    }

    if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
    }
    //Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dm-verity,
    //原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。
    //其中认证的范围涵盖:bootloader,boot.img,system.img
    //此处是在recovery模式下初始化avb的版本,不是recovery模式直接跳过
    SetInitAvbVersionInRecovery();

    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
    uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
    setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));

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

    return 1;
}

重要的虚拟文件系统说明:

tmpfs

虚拟文件系统,tmpfs 是有内核直接管理的的,存在于内存,无需格式化,直接使用,并且是单一的内存到内存的访问,速度快,掉电丢失。可以直接申请一块内存进行挂载,如: mount -t tmpfs -o size=2M tmpfs /tmp, 在linux(android系统)中,/dev, /apex,/debug_ramdisk常被用来作为挂载点。

sysfs

虚拟文件系统,也是存在于内存,这是内核为用户空间提供访问内核空间的一种接口,并且在该文件系统中,将设备和总线,模块相关信息组织起来,供用户查看,如block、bus、class、dev、devices、firmware、fs、kernel、module、power,一般挂载到/sys目录,

proc

虚拟文件系统,也是存在于内存,主要记录系统信息,如cpuinfo, meminfo, interrupt, cmdline,filesystems等信息。一般挂载到/proc目录,

selinxfs

虚拟文件系统,也是存在于内存,selinuxfs中提供了用户空间和内核安全模块的进行交互的接口, 如enforce就是用来配置selinux模式的节点。load节点用加载selinux策略二进制文件并写入内核,一般挂载到/sys/fs/selinux目录,

devpts

伪终端文件系统,用于模拟终端程序,一般挂载到/dev/pts目录,远程登陆(telnet,ssh等)后创建的控制台设备文件就在/dev/pts目录,该目录里面的文件是动态生成。

android fstab文件格式参考说明:

https://blog.csdn.net/baidu_40808339/article/details/108489583

其中rk3399 的如下:

qh100_rk3399:/ $ cat /vendor/etc/fstab.rk30board
# Android fstab file.
#<src>         <mnt_point>         <type>    <mnt_flags and options>  <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
system  /system   ext4 ro,barrier=1 wait,logical,first_stage_mount
vendor  /vendor   ext4 ro,barrier=1 wait,logical,first_stage_mount
odm     /odm      ext4 ro,barrier=1 wait,logical,first_stage_mount
product /product  ext4 ro,barrier=1 wait,logical,first_stage_mount
/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,discard,sync wait,formattable,first_stage_mount
/dev/block/by-name/misc         /misc               emmc      defaults     defaults
/dev/block/by-name/cache          /cache              ext4      noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard                wait,check

/devices/platform/*usb*   auto vfat defaults      voldmanaged=usb:auto

/dev/block/zram0                                none                swap      defaults
            zramsize=50%
# For sdmmc
/devices/platform/fe320000.dwmmc/mmc_host*        auto  auto    defaults        voldmanaged=sdcard1:auto,encryptable=userdata
#  Full disk encryption has less effect on rk3326, so default to enable this.
#/dev/block/by-name/userdata /data f2fs noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier latemount,wait,check,fileencryption=software,quota,formattable,reservedsize=128M,checkpoint=fs
# for ext4
/dev/block/by-name/userdata    /data      ext4    discard,noatime,nosuid,nodev,noauto_da_alloc,data=ordered,user_xattr,barrier=1    wait,formattable,check,fileencryption=software,quota,reservedsize=128M

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

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

相关文章

MAC地址申请MAC码购买流程MAC地址哪里申请MAC地址作用

众所周知mac地址号段全世界都是由美国一家政府机构IEEE进行分配的&#xff0c;也是由他们进行管理的。我站具有丰富的MAC地址申请经验&#xff0c;可以帮助客户有效的快速高效的完成MAC地址的申请。关于mac地址购买种类、范围、方式、费用详情如下。 一、服务项目&#xff1a;…

vue3、vite、pinia 快速入门

准备 开发工具及插件IDE:vscode,WebStorm插件&#xff1a;Auto Close Tag、Auto Rename Tag、Live Server通过“&#xff01;”快速生成html模板正式学习安装vue通过CDN的方式导入vue<script src"" target"_blank">https://unpkg.com/vue3/dist/vue.…

mysql数据库之索引分类

分类含义特点关键字主键索引针对于表中主键创建的索引默认自动创建&#xff0c;只能有一个primary唯一索引避免同一个表中某数据列中的值重复可以有多个unique常规索引快速定位特定数据可以有多个全文索引全文索引查找的是文本中的关键词&#xff0c;而不是比较索引中的值可以有…

01 Android基础--广播

01 Android基础--广播什么是广播&#xff1f;广播的分类本地广播LocalBroadcastManager什么是广播&#xff1f; 传输信息的机制。 主要记住三个要素&#xff1a;1.广播(Broadcast) - 用于发送广播; 2.广播接收器(BroadcastReceiver) - 用于接收广播&#xff1b;3.意图内容(Inte…

【活学活用掌握trap命令】

trap 命令用于指定在接收到信号后将要采取的动作&#xff0c;常见的用途是在脚本程序被中断时完成清理工作。当 shell 接收到 sigspec 指定的信号时&#xff0c; arg 参数(通常是执行命令)会被读取&#xff0c;并被执行。 1. 命令介绍 开始掌握基本的使用方式和方法 [1] 语法…

云计算简介

本文为copy他人编写的文档&#xff0c;由于不确认作者名称&#xff0c;故无法标记来源&#xff08;实际来源是群pdf文档&#xff09;&#xff0c;暂时发文为原创&#xff0c;因为无法贴出原文链接&#xff01; 云原生的前世今生 随着公有云和私有云的广泛部署&#xff0c;云计…

1.1 Linux开发环境搭建

1. Ubuntu服务器安装ssh工具&#xff0c;然后使用xshell连接 查看Ubuntu服务器的IP地址时&#xff0c;如果ifconfig失败&#xff0c;需要先安装一下net工具 sudo apt install openssh-server sudo apt install net-tools2. 打开vscode&#xff0c;安装Remote Development拓展…

操作系统——11.线程概念和多线程模型

这篇文章我们来讲一下操作系统中的线程概念和多线程模型 目录 1.概述 2.线程 2.1问题提出 2.2线程概念 2.3带来的变化 2.4线程的属性 2.5线程的实现方式 2.5.1用户级线程 2.5.2内核级线程 2.5.3相互组合 2.6多线程模型 2.6.1多对一模型 2.6.2一对一模型 2.6.3多…

操作系统权限提升(二十二)之Linux提权-SUDO滥用提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 操作系统权限提升(二十)之Linux提权-计划任务提权 操作系统权限提升(二十一)之Linux提权-环境变量劫持提权 SUDO滥用提权 SUDO滥用提权原理 sudo是linux系统管理指令&…

【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序

避坑宝 v1.0.0 基于SpringBootuniapp企业黑红名单吐槽小程序 &#x1f4da;项目介绍 避坑宝 【避坑宝】企业黑红名单吐槽小程序是一个具有吐槽发布企业信息的一个平台&#xff0c;言论自由&#xff0c;评判自定&#xff0c;便于我们打工人分辨企业好坏。技术栈基于SpringBoot…

墨者靶场--报错盲注

引入一下报错注入的知识点&#xff0c;参考【https://www.cnblogs.com/X-caiji/p/13186633.html】 输入测试语句1 and 11--,正常回显 1 and 12-- 无任何报错信息&#xff0c;但是可以确定我们输入的内容确实是被执行了。 那么可以考虑是盲注 一般在实战过程中就需要考虑各种各…

Elasticsearch集成之(Spring Data Elasticsearch)

Elasticsearch集成之&#xff08;Spring Data Elasticsearch&#xff09; 1、Spring Data 框架介绍 Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问&#xff0c;并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷&#xff0c;并支持 map-re…

php宝塔搭建部署实战织梦响应式人力资源网站模板源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套php开发的织梦响应式人力资源网站模板源码。感兴趣的朋友可以自行下载学习。 技术架构 PHP7 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&#xff0c;宝塔…

线下实践阿里云:「 云原生技术实践营 - 容器微服务专场 」

一、前言 自己在杭州工作和生活也有将近10年&#xff0c;由于有些前同事和朋友在阿里上班&#xff0c;也过去玩过几次。在印象中&#xff0c;作为联谊公司&#xff0c;还和阿里组织过一些小规模的活动&#xff0c;比如相亲和篮球比赛。 所以&#xff0c;对阿里杭州的滨江园区和…

2023年最新人工智能入门指南(学习清单)

ChatGPT最近很火&#xff0c;为了帮助想入门人工智能的小伙伴们准备了一份超详细的学习清单 前言 为帮助大家快速入门机器学习、深度学习&#xff0c;根据我自己多年的学习和工作经验&#xff0c;总结了这份保姆级的学习清单&#xff0c;让大家在学习时不走弯路。 这份清单主…

抽象队列同步器AQS应用lock详解

目录 AQS应用之Lock 并发之父 ReentrantLock 整体实现思路 AQS具备特性 同步等待队列 条件等待队列 AQS源码分析 AQS应用之Lock 并发之父 生平不识Doug Lea&#xff0c;学懂并发也枉然 Java并发编程核心在于java.util.concurrent包而juc当中的大多数同步器实现都是围绕…

linux 环境 postgresql + postgis 安装

postgis 是 postgresql 的一个扩展插件&#xff0c;具有强大的空间计算功能&#xff0c;很适合用于地图系统。本次演示使用的操作系统为CentOS Linux release 7.9.2009 (Core)postgis 下载登录 postgis 的官方网站&#xff1a;http://postgis.net/下载符合 postgresql 数据库版…

ShardingSphere官方文档感悟

ShardingSphere官方文档感悟什么是ShardingSphereShardingSphere-JDBCShardingSphere-Proxy功能产品优势设计哲学连接增强可插拔L1内核层L2功能层L3生态层部署形态ShardingSphere-JDBC独立部署ShardingSphere-Proxy独立部署混合部署架构官网地址&#xff1a; https://shardings…

HTTP的前世今生

承接上文HTTP请求的详细过程http协议版本历史http造就了万维网&#xff0c;http成就了互联网第三次信息技术革命并且影响着即将到来的第四次人工智能技术革命。1989年第一个http协议&#xff0c;http0.9发布&#xff0c;发明了万维网&#xff0c;创建了世界第一个网页浏览器&am…

软件测试面试必杀篇:【2023软件测试面试八股文宝典】

800道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web测试、接口测试、App测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维、人力资源等模块面试题&am…