Android init启动分析(1)init启动
1. 前言
总结下android init启动流程;
为平时debug梳理思路
2.Init代码结构
Init 源代码目录:system/core/init/ , 源文件如下:
├── Android.mk
├── bootchart.c
├── bootchart.h
├── builtins.c
├── devices.c
├── devices.h
├── drv_display.h
├── fswatcherd.c
├── fswatcherd.h
├── grab-bootchart.sh
├── init.c
├── init_disp.c
├── init_disp.h
├── init.h
├── init_parser.c
├── init_parser.h
├── keychords.c
├── keychords.h
├── keywords.h
├── log.h
├── parser.c
├── parser.h
├── property_service.c
├── property_service.h
├── signal_handler.c
├── signal_handler.h
├── sunxi_display2.h
├── ueventd.c
├── ueventd.h
├── ueventd_keywords.h
├── ueventd_parser.c
├── ueventd_parser.h
├── util.c
├── util.h
├── watchdogd.c
└── watchdogd.h
3. Init启动前准备
Android Init是Kernel启动阶段结束后启动的第一个用户空间进程。Android镜像会将init可执行文件放置在内存/目录下,Kernel初始化结束阶段会加载并运行/init文件,处理流程如下:
start_kernel()-->reset_init()-->kernel_init()-->kernel_init_freeable():
static noinline void __init kernel_init_freeable(void)
{
... .... .... ....
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init"; //检查ramdisk_execute_command如果为空,强制赋值”/init”
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
/* 检查ramdisk_execute_command字符串内容路径文件是否存在,如果不存在,为NULL */
prepare_namespace();
}
}
Kernel 启动结束阶段start_kernel()-->reset_init()-->kernel_init()函数中会做如下判断:
static int __ref kernel_init(void *unused)
{
kernel_init_freeable();
async_synchronize_full();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
flush_delayed_fput();
if (ramdisk_execute_command) {
if (!run_init_process(ramdisk_execute_command)) // 启动“/init”
return 0;
pr_err("Failed to execute %s\n", ramdisk_execute_command);
}
if (execute_command) { //如果ramdisk_execute_command启动失败,则再次尝试从execute_command启动
if (!run_init_process(execute_command))
return 0;
}
.... .... .... .... .... .... .... ....
.... .... .... .... .... .... .... ....
}
Bootload退出前传递给Kernel的cmdline中含有字符串”init=/init”, Kernel启动初始化cmdline过程中会将”/init” 赋值到execute_command变量中.
4. Init启动过程
Android init代码很长,入口函数为源文件init.c的main()函数,分阶段对init启动代码处理过程进行分析.
(1) 区分init和ueventd/watchdogd/fswatcherd守护进程
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
if (!strcmp(basename(argv[0]), "watchdogd"))
return watchdogd_main(argc, argv);
if (!strcmp(basename(argv[0]), "fswatcherd"))
return fswatcherd_main(argc, argv);
Android.mk文件中将/sbin/ueventd 、/sbin/watchdogd 和/sbin/fswatcherd守护进程编译成软链接指向init 二进制文件本身,主要考虑到守护进程与init代码重复度很高,可以直接加载init二进制本身进行运行。
init进程执行init.rc命令过程中会启动守护进程,由于main()入口处有区分,因此不同守护进程分别执行对应的流程。具体分析在后面章节中.
(2) 设置init进程创建文件和目录属性
umask(000);
头文件#include<sys/stat.h> 定义函数mode_t umask(mode_t mask);
文件权限读(4)+写(2)+可执行(1),Umask缺省值为022,建立文件的默认权限是(6-0,6-2,6-2)644
建立目录的默认权限是(7-0,7-2,7-2)755. umask()可以更改umask的值.
当umask更改为000时,创建文件权限为666,创建目录权限为777.
(3) 创建目录和挂载linux文件系统到节点
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
创建目录,并经tmpfs文件系统、proc文件系统和sysfs文件系统到对应节点
(4) 创建/dev/.booting临时文件
close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
表明当前init正在启动初始化过程,init启动结束后,会删除该临时文件.
(5)打开/dev/null节点
open_devnull_stdio();
函数定义在util.c文件中,打开/dev/null空设备节点,把标准输入、标准输出和标准错误重定向输入到该节点.守护进程常用的手段.
(6)打开"/dev/kmsg"节点,将init 打印输出到内核打印buf中
klog_init();
Init进程打印宏定义如下:
system/core/init/log.h
#define ERROR(x...) KLOG_ERROR("init", x)
#define NOTICE(x...) KLOG_NOTICE("init", x)
#define INFO(x...) KLOG_INFO("init", x)
system/core/include/cutils/klog.h
#define KLOG_ERROR(tag,x...) klog_write(3, "<3>" tag ": " x)
#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x)
#define KLOG_NOTICE(tag,x...) klog_write(5, "<5>" tag ": " x)
#define KLOG_INFO(tag,x...) klog_write(6, "<6>" tag ": " x)
#define KLOG_DEBUG(tag,x...) klog_write(7, "<7>" tag ": " x)
最终会调用到klog_write,将进程打印信息重定向到内核kmsg中
system/core/libcutils/klog.c
void klog_write(int level, const char *fmt, ...)
{
char buf[LOG_BUF_MAX];
... ...
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
buf[LOG_BUF_MAX - 1] = 0;
va_end(ap);
write(klog_fd, buf, strlen(buf));
}
(7) 创建共享内存区域用于存储Android属性值
property_init();
函数定义在源文件./system/core/init/property_service.c 中,其他关联文件如下:
./bionic/libc/include/sys/_system_properties.h
./bionic/libc/include/sys/system_properties.h
./bionic/libc/bionic/system_properties.cpp
./bionic/libc/bionic/system_properties_compat.c
(8)获取/proc/cpuinfo节点信息
get_hardware_name(hardware, &revision);
函数定义在源文件.system/core/init/util.c
(9) 获取/proc/cmdline节点信息
process_kernel_cmdline
函数主要从cmdline节点获取传参信息后进行解析,并初始化系统property属性如下:
(10) 初始化Selinux
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
./external/sepolicy/:selinux 源文件,selinux的源码分析放在后面章节
(11) 检查当前启动模式是否在充电模式
is_charger = !strcmp(bootmode, "charger");
充电模式是指设置插着充电器开机时设备进入的状态,该模式下系统只会启动kernel和init,打不部分服务不会启动.
(12)读取 /default.prop 文件中配置,设置到系统property空间中
property_load_boot_defaults();
函数主要在/system/core/init/property_service.c文件中定义实现
(13) 解析/init.rc文件,将action和service分别添加到对应的queue中等待执行
init_parse_config_file("/init.rc");
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(init_set_disp_policy, "init_set_disp_policy");
queue_builtin_action(signal_init_action, "signal_init");
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("late-init", action_add_queue_tail);
}
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
#if BOOTCHART
queue_builtin_action(bootchart_init_action, "bootchart_init");
#endif
解析函数主要在./system/core/init/init_parser.c文件中定义
(14) Init 通过for循环执行action_queue中命令和启动service_queue中服务
for(;;) {
int nr, i, timeout = -1;
execute_one_command();
restart_processes();
(15) Init完成系统初始化后,会退化成守护进程通过poll系统调用监听系统的property属性变化、子进程的kill信号和组合按键事件.
if (!property_set_fd_init && get_property_set_fd() > 0) {
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
if (!keychord_fd_init && get_keychord_fd() > 0) {
ufds[fd_count].fd = get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
keychord_fd_init = 1;
}
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() || cur_action)
timeout = 0;
#if BOOTCHART
if (bootchart_count > 0) {
if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
timeout = BOOTCHART_POLLING_MS;
if (bootchart_step() < 0 || --bootchart_count == 0) {
bootchart_finish();
bootchart_count = 0;
}
}
#endif
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
if (ufds[i].revents & POLLIN) {
if (ufds[i].fd == get_property_set_fd())
handle_property_set_fd();
else if (ufds[i].fd == get_keychord_fd())
handle_keychord();
else if (ufds[i].fd == get_signal_fd())
handle_signal();
}
}
}
return 0;
}
- 分享
- 举报
-
浏览量:5057次2021-04-15 15:05:49
-
浏览量:822次2023-08-30 10:00:35
-
浏览量:567次2024-01-25 13:00:44
-
浏览量:5654次2020-12-23 09:56:49
-
浏览量:5656次2020-12-30 16:36:48
-
浏览量:1272次2023-11-06 15:17:14
-
浏览量:6889次2020-11-24 23:15:35
-
浏览量:684次2024-01-25 13:17:04
-
2024-05-30 09:29:48
-
浏览量:2554次2017-11-23 12:47:40
-
浏览量:2879次2022-05-31 10:57:34
-
浏览量:728次2023-12-06 16:50:25
-
浏览量:1442次2023-12-06 12:30:38
-
浏览量:990次2023-12-01 12:14:35
-
浏览量:1276次2023-12-04 13:11:50
-
浏览量:111次2024-08-23 14:21:28
-
2023-07-05 09:40:26
-
浏览量:6340次2020-12-19 11:23:50
-
浏览量:19906次2022-02-19 09:00:10
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
free-jdx
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明