当我们想到Android启动过程时,脑海中总是冒出很多疑问。本文将介绍Android的启动过程,希望能帮助你找到上面这些问题的答案。
Android是一个基于Linux的开源操作系统。x86(x86是一系列的基于intel 8086 CPU的计算机微处理器指令集架构)是linux内核部署最常见的系统。然而,所有的Android设备都是运行在ARM处理器(ARM 源自进阶精简指令集机器,源自ARM架构)上,除了英特尔的Xolo设备(http://xolo.in/xolo-x900-features)。Xolo来源自凌动1.6GHz x86处理器。Android设备或者嵌入设备或者基于linux的ARM设备的启动过程与桌面版本相比稍微有些差别。这篇文章中,我将解释Android设备的启动过程。深入linux启动过程是一篇讲桌面linux启动过程的好文。
当你按下电源开关后Android设备执行了以下步骤。
当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。
引导程序是在Android操作系统开始运行前的一个小程序。引导程序是运行的第一个程序,因此它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如redboot、uboot、qi bootloader或者开发自己的引导程序,它不是Android操作系统的一部分。引导程序是OEM厂商或者运营商加锁和限制的地方。
引导程序分两个阶段执行。第一个阶段,检测外部的RAM以及加载对第二阶段有用的程序;第二阶段,引导程序设置网络、内存等等。这些对于运行内核是必要的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。
Android引导程序可以在\bootable\bootloader\legacy\usbloader
找到。
传统的加载器包含的个文件,需要在这里说明:
更多关于Android引导程序的可以在这里了解。
Android内核与桌面linux内核启动的方式差不多。内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
init是第一个进程,我们可以说它是root进程或者说有进程的父进程。init进程有两个责任,一是挂载目录,比如/sys、/dev、/proc,二是运行init.rc脚本。
/system/core/init
找到。/system/core/rootdir/init.rc
找到。/system/core/init/readme.txt
找到。 对于init.rc文件,Android中有特定的格式以及规则。在Android中,我们叫做Android初始化语言。
Android初始化语言由四大类型的声明组成,即Actions(动作)、Commands(命令)、Services(服务)、以及Options(选项)。
Action(动作):动作是以命令流程命名的,有一个触发器决定动作是否发生。
语法
on
Service(服务):服务是init进程启动的程序、当服务退出时init进程会视情况重启服务。
语法
service []*
Options(选项)
选项是对服务的描述。它们影响init进程如何以及何时启动服务。
咱们来看看默认的init.rc文件。这里我只列出了主要的事件以及服务。
Table
Action/Service | 描述 |
on early-init | 设置init进程以及它创建的子进程的优先级,设置init进程的安全环境 |
on init | 设置全局环境,为cpu accounting创建cgroup(资源控制)挂载点 |
on fs | 挂载mtd分区 |
on post-fs | 改变系统目录的访问权限 |
on post-fs-data | 改变/data目录以及它的子目录的访问权限 |
on boot | 基本网络的初始化,内存管理等等 |
service servicemanager | 启动系统管理器管理所有的本地服务,比如位置、音频、Shared preference等等… |
service zygote | 启动zygote作为应用进程 |
在这个阶段你可以在设备的屏幕上看到“Android”logo了。
在Java中,我们知道不同的虚拟机实例会为不同的应用分配不同的内存。假如Android应用应该尽可能快地启动,但如果Android系统为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了”Zygote”。Zygote让Dalvik虚拟机共享代码、低内存占用以及最小的启动时间成为可能。Zygote是一个虚拟器进程,正如我们在前一个步骤所说的在系统引导的时候启动。Zygote预加载以及初始化核心库类。通常,这些核心类一般是只读的,也是Android SDK或者核心框架的一部分。在Java虚拟机中,每一个实例都有它自己的核心库类文件和堆对象的拷贝。
Zygote加载进程
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
在这个阶段,你可以看到启动动画。
完成了上面几步之后,运行环境请求Zygote运行系统服务。系统服务同时使用native以及java编写,系统服务可以认为是一个进程。同一个系统服务在Android SDK可以以System Services形式获得。系统服务包含了所有的System Services。
Zygote创建新的进程去启动系统服务。你可以在ZygoteInit类的”startSystemServer”方法中找到源代码。
核心服务:
其他服务:
一旦系统服务在内存中跑起来了,Android就完成了引导过程。在这个时候“ACTION_BOOT_COMPLETED”开机启动广播就会发出去。
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
本文描述init.rc脚本解析以及执行过程,读完本章后,读者应能
(1) 了解init.rc解析过程 (2) 定制init.rc
init.rc介绍 init.rc是一个文本文件,可认为它是Android系统启动脚本。init.rc文件中定义了环境变量配置、系统进程启动,分区挂载,属性配置等诸多内容。init.rc具有特殊的语法。init源码目录下的readme.txt中详细的描述了init启动脚本的语法规则,是试图定制init.rc的开发者的必读资料。
Android启动脚本包括一组文件,包括
init.rc init.usb.rc init.trace.rc init.{hardware}.rc init.environ.rc init.zygote32.rc这些文件可能分布于如下目录中
# Copyright (C) 2012 The Android Open Source Project # # IMPORTANT: Do not create world writable files or directories. # This is a common source of Android security bugs. # import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /init.${ro.zygote}.rc import /init.trace.rc on early-init start ueventd on init sysclktz 0 loglevel 3 mkdir /system mkdir /data 0771 system system write /proc/sys/kernel/panic_on_oops 1 # Load properties from /system/ + /factory after fs mount. on load_all_props_action load_all_props on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount on boot # basic network init ifup lo class_start core class_start main on property:vold.decrypt=trigger_reset_main class_reset main service ueventd /sbin/ueventd class core critical on property:ro.debuggable=1 start console service debuggerd /system/bin/debuggerd class main service bootanim /system/bin/bootanimation class main user graphics group graphics disabled oneshot
on其中
on early-init on init on property:sys.boot_from_charger_mode=1 on load_all_props_action on firmware_mounts_complete on late-init on post-fs on post-fs-data on boot on nonencrypted on property:sys.init_log_level=* on charger on property:vold.decrypt=trigger_reset_main on property:vold.decrypt=trigger_load_persist_props on property:vold.decrypt=trigger_post_fs_data on property:vold.decrypt=trigger_restart_min_framework on property:vold.decrypt=trigger_restart_framework on property:vold.decrypt=trigger_shutdown_framework on property:sys.powerctl=* on property:sys.sysctl.extra_free_kbytes=* on property:sys.sysctl.tcp_def_init_rwnd=* on property:ro.debuggable=1 on property:ro.kernel.qemu=1 on boot on post-fs-data on property:sys.usb.config=none on property:sys.usb.config=adb on property:sys.usb.config=accessory on property:sys.usb.config=accessory,adb on property:sys.usb.config=audio_source on property:sys.usb.config=audio_source,adb on property:sys.usb.config=accessory,audio_source on property:sys.usb.config=accessory,audio_source,adb on property:persist.sys.usb.config=*根据trigger的不同,可以将Actions大致分为两类: (1) 普通型
chdir改变工作目录 chroot 改变当前进程的root目录 class_start 如果serviceclass内所有services尚未启动,则启动它 class_stop 停止serviceclass内所有services class_reset 重启serviceclass内所有services domainname 设置domain名称 enable exec [ ]* fork后执行path所执行的程序,该语句会阻塞直到path指定的程序执行完毕。 export 设置全局环境变量,将会被该命令后所启动的进程继承。 hostname 设置主机名 ifup 启动interface所指定的网络接口 insmod 安装path所指定的内核模块 mkdir [mode] [owner] [group] 创建path制定的目录,并根据可选的mode、owner、group设定参数。如果未指定可选参数,则创建的文件夹权限将会设置为0755,而owner与group都为root mount_all mount [ restorecon restorecon_recursive rm 删除path指定的文件 rmdir 删除path指定的目录(目录为空才能删除) setcon setenforce setkey setprop setrlimit setsebool start stop swapon_all trigger symlink sysclktz wait write copy chown chmod loglevel load_persist_props load_all_props
service[ ]*
capability class设定service的class console critical disabled group [ ]* 设定进程 keycodes oneshot service只执行一次 onrestart 当service终止时自动重启 seclabeli setenv socket user ioprio
INFO("reading config file\n"); init_parse_config_file("/init.rc")init_parse_config_file("/init.rc")
static list_declare(service_list); static list_declare(action_list); static list_declare(action_queue);service_list是全局service链表,解析启动脚本过程中,service对应数据结构struct service将会挂载到这里。
struct command { /* list of commands in an action */ struct listnode clist; int (*func)(int nargs, char **args); int line; const char *filename; int nargs; char *args[1]; }; struct action { /* node in list of all actions */ struct listnode alist; /* node in the queue of pending actions */ struct listnode qlist; /* node in list of actions for a trigger */ struct listnode tlist; unsigned hash; const char *name; struct listnode commands; struct command *current; };
struct service { /* list of all services */ struct listnode slist; //用于挂载于service_list的钩子 const char *name; //存放service的名称 const char *classname; //用于存放该service所隶属的class的名称 unsigned flags; //位图变量,其各个位代表不同的servcie的属性(对应service中的option字段) pid_t pid; //当service对应的程序执行时,存放其进程号 time_t time_started; /* time of last start */ //存放进程启动时间 time_t time_crashed; /* first crash within inspection window */ //存放第一次进程崩溃时间 int nr_crashed; /* number of times crashed within window */ //存放进程崩溃次数 uid_t uid; //该servcie对应进程的uid gid_t gid; //该service对应进程的gidinit_parse_config_file("/init.rc"); gid_t supp_gids[NR_SVC_SUPP_GIDS];//该service对应进程的附加群组id size_t nr_supp_gids; //该service所隶属的附件组的数目 char *seclabel; //存放selinux所需要的security context struct socketinfo *sockets; struct svcenvinfo *envvars; struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes;"queue_property_triggers" int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; //对应service语句传入的参数数目 /* "MUST BE AT THE END OF THE STRUCT" */ char *args[1]; //存放service语句实际传入的参数,其长度将会被修正为nargs+1 }; /* ^-------'args' MUST be at the end of this struct! */
service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drmonrestart属性后必须跟restart关键字,随后必须再跟一个service名称。 这段含义是定义一个名为servicemanager的service,对应的可执行程序为/system/bin/servicemanager,其多个option则设定servicemanager进程退出时,重启healthd、zygote、media、surfaceflinger、drm等service。
//init.h #define SVC_DISABLED 0x01 /* do not autostart with class */ #define SVC_ONESHOT 0x02 /* do not restart on exit */ #define SVC_RUNNING 0x04 /* currently active */ #define SVC_RESTARTING 0x08 /* waiting to restart */ #define SVC_CONSOLE 0x10 /* requires console */ #define SVC_CRITICAL 0x20 /* will reboot into recovery if keeps crashing */ #define SVC_RESET 0x40 /* Use when stopping a process, but not disabling so it can be restarted with its class */ #define SVC_RC_DISABLED 0x80 /* Remember if the disabled flag was set in the rc script */ #define SVC_RESTART 0x100 /* Use to safely restart (stop, wait, start) a service */ #define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */
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_lin一部分在ux_rng"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); /* skip mounting filesystems in charger mode */ if (!is_charger) { action_for_each_trigger("early-fs", action_add_queue_tail); action_for_each_trigger("fs", action_add_queue_tail); action_for_each_trigger("post-fs", action_add_queue_tail); action_for_each_trigger("post-fs-data", action_add_queue_tail); } /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random * wasn't ready immediately after wait_for_coldboot_done */ 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(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); if (is_charger) { action_for_each_trigger("charger", action_add_queue_tail); } else { action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); } /* run all property triggers based on current state of the properties */ queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); #if BOOTCHART queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif
for(;;) { execute_one_command(); ... }
on late-init trigger early-fs trigger fs trigger post-fs trigger post-fs-data # Load properties from /system/ + /factory after fs mount. Place # this in another action so that the load will be scheduled after the prior # issued fs triggers have completed. trigger load_all_props_action # Remove a file to wake up anything waiting for firmware. trigger firmware_mounts_complete trigger early-boot trigger boot
//keyword.h //------------- KEYWORD(trigger, COMMAND, 1, do_trigger) //builtins.c //------------- int do_trigger(int nargs, char **args) { action_for_each_trigger(args[1], action_add_queue_tail); return 0; }
static int queue_property_triggers_action(int nargs, char **args) { queue_all_property_triggers(); /* enable property triggers */ property_triggers_enabled = 1; return 0; } void queue_all_property_triggers() { struct listnode *node; struct action *act; list_for_each(node, &action_list) { act = node_to_item(node, struct action, alist); if (!strncmp(act->name, "property:", strlen("property:"))) { /* parse property name and value syntax is property:可见queue_all_property_triggers函数中会检查从action_list中查找所有property型的Actions,并判断其属性条件是否满足,若满足则使用action_add_queue_tail(act)将其添加到action_queue中。= */ const char* name = act->name + strlen("property:"); const char* equals = strchr(name, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; char value[PROP_VALUE_MAX];新浪微博 int length = equals - name; if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); } else { int ret; memcpy(prop_name, name, length); prop_name[length] = 0; /* does the property exist, and match the trigger value? */ ret = property_get(prop_name, value); if (ret > 0 && (!strcmp(equals + 1, value) || !strcmp(equals + 1, "*"))) { action_add_queue_tail(act); } } } } } }
int main(int argc, char* argv[]) { .... for(;;) { execute_one_command(); .... 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(); ... } } } }