当前位置: 首页 > news >正文

Android 12 S 系统开机流程分析 - SecondStageMain(三)

Android 12 S 系统开机流程分析-FirstStageMain(一)

Android 12 S 系统开机流程分析 - SetupSelinux(二)

本文接着上文开始往下讲 

1. SecondStageMain

此时就到了init的第二阶段。

int main(int argc, char** argv) {
...
//执行此处if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}}return FirstStageMain(argc, argv);
}
3.3.1  初始化log系统并设置环境变量

1)InstallRebootSignalHandlers中主要做了以下几件事:

       1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。

       2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot

       3. 通过syscall向内核发送重启命令

       4. 捕获一些信号

2)初始化kernel log

int SecondStageMain(int argc, char** argv) {if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}boot_clock::time_point start_time = boot_clock::now();trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };SetStdioToDevNull(argv);
//初始化kernel logInitKernelLogging(argv);LOG(INFO) << "init second stage started!";//设置环境变量if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";}
3.3.2 设置init和它的子进程的oom_adj
//设置init和它的子进程的 oom_adj. 防止被杀,adj越小越不容易被杀
//static const int MIN_OOM_SCORE_ADJUST = -1000;
//static const int MAX_OOM_SCORE_ADJUST = 1000;
//static const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST;if (auto result =WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));!result.ok()) {LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST<< " to /proc/1/oom_score_adj: " << result.error();}
3.3.3 
    // Set up a session keyring that all processes will have access to. It// will hold things like FBE encryption keys. No process should override// its session keyring.keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);// Indicate that booting is in progress to background fw loaders, etc.close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));// See if need to load debug props to allow adb root, when the device is unlocked.const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");bool load_debug_prop = false;
//是debug版本+设备unlockif (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {load_debug_prop = "true"s == force_debuggable_env;}
//清除环境变量unsetenv("INIT_FORCE_DEBUGGABLE");// Umount the debug ramdisk so property service doesn't read .prop files from there, when it// is not meant to.if (!load_debug_prop) {UmountDebugRamdisk();}
3.3.4 初始化property
    PropertyInit();

该函数主要是加载以下prop文件:

/system/build.prop, /system_ext/etc/build.prop, /system_ext/etc/default.prop, /vendor/default.prop, /vendor/build.prop, /vendor_dlkm/etc/build.prop, /odm_dlkm/etc/build.prop
/odm/etc/build.prop, /odm/etc/default.prop, /product/etc/build.prop /product/etc/default.prop,/debug_ramdisk/adb_debug.prop

void PropertyInit() {selinux_callback cb;cb.func_audit = PropertyAuditCallback;selinux_set_callback(SELINUX_CB_AUDIT, cb);mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);CreateSerializedPropertyInfo();if (__system_property_area_init()) {LOG(FATAL) << "Failed to initialize property area";}if (!property_info_area.LoadDefaultPath()) {LOG(FATAL) << "Failed to load serialized property info file";}// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.ProcessKernelDt();ProcessKernelCmdline();ProcessBootconfig();// Propagate the kernel variables to internal variables// used by init as well as the current required properties.ExportKernelBootProps();PropertyLoadBootDefaults();
}

 加载各个目录下的prop文件

