深入了解Android读书笔记——深入理解init

171 阅读3分钟

源码Android10

init 概述

init进程是Linux系统中用户空间的第一个进程,进程号固定为1。启动代码在 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;

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 --> SetupSelinux --> SecondStageMain

  • FirstStageMain : 挂载及创建基本的文件系统,并设置合适的访问权限; 关闭默认控制台输出,并初始化内核级log; 挂载 system、cache、data 等系统分区

  • SetupSelinux : 设置SELinux安全策略

  • SecondStageMain : 核心步骤,下面详细解释

SecondStageMain

SecondStageMain 方法在 system/core/init/init.cpp 中,下面只列出了核心代码


int SecondStageMain(int argc, char** argv) {

...

//初始化属性服务,并从指定文件读取属性

property_init();

...

//epoll 中注册signalfd,主要是为了创建handler处理子进程终止信号(SIGCHLD)

InstallSignalFdHandler(&epoll);

...

//解析init.rc文件,文件在 system/core/rootdir 目录下

LoadBootScripts(am, sm);

...

//执行rc文件中触发器为on early-init的语句

am.QueueEventTrigger("early-init");

...

//执行rc文件中触发器为on init的语句

am.QueueEventTrigger("init");

...

// 当处于充电模式,则charger加入执行队列;否则late-init加入队列。

std::string bootmode = GetProperty("ro.bootmode", "");

if (bootmode == "charger") {

am.QueueEventTrigger("charger");

} else {

am.QueueEventTrigger("late-init");

}

...

while (true) {

// By default, sleep until something happens.

auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

if (do_shutdown && !shutting_down) {//关机重启

do_shutdown = false;

if (HandlePowerctlMessage(shutdown_command)) {

shutting_down = true;

}

}

if (!(waiting_for_prop || Service::is_exec_service_running())) {//?

am.ExecuteOneCommand();

}

if (!(waiting_for_prop || Service::is_exec_service_running())) {//重启进程

if (!shutting_down) {

auto next_process_action_time = HandleProcessActions();

// If there's a process that needs restarting, wake up in time for that.

if (next_process_action_time) {

epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(

*next_process_action_time - boot_clock::now());

if (*epoll_timeout < 0ms) epoll_timeout = 0ms;

}

}

// If there's more work to do, wake up again immediately.

if (am.HasMoreCommands()) epoll_timeout = 0ms;

}

if (auto result = epoll.Wait(epoll_timeout); !result) {//循环等待事件发生

LOG(ERROR) << result.error();

}

}

return 0;

}

属性服务

一般而言,系统或者某些应用程序会把自己的一些属性存储在注册表中(注册表可以存储一些类似key/value的键值对),即使系统重启或者应用程序重启,它还能根据之前在注册表中设置的属性,进行相应的初始化工作。Android平台也提供了类似的机制,称为属性服务。应用程序可以通过这个属性机制查询或者设置属性。

property_init()system/core/init/property_service.cpp 文件中

信号处理

在Linux内核中,如父进程不等待子进程的结束直接退出,会导致子进程在结束后变成僵尸进程,占用系统资源。为此,init进程专门安装了SIGCHLD信号接收器,当某些子进程退出时发现其父进程已经退出,则会向init进程发送SIGCHLD信号,init进程调用回调方法handle_signal()来回收僵尸子进程。

init.rc

nit.rc文件在 system/core/rootdir 目录下

Action

Action: 通过触发器trigger,即以on开头的语句来决定执行相应的service的时机,具体有如下时机:

on early-init; 在初始化早期阶段触发;

on init; 在初始化阶段触发;

on late-init; 在初始化晚期阶段触发;

on boot/charger: 当系统启动/充电时触发,还包含其他情况,此处不一一列举;

on property:=: 当属性值满足条件时触发;

Service

服务Service,以 service开头,由init进程启动,一般运行在init的一个子进程,所以启动service前需要判断对应的可执行文件是否存在。init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。

例如: service servicemanager /system/bin/servicemanager代表的是服务名为servicemanager,服务执行的路径为/system/bin/servicemanager。

Command

下面列举常用的命令

class_start <service_class_name>: 启动属于同一个class的所有服务;

start <service_name>: 启动指定的服务,若已启动则跳过;

stop <service_name>: 停止正在运行的服务

setprop :设置属性值

mkdir :创建指定目录

symlink <sym_link>: 创建连接到的<sym_link>符号链接;

write : 向文件path中写入字符串;

exec: fork并执行,会阻塞init进程直到程序完毕;

exprot :设定环境变量;

loglevel :设置log级别

trigger command :触发命令

Options

Options是Service的可选项,与service配合使用

disabled: 不随class自动启动,只有根据service名才启动;

oneshot: service退出后不再重启;

user/group: 设置执行服务的用户/用户组,默认都是root;

class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;

onrestart:当服务重启时执行相应命令;

socket: 创建名为/dev/socket/的socket

critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

default: 意味着disabled=false,oneshot=false,critical=false。

参考

gityuan.com/2016/02/05/…

www.imgeek.org/article/825…

juejin.cn/post/699844…