OpenHarmony启动子系统(init)
启动恢复子系统除负责加载各系统关键进程之外,还需在启动的同时设置其对应权限,并在子进程启动后对指定进程实行保活(若进程意外退出要重新启动),对于特殊进程意外退出时,启动恢复子系统还要执行系统复位操作。OHOS的启动框架图如下图所示。在kernel中启动的/init进程,实际上是一个软链接,指向了/bin/init_early;查看//base/startup/init/services/init/
子系统架构
init组件处理从内核加载一个用户态进程开始,到第一个应用程序启动之间的系统服务进程启动过程。启动恢复子系统除负责加载各系统关键进程之外,还需在启动的同时设置其对应权限,并在子进程启动后对指定进程实行保活(若进程意外退出要重新启动),对于特殊进程意外退出时,启动恢复子系统还要执行系统复位操作。OHOS的启动框架图如下图所示。

子系统的目录结构如下:
base/startup/init/
├── device_info # 提供设备信息的SA服务
├── initsync # 同步命令(小型系统)
├── interfaces # 对外接口
├── scripts # 脚本(LiteOS系统使用)
├── services
│ ├── begetctl # 命令集合。提供服务的dump,拉起等一系列命令
│ ├── etc # init配置文件目录(标准系统)
│ ├── etc_lite # init配置文件目录(小型系统)
│ ├── include # init头文件目录
│ ├── init # init核心功能源码
│ │ ├── adapter # 内核适配层
│ │ ├── include # 头文件目录
│ │ ├── lite # init核心功能源码(小型系统)
│ │ └── standard # init核心功能源码(标准系统)
│ ├── log # init日志部件。
│ ├── loopevent # 事件库
│ │ ├── include # 头文件目录
│ │ ├── loop # 基于epoll封装的I/O多路复用接口
│ │ ├── signal # 信号处理接口封装。提供信号的添加,handler注册等功能
│ │ ├── socket # socket通信接口
│ │ ├── task # 事件的抽象任务,如signal, timer等事件都要创建对应task
│ │ ├── timer # 定时器接口
│ │ └── utils # loopevent通用接口
│ ├── modules # 插件化模块
│ │ ├── bootchart # bootchart插件化源码
│ │ ├── bootevent # bootevent插件化源码
│ │ ├── init_hook # init提供的回调函数
│ │ ├── reboot # reboot插件化源码
│ │ ├── seccomp # seccomp插件化源码
│ │ └── selinux # selinux插件化源码
│ ├── param # 系统参数部件
│ └── utils # init通用接口
├── test # init组件测试用例源文件目录
├── ueventd # ueventd服务源码
│ ├── etc # ueventd配置文件目录
│ ├── include # ueventd头文件目录
│ ├── lite # ueventd核心功能源码(小型系统)
│ └── standard # ueventd核心功能源码(标准系统)
└── watchdog # 看门狗服务源码
查看//base/startup/init/services/init/standard/BUILD.gn文件中构建规则,用于编译init进程和init_early进程。
ohos_executable("init_early") { # 编译init_early执行程序
sources = [
"//base/startup/init/interfaces/innerkits/hookmgr/hookmgr.c",
"//base/startup/init/services/log/init_commlog.c",
"bootstagehooker.c",
"device.c",
"init_firststage.c",
"init_mount.c",
"main_early.c",
"switch_root.c",
]
......
install_images = [ "ramdisk" ] #安装到ramdisk文件系统中
install_enable = true
part_name = "init"
}
ohos_executable("init") { #编译init执行程序
sources = [
"../adapter/init_adapter.c",
"../standard/device.c",
"../standard/fd_holder_service.c",
"../standard/init.c",
"../standard/init_cmdexecutor.c",
"../standard/init_cmds.c",
"../standard/init_control_fd_service.c",
"../standard/init_firststage.c",
"../standard/init_jobs.c",
"../standard/init_mount.c",
"../standard/init_reboot.c",
"../standard/init_service.c",
"../standard/init_signal_handler.c",
"../standard/switch_root.c",
"bootstagehooker.c",
]
......
install_images = [ # 安装打包到/system文件系统和updater文件系统中
"system",
"updater",
]
deps +=
[ "//base/startup/init/services/modules/crashhandler:libcrashhandler" ]
install_enable = true
part_name = "init"
subsystem_name = "startup"
}
Linux内核启动流程
在Linux-5.10中,start_kernel函数开启内核阶段的初始化过程。
//在Linux-5.10内核中,start_kernel函数开启了系统内核的初始化过程。
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
page_address_init(); //初始化页表相关的结构
pr_notice("%s", linux_banner);
early_security_init();
//进行体系结构相关的初始化,包括设置内存布局、识别处理器类型、初始化中断控制器等。
setup_arch(&command_line);
boot_cpu_hotplug_init();
build_all_zonelists(NULL); //建立内存管理的区域列表
page_alloc_init();
trap_init(); //初始化中断和异常处理机制
mm_init();
ftrace_init();
/* trace_printk can be enabled here */
early_trace_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init(); //初始化调度器相关的数据结构
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ(); //初始化中断
tick_init();
...
cred_init();
fork_init(); //初始化进程创建相关的数据结构
proc_caches_init();
uts_ns_init();
...
/* Do the rest non-__init'ed, we're now alive */
arch_call_rest_init(); //调用rest_init 创建一个进程,通常是init进程
rest_init创建一个内核线程kernel_init,在这个线程里面启动init进程。
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
//创建一个内核线程来执行kernel_init函数。这个内核线程将负责进一步的系统初始化,
//包括加载初始根文件系统、启动init进程。
//pid = 0
static char *execute_command;
static char *ramdisk_execute_command = "/init";
......
static int __ref kernel_init(void *unused)
{
int ret;
do_sysctl_args();
//启动init进程,该进程负责进一步的系统初始化,如启动系统服务,挂在文件系统等。
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
...
}
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
//创建kthreadd内核线程;负责管理和调度其他内核线程。
//pid = 2
调用kernel_init_freeable执行相关初始化。
static noinline void __init kernel_init_freeable(void)
{
/*
* Wait until kthreadd is all set-up.
* 等待kthread线程启动完成
*/
wait_for_completion(&kthreadd_done);
/* Now the scheduler is fully set up and can do blocking allocations */
gfp_allowed_mask = __GFP_BITS_MASK;
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_MEMORY]);
cad_pid = get_pid(task_pid(current));
smp_prepare_cpus(setup_max_cpus);
workqueue_init();
init_mm_internals();
rcu_init_tasks_generic();
//调用do_pre_smp_initcalls()函数初始化内核中所有early_initcall的驱动模块,这些驱动函数是在多喝CPU启动前初始化的。
do_pre_smp_initcalls();
lockup_detector_init();
//调用smp_init()启动剩余的CPU核心,至此多核CPU才是真正的多核CPU了,因为之前都是单核运行的。
smp_init();
//启动多核CPU调度功能
sched_init_smp();
#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
kthread_run(defer_free_memblock, NULL, "defer_mem");
#endif
padata_init();
page_alloc_init_late();
/* Initialize page ext after all struct pages are initialized. */
page_ext_init();
/*1. 初始化 共享内存shmem/devtmpfs/buses/classes/firmware/hypervisior;
2. 初始化平台驱动platform_bus等;
3. 注册和初始化irq中断的proc文件系统;
4. 调用kernel_constructor_function kernel构造函数,kernel中的构造函数保存在.ctors中;
5. 使能usermodelhelper_enable(),usemodeheler:为了在内核中能够直接新建和运行具备root权限的用户空间程序,为init启动做准备;
6. 调用do_initcalls();初始化所有需要module_init的驱动入口函数,各个驱动模块在这里开始加载,
*/
do_basic_setup();
kunit_run_all_tests();
#if IS_BUILTIN(CONFIG_INITRD_ASYNC)
async_synchronize_full();
#endif
//启动/dev/console
console_on_rootfs();
/*
* check if there is an early userspace init. If yes, let it do all
* the work
* 配置ramdisk_execute_command = '/init';如果文件系统不存在/init,则无文件系统,将
* 调用prepare_namespace()来挂载ramdisks等其他的文件系统,创建/dev/ramn,加载/initrd.image镜像。
*/
if (init_eaccess(ramdisk_execute_command) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*
* rootfs is available now, try loading the public keys
* and default modules
*/
integrity_load_keys();
}
最终调用run_init_process启动/init进程,kernel_init线程结束,然后kthreadd承担维护内核线程的调度和管理。
init_early分析
在kernel中启动的/init进程,实际上是一个软链接,指向了/bin/init_early;可在out/rk3568/packages/phone/ramdisk/目录查看。
![]()
第一阶段初始化
//在//base/startup/init/services/init/standard/main_early.c文件中,
//是init_early的入口main函数。
int main(int argc, char * const argv[])
{
long long upTimeInMicroSecs = GetUptimeInMicroSeconds(NULL);
SystemPrepare(upTimeInMicroSecs);//拥有root权限
return 0;
}
//base/startup/init/services/init/standard/init_firststage.c
void SystemPrepare(long long upTimeInMicroSecs)
{
//忽略终端信号,将SIGPIPE交系统处理,SIG_IGN是信号处理的一个常量,标识忽略信号。
(void)signal(SIGPIPE, SIG_IGN);
EnableInitLog(INIT_INFO); //使能InitLog的INIT_INFO级别
EarlyLogInit(); //初始化EarlyLog
INIT_LOGI("Start init first stage.");
CreateFsAndDeviceNode(); //创建常用的文件和设备节点
//执行钩子函数,一般钩子链表是通过—__attribute__((constructor))构造的函数添加的
HookMgrExecute(GetBootStageHookMgr(), INIT_FIRST_STAGE, NULL, NULL);
//判断是否为升级模式,升级模式不需要挂载文件系统和切换root权限
// Updater mode no need to mount and switch root
if (InUpdaterMode() != 0) {
return;
}
//挂载必要的分区,从命令行/proc/cmdline中的读取fstab文件,挂载必要的分区,同时启动ueventd,监视linux内核设备的注册;
MountRequiredPartitions();
/*
ohos.required_mount.system=/dev/block/platform/fe310000.sdhci/byname/system@/usr@ext4@ro,barrier=1@wait,required
ohos.required_mount.vendor=/dev/block/platform/fe310000.sdhci/byname/vendor@/vendor@ext4@ro,barrier=1@wait,required
ohos.required_mount.misc=/dev/block/platform/fe310000.sdhci/byname/misc@none@none@none@wait,required
ohos.required_mount.bootctrl=/dev/block/platform/fe310000.sdhci/byname/bootctrl@none@none@none@wait,required
*/
//启动第二阶段初始化(INIT)
StartSecondStageInit(upTimeInMicroSecs);
}
第二阶段初始化
第二阶段主要实现切换root路径到/usr/(system文件系统),并执行init程序的代码。
//base/startup/init/services/init/standard/init_firststage.c
static void StartSecondStageInit(long long uptime)
{
INIT_LOGI("Start init second stage.");
// It will panic if close stdio before execv("/bin/sh", NULL)
CloseStdio();//关闭stdio,此时不能通过printf打印log了.
SwitchRoot("/usr"); //切换root路径为/usr(system文件系统)
char buf[64];
snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%lld", uptime);
// Execute init second stage
char * const args[] = {
"/bin/init", //init可执行文件的路径
"--second-stage", //参数标识是启动的第二阶段
buf,
NULL,
};
if (execv("/bin/init", args) != 0) { //让init进程执行init程序的代码
INIT_LOGE("Failed to exec \"/bin/init\", err = %d", errno);
exit(-1);
}
}
//base/startup/init/services/init/main.c
int main(int argc, char * const argv[])
{
const char *uptime = NULL;
long long upTimeInMicroSecs = 0;
int isSecondStage = 0;
(void)signal(SIGPIPE, SIG_IGN);
// Number of command line parameters is 2
if (argc > 1 && (strcmp(argv[1], "--second-stage") == 0)) { //是否为二阶段启动
isSecondStage = 1; //给第二阶段启动标识赋值为1;
if (argc > 2) {
uptime = argv[2];
}
} else {
upTimeInMicroSecs = GetUptimeInMicroSeconds(NULL);
}
if (getpid() != INIT_PROCESS_PID) { //判断当前的进程ID是否等于1
INIT_LOGE("Process id error %d!", getpid());
return 0;
}
EnableInitLog(INIT_INFO);
// Updater mode
if (isSecondStage == 0) { //不成立,
SystemPrepare(upTimeInMicroSecs); //执行init_early代码
} else {
LogInit();
}
SystemInit(); //系统初始化
SystemExecuteRcs(); //rk3568没有/etc/init.d/rcS脚本
SystemConfig(uptime); //系统配置
SystemRun(); //系统运行
return 0;
}
系统初始化
//base/startup/init/services/init/standard/init.c
void SystemInit(void)
{
CloseStdio(); //关闭标准输入输出
#ifndef STARTUP_INIT_TEST
//设置一个会话密钥,所有进程都将具有访问权限
// Set up a session keyring that all processes will have access to.
KeyCtrlGetKeyringId(KEY_SPEC_SESSION_KEYRING, 1);
#endif
// umaskd调用总是成功并返回先前的掩码值,这里不需要。
// umask call always succeeds and return the previous mask value which is not needed here
(void)umask(DEFAULT_UMASK_INIT);
MakeDirRecursive("/dev/unix/socket", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
//初始化套接字
int sock = FdHolderSockInit();
if (sock >= 0) {
//注册套接字监视器
RegisterFdHoldWatcher(sock);
}
InitControlFd(); //初始化控制文件描述符
// sysclktz 0 //设置系统时钟
struct timezone tz = { 0 };
if (settimeofday(NULL, &tz) == -1) {
INIT_LOGE("Set time of day failed, err = %d", errno);
}
}
系统配置
//base/startup/init/services/init/standard/init.c
void SystemConfig(const char *uptime)
{
INIT_TIMING_STAT timingStat;
// 初始化"/proc/self/oom_score_adj" 为-1000,取值范围-1000到1000,取值-1000时这个进程在low mem时永远杀不掉
InitSysAdj();
HOOK_EXEC_OPTIONS options;
options.flags = 0;
options.preHook = InitPreHook;
options.postHook = InitPostHook;
//初始化服务空间, boot start: device.boot.group; 从设备启动组开始启动
InitServiceSpace();
// 最终回调 InitPreHook InitPostIHook
HookMgrExecute(GetBootStageHookMgr(), INIT_GLOBAL_INIT, (void *)&timingStat, (void *)&options);
//记录初始化事件
RecordInitBootEvent("init.prepare");
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_PARAM_SERVICE, (void *)&timingStat, (void *)&options);
// 初始化paramService
if (InitParamService() != 0) {
//失败就重启
ExecReboot("panic");
}
//解析/system/etc/device.boot.group.cfg
InitParseGroupCfg();
RegisterBootStateChange(BootStateChange); //注册启动状态改变处理函数
INIT_LOGI("boot stage: init finish.");
// load SELinux context and policy
// Do not move position!
PluginExecCmdByName("loadSelinuxPolicy", ""); //加载selinux 上下文
RecordInitBootEvent("init.prepare");
// after selinux loaded
SignalInit(); //信号处理初始化
RecordInitBootEvent("init.ParseCfg");
LoadSpecialParam(); //加载专用的参数
// parse parameters
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_PARAM_LOAD, (void *)&timingStat, (void *)&options);
InitLoadParamFiles(); //加载param文件
// Write kernel uptime into system parameter
WriteUptimeSysParam("ohos.boot.time.kernel", uptime);
// read config
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_CFG_LOAD, (void *)&timingStat, (void *)&options);
ReadConfig(); //读取cfg文件,实际是json文件,其中定义了各中启动配置信息。
RecordInitBootEvent("init.ParseCfg");
INIT_LOGI("boot stage: parse config file finish.");
HookMgrExecute(GetBootStageHookMgr(), INIT_POST_CFG_LOAD, (void *)&timingStat, (void *)&options);
IsEnableSandbox();
// execute init
//触发执行"pre-init"阶段的启动配置
PostTrigger(EVENT_TRIGGER_BOOT, "pre-init", strlen("pre-init"));
//触发执行"init"阶段的启动配置
PostTrigger(EVENT_TRIGGER_BOOT, "init", strlen("init"));
//触发启动模式是START_MODE_BOOT的service执行
TriggerServices(START_MODE_BOOT);
//触发执行"post-init"阶段的启动配置
PostTrigger(EVENT_TRIGGER_BOOT, "post-init", strlen("post-init"));
//触发启动模式是START_MODE_NORMAL的service执行
TriggerServices(START_MODE_NORMAL);
clock_gettime(CLOCK_MONOTONIC, &(g_bootJob.startTime));
}
系统运行
//base/startup/init/services/init/standard/init.c
void SystemRun(void)
{
StartParamService(); //启动ParamService
}
//base/startup/init/services/param/linux/param_service.c
int StartParamService(void)
{
// read selinux label
LoadSelinuxLabel("permission");
return ParamServiceStart();
}更多推荐
所有评论(0)