1. 介绍

init进程是Linux系统第一个用户进程,是Android系统应用程序的根进程,即1号进程(PID为1);Android中的init文件位于/init,代码位于system/core/init目录

Linux中第一个进程为init_task,也即0号进程(PID为0),init进程由init_task进程fork而来,在kernel初始化完成后init_task便化身为idle进程

更多内核初始化init_task和init进程的信息,参考<Android 8.0 : 系统启动流程之Linux内核>

首先说明一下,笔者的代码分析基于Android 9.0

-------------------------------------------------------------------------------
| 镜像 | 内容 | 挂载点 | 加载方式 |
-------------------------------------------------------------------------------
| ramdisk.img | $(OUT)/root | / | 内核加载 |
| ramdisk-recovery.img | $(OUT)/recovery/root | / | |
| boot.img | $(OUT)/kernel + ramdisk.img | | |
| recovery.img | ramdisk-recovery.img | | |
| system.img | $(OUT)/system | /system | init加载 |
| userdata.img | $(OUT)/data | /data | init加载 |
-------------------------------------------------------------------------------

2. 云竹

    if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
} if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
} if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
} if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}

2.1 ueventd和watchdogd

在Android.mk中

LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd

这里是在/sbin/目录下创建init的软连接,因为ueventd和watchdogd应用的代码也位于init目录中,通过程序名称来决定运行的代码

而在system/core/rootdir/init.rc文件中

on early-init
...
start ueventd
...
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical

由此可见,ueventd会在init解析rc文件的early-init阶段被执行;而watchdogd由厂商来定制是否要运行

2.2 信号处理

当编译userdebug或者eng版本时,会在Android.mk中打开REBOOT_BOOTLOADER_ON_PANIC选项,该选项打开时会注册init进程的特殊信号处理函数,当init收到SIGABRT、SIGBUS、SIGSEGV等异常信号时Android系统将进入bootloader模式

3. 第一阶段

这里简单介绍下Android Device Tree(DT)

// CASE 1: 默认路径
/proc/device-tree/firmware/android // CASE 2: 内核命令行/proc/cmdline中定义
// androidboot.android_dt_dir=/sys/bus/platform/devices/ANDR0001:00/properties/android/
/sys/bus/platform/devices/ANDR0001:00/properties/android/

DT中定义了Android中初始化中需要的一些参数, 笔者该目录内容如下

# tree /sys/bus/platform/devices/ANDR0001:00/properties/android/
.
|---compatible // "android,firmware"
|---fstab
| |---compatible // "android,fstab"
| |---product
| | |---compatible // "android,product"
| | |---dev // "/dev/block/pci/pci0000:00/0000:00:1c.0/by-name/product"
| | |---fsmgr_flags // "wait,slotselect,avb"
| | |---mnt_flags // "ro"
| | |---type // "ext4"
| |---vendor
| | |---compatible // "android,vendor"
| | |---dev // "/dev/block/pci/pci0000:00/0000:00:1c.0/by-name/vendor"
| | |---fsmgr_flags // "wait,slotselect,avb"
| | |---mnt_flags // "ro"
| | |---type // "ext4"
|---vbmeta
| |---compatible // "android,vbmeta"
| |---parts // "vbmeta,boot,system,vendor,tos,product"

3.1 挂载文件系统

首先挂载了如下文件系统

----------------------------------------------
| 设备 | 类型 | 挂载目录 |
----------------------------------------------
| tmpfs | tmpfs | /dev |
| devpts | devpts | /dev/pts |
| proc | proc | /proc |
| sysfs | sysfs | /sys |
| selinuxfs | selinuxfs | /sys/fs/selinux |
| tmpfs | tmpfs | /mnt |
----------------------------------------------

创建如下文件夹

/dev/socket: 用于Android套接字

创建如下字符设备文件

/dev/kmsg:       1/11
/dev/kmsg_debug: 1/11
/dev/random: 1/8
/dev/urandom: 1/9

3.2 日志初始化

InitKernelLogging(argv)
// FIXME: 将[标准输入/标准输出/错误输出]重定向到/dev/null
open("/sys/fs/selinux/null", O_RDWR);
dup2(fd, 0/1/2); // 设置日志输出函数, 然后获取环境变量ANDROID_LOG_TAGS并解析从而设置最小输出等级
android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter)
// 将日志写入/dev/kmsg
android::base::KernelLogger

