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属性如下: Android init启动分析(1)init启动

(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; }
<

免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:Android init启动分析(1)init启动 https://www.yhzz.com.cn/a/12633.html

上一篇 2023-05-09 11:48:46
下一篇 2023-05-09 13:09:12

相关推荐

联系云恒

在线留言: 我要留言
客服热线:400-600-0310
工作时间:周一至周六,08:30-17:30,节假日休息。