void PropertyLoadBootDefaults() {// We read the properties and their values into a map, in order to always allow properties// loaded in the later property files to override the properties in loaded in the earlier// property files, regardless of if they are "ro." properties or not.std::map<std::string, std::string> properties;if (IsRecoveryMode()) {load_properties_from_file("/prop.default", nullptr, &properties);}
----------------------------------------------------------------------
//定义load_properties_from_partition 函数const auto load_properties_from_partition = [&properties](const std::string& partition,int support_legacy_path_until) {auto path = "/" + partition + "/etc/build.prop";if (load_properties_from_file(path.c_str(), nullptr, &properties)) {return;}// To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a// separate map. Then by comparing its value with legacy_version, we know that if the// partition is old enough so that we need to respect the legacy paths.std::map<std::string, std::string> temp;auto legacy_path1 = "/" + partition + "/default.prop";auto legacy_path2 = "/" + partition + "/build.prop";load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);bool support_legacy_path = false;auto version_prop_name = "ro." + partition + ".build.version.sdk";auto it = temp.find(version_prop_name);if (it == temp.end()) {// This is embarassing. Without the prop, we can't determine how old the partition is.// Let's be conservative by assuming it is very very old.support_legacy_path = true;} else if (int value;ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {support_legacy_path = true;}if (support_legacy_path) {// We don't update temp into properties directly as it might skip any (future) logic// for resolving duplicates implemented in load_properties_from_file.  Instead, read// the files again into the properties map.load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);} else {LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "<< "because " << version_prop_name << "(" << it->second << ") is newer "<< "than " << support_legacy_path_until;}};
--------------------------------------------------------------------------// Order matters here. The more the partition is specific to a product, the higher its// precedence is.LoadPropertiesFromSecondStageRes(&properties);load_properties_from_file("/system/build.prop", nullptr, &properties);load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);// TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are// all updated.// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {load_properties_from_file("/vendor/default.prop", nullptr, &properties);// }load_properties_from_file("/vendor/build.prop", nullptr, &properties);load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
//load /odm/etc/build.propload_properties_from_partition("odm", /* support_legacy_path_until */ 28);
//load /product/etc/build.propload_properties_from_partition("product", /* support_legacy_path_until */ 30);if (access(kDebugRamdiskProp, R_OK) == 0) {LOG(INFO) << "Loading " << kDebugRamdiskProp;load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);}for (const auto& [name, value] : properties) {std::string error;if (PropertySet(name, value, &error) != PROP_SUCCESS) {LOG(ERROR) << "Could not set '" << name << "' to '" << value<< "' while loading .prop files" << error;}}property_initialize_ro_product_props();property_initialize_build_id();property_derive_build_fingerprint();property_derive_legacy_build_fingerprint();property_initialize_ro_cpu_abilist();update_sys_usb_config();
}
3.3.5 
    // Umount second stage resources after property service has read the .prop files.UmountSecondStageRes();// Umount the debug ramdisk after property service has read the .prop files when it means to.if (load_debug_prop) {UmountDebugRamdisk();}// Mount extra filesystems required during second stage initMountExtraFilesystems();
3.3.6 Selinux标签restore
    // Now set up SELinux for second stage.SelinuxSetupKernelLogging();SelabelInitialize();SelinuxRestoreContext();

自启动的服务都是经过init进行域转换的,在转换之前标签是携带_exec的,在转化之后则不携带此后缀,所以是需要在sepolicy策略中增加init_daemon_domain的。

例如:如果/system/bin/testsh需要自启动,则需要在testsh_exec.te(此文件新增,根据selinux要求

创建,以标签名+.te文件命名)中添加如下内容

file_contexts

+ /system/bin/testsh0    u:object_r:testsh0_exec:s0

testsh_exec.te

+typeattribute testsh0 coredomain;

+type testsh0_exec, exec_type, file_type, system_file_type,;

+init_daemon_domain(testsh0)

init_daemon_domain定义如下

define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')
------------------------------------------------------------
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')
------------------------------------------------------------
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr map };
# New domain can send SIGCHLD to its caller.
ifelse($1, `init', `', `allow $3 $1:process sigchld;')
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')

从定义就可以清楚的知道init在这其中所起到的作用了,以及自启动的bin是怎样将自己的标签转化的。

有一个知识点,比如新增了一个Selinux标签,并将相应文件push进了手机:

比如新增如下内容

file_contexts

+ /system/bin/testsh    u:object_r:testsh_exec:s0

file.te

+ type testsh_exec, exec_type, file_type, system_file_type,;

执行make selinux_policy编译selinux模块

adb push system/etc/selinux  /system_ext/etc/

adb reboot

如果新增了文件的标签,会在手机的/system/etc/selinux目录下的system_file_contexts中可以查看新增的标签。但是去/system/bin下执行命令ls -l -Z会发现,新增的标签并没有效果,此时如果在system/bin下执行

restorecon testsh
即
restorecon + 目录名

然后再执行ls -l -Z就会发现新增的标签生效了,这个restorecon的在此时作用就是重新加载一下selinux的标签。对于节点和文件系统的新增标签都可以执行此命令,如果不执行不会重新加载,重启也不会重新加载,此命令如果遇到重启,需要再重新执行一次。当然,如果能够编译boot.img镜像或者刷机就不用执行以上步骤了,此步骤只针对push的场景。

restorecon还有很多其他的功能,如果想了解,可以去百度一下。

3.3.7 启动属性服务
    Epoll epoll;if (auto result = epoll.Open(); !result.ok()) {PLOG(FATAL) << result.error();}InstallSignalFdHandler(&epoll);InstallInitNotifier(&epoll);StartPropertyService(&property_fd);