3.3 挂载分区

DoFirstStageMount()
// 检测DT的fstab配置是否支持, 如果支持则直接跳过挂载
android::init::is_android_dt_value_expected("fstab/compatible", "android,fstab")
// 获取Android DT(设备树)并获取$(DT)/fstab/compatible的值, 然后与android,fstab比较
android::init::read_android_dt_file("fstab/compatible", &dt_content) // 检测DT的vbmeta配置, 如果支持则使用FirstStageMountVBootV2, 否则使用FirstStageMountVBootV1
android::init::FirstStageMount::Create()
android::init::FirstStageMount::FirstStageMount()
// 获取$(DT)/fstab/目录定义的分区及属性, 笔者当前包含product和vendor
fs_mgr_read_fstab_dt()
read_fstab_from_dt()
fs_mgr_read_fstab_file()
/*
* 读取fstab
* - 首先尝试$(DT)/boot_devices
* - 不成功则依次尝试
* /odm/etc/fstab.$(platform)
* /vendor/etc/fstab.$(platform)
* /fstab.$(platform)
*/
fs_mgr_get_boot_devices()
// 获取$(DT)/vbmeta/parts的值, 当前为: vbmeta,boot,system,vendor,tos,product
android::init::FirstStageMountVBootV2::FirstStageMountVBootV2() // 挂载fastb中的分区列表
android::init::FirstStageMount::DoFirstStageMount()
android::init::FirstStageMount::InitDevices()
android::init::FirstStageMount::GetRequiredDevices()
android::init::FirstStageMount::InitRequiredDevices()
// 依次挂载各分区
android::init::FirstStageMount::MountPartitions()
android::init::FirstStageMount::SetUpDmVerity()
fs_mgr_do_mount_one()

3.4 AVB初始化

SetInitAvbVersionInRecovery()
// 如果不是Revery模式则直接返回
IsRecoveryMode()
// 如果不兼容vbmeta则直接返回
IsDtVbmetaCompatible()
 
    android::init::FirstStageMountVBootV2()
// 获取vbmeta/parts对应的分区
read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)
// 初始化设备
    android::init::FirstStageMount::InitDevices()
// 获取AVB Handler
FsManagerAvbHandle::Open(FirstStageMountVBootV2::by_name_symlink_map_)
// 设置环境变量INIT_AVB_VERSION
setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1)

AVB(Android Verified Boot),主要用于防止系统文件本身被篡改,还包含了防止系统回滚的功能

3.5 seccomp初始化

// 此处决定是否使能全局seccomp; 如果没有使能, zygote也会使能
// 读取/proc/cmdline内容, 如果包含androidboot.seccomp=global则调用set_global_seccomp_filter
// set_global_seccomp_filter位于bionic/libc/seccomp/seccomp_policy.cpp
global_seccomp()

3.6 SELinux初始化

// 将selinux日志重定向到内核日志输出, 即/dev/kmsg
SelinuxSetupKernelLogging() SelinuxInitialize()
/*
* 加载SELinux策略
* 如果IsSplitPolicyDevice返回true, 执行LoadSplitPolicy, 否则执行LoadMonolithicPolicy
*/
android::init::LoadPolicy()
// 检测/system/etc/selinux/plat_sepolicy.cil文件是否存在
android::init::IsSplitPolicyDevice()
android::init::LoadSplitPolicy()
/*
* 依次查找以下预编译SELinux文件
* "/vendor/etc/selinux/precompiled_sepolicy"
* "/odm/etc/selinux/precompiled_sepolicy"
*/
android::init::FindPrecompiledSplitPolicy(&file)
open(file, O_RDONLY | O_CLOEXEC | O_BINARY)
selinux_android_load_policy_from_fd(fd, file)
set_selinuxmnt("/sys/fs/selinux")
mmap(NULL, , PROT_READ, MAP_PRIVATE, fd, 0)
// 将策略文件写入到 /sys/fs/selinux/load
security_load_policy(,)
// 从文件/sepolicy加载策略
android::init::LoadMonolithicPolicy()
selinux_android_load_policy()
open("/sepolicy", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)
selinux_android_load_policy_from_fd(fd, "/sepolicy") /*
* 设置selinux状态
* 如果内核selinux状态与自定义selinux状态进行比较, 最终以自定义selinux状态为准
*/
// 获取内核selinux状态
security_getenforce()
// 获取Android自定义selinux状态
android::init::IsEnforcing()
// 如果未定义ALLOW_PERMISSIVE_SELINUX, 直接返回Enforcing, 否则才调用StatusFromCmdline
// 读取/proc/cmdline内容, 如果包含androidboot.selinux=permissive则返回Permissive
android::init::StatusFromCmdline()
// 设置当前selinux状态, 其实是将0/1写入/sys/fs/selinux/enforce
security_setenforce() // 设置checkreqprot为0, 该值决定SELinux通过程序(1)还是通过内核(0)响应进行安全检查
WriteFile("/sys/fs/selinux/checkreqprot", "0") // 获取SELinux策略加载时间并设置到设置环境变量INIT_SELINUX_TOOK
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); // 将init文件的Context恢复成file_contexts中的初始配置
selinux_android_restorecon("/init", 0)

SEAndroid整体架构图如下图所示

3.7 第二阶段准备

    // 将环境变量INIT_SECOND_STAGE设置为1, 准备进入第二阶段
setenv("INIT_SECOND_STAGE", "true", 1); // 设置环境变量INIT_STARTED_AT
setenv("INIT_STARTED_AT", start_ms, 1); // 重新执行/init
execv("/init", { /init, nullptr });

4. 第二阶段

4.1 创建密钥环

keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

关于密钥环,参考:
<Linux密钥保留服务入门>
<Kernel Key Retention Service>

4.2 启动准备

close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

创建/dev/.booting文件,用于标识当前正在启动中,当firmware_mounts_complete被触发时删除

4.3 Property服务初始化

property_init()
// 创建目录: O/RWX、G/X、O/X
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH) // 创建序列化的System Property
CreateSerializedPropertyInfo()
// 读取System Property并加载到PropertyInfoEntry
LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts", &e)
LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts", &e)
LoadPropertyInfoFromFile(/vendor/etc/selinux/nonplat_property_contexts", &e)
// 将加载的System Property序列化
BuildTrie(e, "u:object_r:default_prop:s0", "string", &s)
// 将序列化的System Property写入文件
WriteStringToFile(s, "/dev/__properties__/property_info",,,,)
// 恢复property_info文件的Context
selinux_android_restorecon("/dev/__properties__/property_info", 0) // 初始化System Property内存区域
__system_property_area_init()
SystemProperties::AreaInit("/dev/__properties__", false)
ContextsSerialized::Initialize(true, "/dev/__properties__", false)
ContextsSerialized::InitializeProperties()
// 加载System Property默认路径
android::properties::PropertyInfoAreaFile::LoadDefaultPath()
// 将文件加载到内存
LoadPath("/dev/__properties__/property_info")
// 初始化ContextNode
ContextsSerialized::InitializeContextNodes()
// 获取ContextNode数量
android::properties::PropertyInfoAreaFile::num_contexts()
// 创建num_contexts * ContextNode大小共享内核
mmap(, , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS , -1, 0)
// 创建ContextNode
ContextNode(PropertyInfoAreaFile::context(i), "/dev/__properties__")
// 将所有property对应的Security Context写入文件
ContextNode::Open(true, false)
// 将property序列化并保持至/dev/__properties/properties_serial
ContextsSerialized::MapSerialPropertyArea(true, false)

以下是初始化完成后/dev/__properties__目录文件列表

# ls -al /dev/__properties__
drwx--x--x 2 root root 2360 2011-11-11 19:11 .
drwxr-xr-x 18 root root 4820 2011-11-11 19:11 ..
-r--r--r-- 1 root root 131072 2011-11-11 19:11 properties_serial
-r--r--r-- 1 root root 29660 2011-11-11 19:11 property_info
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:audio_prop:s0
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:bluetooth_prop:s0
...
-r--r--r-- 1 root root 131072 2019-10-01 08:00 u:object_r:system_prop:s0
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:system_radio_prop:s0
...
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:vendor_default_prop:s0
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:vendor_persist_prop:s0
...
-r--r--r-- 1 root root 131072 2011-11-11 19:11 u:object_r:wifi_prop:s0

4.4 处理设备树和命令行

    /*
* DT中的属性优先级高于内核命令行的属性
*/
// 处理Android设备树中定义的属性
process_kernel_dt();
// 检测是否兼容Android, 否则直接返回
is_android_dt_value_expected("compatible", "android,firmware") // 读取/proc/cmdline, 将androidboot.name=value的键值对保存为ro.boot.name=value属性
process_kernel_cmdline();
import_kernel_cmdline(false, )
import_kernel_nv(key, value)
property_set("ro.boot." + key.substr(12), value)

4.5 RO属性相关

    /*
* 将部分ro.boot.*属性转换为ro.*属性, 前者值不存在则设置默认值
* { "ro.boot.serialno", "ro.serialno", "", },
* { "ro.boot.mode", "ro.bootmode", "unknown", },
* { "ro.boot.baseband", "ro.baseband", "unknown", },
* { "ro.boot.bootloader", "ro.bootloader", "unknown", },
* { "ro.boot.hardware", "ro.hardware", "unknown", },
* { "ro.boot.revision", "ro.revision", "0", },
*/
export_kernel_boot_props() // 将第一阶段设置的环境变量设置为property属性
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
property_set("ro.boot.avb_version", getenv("INIT_AVB_VERSION")) // 清空环境变量
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");

4.6 SELinux再次初始化

// 将selinux日志重定向到内核日志输出, 即/dev/kmsg
SelinuxSetupKernelLogging(); // FIXME:
SelabelInitialize();
selinux_android_file_context_handle()
selinux_android_set_sehandle() // 恢复file_contexts中的初始配置
SelinuxRestoreContext();
selinux_android_restorecon("/dev", 0);
selinux_android_restorecon("/dev/kmsg", 0);
...

4.7 EPOLL实例

// 创建EPOLL
epoll_create1(EPOLL_CLOEXEC) /*
* 创建一个相互连接的套接字对
* 接收SIGCHLD信号时往其中一个套接字写
* 另一个套接字的读则注册到EPOLL中
*/
sigchld_handler_init()
socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s)
act.sa_handler = SIGCHLD_handler;
|-> write(signal_write_fd, "1", 1)
sigaction(SIGCHLD, &act, 0);
register_epoll_handler(signal_read_fd, handle_signal);
read(signal_read_fd, buf, sizeof(buf)) <-|
ReapAnyOutstandingChildren() <-| /*
* 添加SIGTERM信号处理函数, 并注册到EPOLL中
*/
InstallSigtermHandler()
sigaddset(&mask, SIGTERM);
sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
read(sigterm_signal_fd, &, ) <-|
HandlePowerctlMessage("shutdown,container") <-|