void StartPropertyService(int* epoll_socket) {InitPropertySet("ro.property_service.version", "2");int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {PLOG(FATAL) << "Failed to socketpair() between property_service and init";}*epoll_socket = from_init_socket = sockets[0];init_socket = sockets[1];StartSendingMessages();
//创建property service的socketif (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, {});result.ok()) {property_set_fd = *result;} else {LOG(FATAL) << "start_property_service socket creation failed: " << result.error();}listen(property_set_fd, 8);auto new_thread = std::thread{PropertyServiceThread};property_service_thread.swap(new_thread);
}
 3.3.8 
    // Make the time that init stages started available for bootstat to log.RecordStageBoottimes(start_time);// Set libavb version for Framework-only OTA match in Treble build.if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {SetProperty("ro.boot.avb_version", avb_version);}unsetenv("INIT_AVB_VERSION");fs_mgr_vendor_overlay_mount_all();export_oem_lock_status();MountHandler mount_handler(&epoll);SetUsbController();const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();Action::set_function_map(&function_map);if (!SetupMountNamespaces()) {PLOG(FATAL) << "SetupMountNamespaces failed";}InitializeSubcontext();
3.3.9 加载rc文件
    ActionManager& am = ActionManager::GetInstance();ServiceList& sm = ServiceList::GetInstance();
//加载init.rc文件LoadBootScripts(am, sm);// Turning this on and letting the INFO logging be discarded adds 0.2s to// Nexus 9 boot time, so it's disabled by default.if (false) DumpState();// Make the GSI status available before scripts start running.auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";SetProperty(gsi::kGsiBootedProp, is_running);auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";SetProperty(gsi::kGsiInstalledProp, is_installed);am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");am.QueueEventTrigger("early-init");// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");// ... so that we can start queuing up actions that require stuff from /dev.am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");Keychords keychords;am.QueueBuiltinAction([&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {for (const auto& svc : ServiceList::GetInstance()) {keychords.Register(svc->keycodes());}keychords.Start(&epoll, HandleKeychord);return {};},"KeychordInit");// Trigger all the boot actions to get us started.am.QueueEventTrigger("init");// Don't mount filesystems or start core system services in charger mode.std::string bootmode = GetProperty("ro.bootmode", "");if (bootmode == "charger") {am.QueueEventTrigger("charger");} else {am.QueueEventTrigger("late-init");}// Run all property triggers based on current state of the properties.am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
//解析/system/etc/init/hw/init.rc
//将/system/etc/init,/system_ext/etc/init,/vendor/etc/init,/odm/etc/init
//和/product/etc/init存入late_import_paths
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("/vendor/etc/init")) {late_import_paths.emplace_back("/vendor/etc/init");}if (!parser.ParseConfig("/odm/etc/init")) {late_import_paths.emplace_back("/odm/etc/init");}if (!parser.ParseConfig("/product/etc/init")) {late_import_paths.emplace_back("/product/etc/init");}} else {parser.ParseConfig(bootscript);}
}

相关文章:

  • 一封来自未来的offer
  • Vite探索:构建、启程、原理、CSS艺术与插件魔法
  • CSS常用示例100+ 【目录】
  • MyBatis 知识总结
  • 从Docker Hub获取镜像和创建容器
  • 绩效管理系统有哪些?
  • 新生儿奶瓣:原因、科普和注意事项
  • 线性代数-Python-05:矩阵的逆+LU分解
  • 【vue2】vue2 适配pc端,解决浏览器缩放问题,解决电脑显示设置缩放、分辨率问题
  • opencv车牌识别<一>
  • 时钟丢失监控机制
  • 动态规划、回溯搜索、分治算法、分支定界算法
  • 如何判断从本机上传到服务器的文件数据内容是一致的?用md5加密算法!
  • Command PhaseScriptExecution failed with a nonzero exit code
  • Stable Diffusion 是否使用 GPU?
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 【React系列】如何构建React应用程序
  • CSS实用技巧干货
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • es6要点
  • Hibernate【inverse和cascade属性】知识要点
  • overflow: hidden IE7无效
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • SpriteKit 技巧之添加背景图片
  • vue-cli3搭建项目
  • zookeeper系列(七)实战分布式命名服务
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 力扣(LeetCode)357
  • 入门级的git使用指北
  • 使用Swoole加速Laravel(正式环境中)
  • 数据结构java版之冒泡排序及优化
  • 通过npm或yarn自动生成vue组件
  • 一道面试题引发的“血案”
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​flutter 代码混淆
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (备忘)Java Map 遍历
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (附源码)springboot教学评价 毕业设计 641310
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (一)Thymeleaf用法——Thymeleaf简介
  • *** 2003
  • ***检测工具之RKHunter AIDE
  • .NET BackgroundWorker
  • .Net Core与存储过程(一)
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET企业级应用架构设计系列之应用服务器
  • .NET应用架构设计:原则、模式与实践 目录预览
  • [AIGC] SQL中的数据添加和操作:数据类型介绍
  • [android] 切换界面的通用处理