EPOLL的使用方法如下:

- epoll_create/epoll_create1创建一个EPOLL实例
- epoll_ctl向EPOLL实例注册/修改/删除监听事件
- epoll_wait等待事件发生
- close关闭EPOLL实例

4.8 Property相关服务

// 加载系统启动属性
property_load_boot_defaults();
load_properties_from_file("/system/etc/prop.default", NULL)
load_properties_from_file("/product/build.prop", NULL)
load_properties_from_file("/odm/default.prop", NULL)
load_properties_from_file("/vendor/default.prop", NULL)
ReadFile()
LoadProperties()
// 将property键值对设置到系统中
HandlePropertySet(key, value, ,)
// 设置persist.sys.usb.config属性值
update_sys_usb_config() /*
* OEM Lock和AVB相关, 涉及如下属性
* ro.oem_unlock_supported
* ro.boot.verifiedbootstate
* ro.boot.flash.locked
*/
export_oem_lock_status(); // 启动属性系统服务
start_property_service();
// 创建socket, 用于监听写系统属性的请求
CreateSocket("property_service")
lisent(fd, 8)
// 注册到EPOLL中
register_epoll_handler(fd, handle_property_set_fd)
HandlePropertySet(key, value)<-| // 设置sys.usb.controller属性值
set_usb_controller();

下一篇请参考<Android init介绍(下)>

Android init介绍(上)的更多相关文章

  1. Android init介绍(下)

    上一篇请参考<Android init介绍(上)> 5. AIL 在init启动过程中,系统服务等均是通过解析rc文件来启动,而rc文件则是由Android初始化语言(Android In ...

  2. Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08系统:an ...

  3. android Animation介绍

    Animation介绍: 在Android SDK介绍了2种Animation模式: 1. Tween Animation:间动画,通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果,即 ...

  4. android init进程分析 ueventd

    转自:http://blog.csdn.net/freshui/article/details/2132299 (懒人最近想起我还有csdn好久没打理了,这个Android init躺在我的草稿箱中快 ...

  5. 开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发

    [原][开源框架]Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位... 时间 2015-01-05 10:08:18 我是程序猿,我为自己代言 原文  http: ...

  6. Android monkey介绍

    Android monkey介绍 原文地址 1 简略 monkey是android下自动化测试比较重要的的一个工具,该工具可以运行在host端或者设备(模拟器或真实设备).它会向系统发送随机事件流(即 ...

  7. Android init.rc文件格式解析

    /***************************************************************************** * Android init.rc文件格式 ...

  8. [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍

    注:为了看上去比较清晰这里只转载了中文 原地址:  [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍 本章将引导您完成安装和设置开发环境,然后你就可 ...

  9. 菜鸟Android之路(上)

    自己为什么要学android 本人作为应届毕业生,自己进入社会前做过好多梦,可是呢,现实还是打败了无邪!!面对社会的压力和残酷的竞争力自己如何生成下去??我自己对自己说:第一步 先养活自己,才能走好以 ...

随机推荐

  1. [ARM-Linux开发]Linux open函数

    Linux open函数 open 函数用于打开和创建文件.以下是 open 函数的简单描述 #include <fcntl.h> int open(const char *pathnam ...

  2. 「杂录」CSP-S 2019 爆炸记&题解

    考试状况 \(Day1\) \(8:30\) 解压,先打个含头文件和\(freopen\)的模板程序,准备做题. \(8:35\) 开题,心想着按顺序做吧,毕竟难度一般是按顺序排的. 第一题,一眼看过 ...

  3. pandas 模块

    什么是pandas pandas是一个python的包,主要用来处理表格格式的文件,可以快速的对表格进行查询,过滤,合并等操作. pandas的简单使用 pandas读入table格式文件 #读入一个 ...

  4. 【LOJ502】[LibreOJ β Round] ZQC 的截图 (随机化)

    真的是神仙题目啊-- 题目 LOJ502 官方题解 我认为官方题解比我讲得好. 分析 这是一道蒙特卡洛算法的好题 上面那个奇奇怪怪的词是从官方题解里看到的,意思大概就是随机化算法 -- ? 一句话题意 ...

  5. 袁新生《LINGO和Excel在数学建模中的应用》

    内容介绍 本书深入浅出地介绍了LINGO的基础知识.用LINGO语言描述现实问题的方法和用Excel处理数据的方法,重点是这两种软件在解决各种优化问题以及在数学建模中的应用,通过丰富的实例介绍了把实际 ...

  6. Python之路【第二十二篇】:轮播图片CSS

    轮播代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  7. LInux基础(04)项目设计一(理解链表管理协议的代码架构)

    要设计好一个项目必须要有一个健全的代码框架 一个结构体内有数据域和处理数据的函数指针, 先实现管理链表的函数 增加节点  删除节点  清空链表  遍历节点对每个节点进行操作 再实现协议的注册 把对象s ...

  8. Flutter 增加三方库卡在flutter package get 的解决办法

    修改 pubspec.yaml 文件增加第三方库之后,AndroidStudio 像往常一样提示 需要 package get. 然后一直卡在 Running "flutter packag ...

  9. vim 自定义设置

    修改系统配置(面对所有用户): root@bogon:~# cd /etc/vim/ root@bogon:/etc/vim# ls vimrc vimrc.tiny root@bogon:/etc/ ...

  10. 解决clover配置文件conf.plist中nv_disable=1或者nvda_drv=1不生效或者说不能删除的问题

    情况一:U盘安装MacOS的时候conf.plist中设置了nv_disable=1,但是启动的时候任然显示nvda_drv=1. 这种情况一般出现在该机器曾经安装过MacOS,MacOS会将这个参数 ...