摘要:本文讲解Android系统在启动过程中的关键动作,摈弃特定平台之间的差异,讨论共性的部分,至于启动更加详细的过程,需要结合代码分析,这里给出流程框架,旨在让大家对开机过程更明了。
关键词:U-boot、Linux、Android
一、Bootloader的定义和种类
二、Arm特定平台的Bootloader
三、U-boot启动流程分析
一、zImage是怎样炼成的?
二、linux的c启动阶段
一、init进程
二、init启动的各种服务
第一部分:Bootloader启动
一、
简单地说,BootLoader是在操作系统运行之前运行的一段程序,它可以将系统的软硬件
环境带到一个合适状态,为运行操作系统做好准备。这样描述是比较抽象的,但是它的任务确实不多,终极目标就是把OS拉起来运行。
在嵌入式系统世界里存在各种各样的Bootloader,种类划分也有多种方式。除了按照处
理器体系结构不同划分以外,还有功能复杂程度的不同。
先区分一下Bootloader和Monitor[l1]
码;而Monitor另外还提供了很多的命令行接口,可以进行调试、读写内存、烧写Flash、配置环境变量等。在开发过程中Monitor提供了很好地调试功能,不过在开发结束之后,可以完全将其设置成一个Bootloader。所以习惯上将其叫做Bootloader。
Bootloader | Monitor? | 描述 | X86 | ARM | PowerPC |
U-boot | 是 | 通用引导程序 | 是 | 是 | 是 |
是 | 基于eCos的引导程序 | 是 | 是 | 是 | |
否 | (StrongARM构架)LART(主板)等硬件平台的引导程序 | 否 | 是 | 否 | |
LILO | 否 | Linux磁盘引导程序 | 是 | 否 | 否 |
GRUB | 否 | GNU的LILO替代程序 | 是 | 否 | 否 |
Loadlin | 否 | 从DOS引导Linux | 是 | 否 | 否 |
Vivi | 是 | 韩国mizi 公司开发的bootloader | 否 | 是 | 否 |
台的blob,还有S3C2410处理器开发板上的vivi等。现在armboot已经并入了U-Boot,所以U-Boot也支持ARM/XSCALE平台。U-Boot已经成为ARM平台事实上的标准Bootloader。
二、
到目前为止,我们公司已经做过多个Arm平台的android方案,包括:marvell(pxa935)、
informax(im9815)、mediatek(mt6516/6517)、broadcom(bcm2157)。由于不同处理器芯片厂商对armcore的封装差异比较大,所以不同的arm处理器,对于上电引导都是由特定处理器芯片厂商自己开发的程序,这个上电引导程序通常比较简单,会初始化硬件,提供下载模式等,然后才会加载通常的bootloader。
下面是几个arm平台的bootloader方案:
marvell(pxa935) :
informax(im9815) :
mediatek(mt6516/6517) :
broadcom(bcm2157) :
为了明确U-boot之前的两个loader的作用,下面以broadcom平台为例,看下在上电之
后到U-boot的流程,如图1.2.1:
三、
最常用的bootloader还是U-boot,可以引导多种操作系统,支持多种架构的CPU。它支持的操作系统有:Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS等,支持的CPU架构有:ARM、PowerPC、MISP、X86、NIOS、Xscale等。
手机系统不像其他的嵌入式系统,它还需要在启动的过程中关心CP的启动,这个时候就涉及到CP的image和唤醒时刻,而一般的嵌入式系统的uboot只负责引导OS内核。所以这里我们也暂不关心CP的启动,而主要关心AP侧。
从上面第二小节中可以看出,bootloader通常都包含有处理器厂商开发的上电引导程序,不过也不是所有的处理都是这样,比如三星的S3C24X0系列,它的bootROM直接跳到U-boot中执行,首先由bootROM将U-boot的前4KB拷贝到处理器ISRAM,接着在U-boot的前4KB中必须保证要完成的两项主要工作:初始化DDR,nand和nand控制器,接着将U-boot剩余的code拷贝到SDRAM中,然后跳到SDRAM的对应地址上去继续跑U-boot——这种方式比较简单直白,但是太裸了,就好像不穿衣服给比人看得一清二楚,不安全啊,所以后来三星exynos搞了bl1和bl2,商业化好了许多。
所以U-boot[l6]
3.1
4. 调用s3c2410_cache_flush_all函数,使TLBS,I、D Cache,WB中数据失效。
5. 时钟设置CLKDIVN=0x3 , FCLK:HCLK:PCLK = 1:2:4。
6. 读取mp15的c1寄存器,将最高两位改成11,表示选择了异步时钟模型。
7. 检查系统的复位状态,以确定是不是从睡眠唤醒。
8.
的,这里涉及到运行时域[l7]
9. cpu_init_crit函数,主要完成了两个工作:首先使ICache andDcache,TLBs中早期内容失效,再设置p15 control registerc1,关闭MMU,Dcache,但是打开了Icache和Faultchecking,(要求mmu和Dcache是必须要关闭的,而Icache可以打开可以关闭);其次调用/board/nextdvr2410/memsetup.S文件中的memsetup函数来建立对SDRAM的访问时序。
10. Relocate函数,加载nandflash中的uboot到SDRAM中,代码会加载到0x33f80000开始的地址,空间大小是512。
1). ndf2ram函数
a.
b.
c.
d.
e.
f.
2). ldr pc, _start_armboot
3.2
1.
gd_data,定义一个structbd_info结构体对象bd_data,定义一个指向函数的二级指针init_fnc_ptr,定义的全局结构体对象都是放在堆栈中的,gd是放在寄存器中的。
2.
3.
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_VCMA9)
#endif
};
cpu_init:根据需要设定IRQ,FIR堆栈。如果使用中断的话,中断堆栈就接在后面。
board_init:设置LOCKTIME,配置MPLL,UPLL,配置IOports,设置gd->bd->bi_arch_number(553),gd->bd->bi_boot_params= 0x30000100设置boot参数地址,使能Icache和Dcache。
interrupt_init:使用timer 4来作为系统clock, 即时钟滴答,10ms一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。
env_init:该函数主要做关于环境变量的工作,这个环境变量可以不用存放在nor或者nandflash上,直接在内存中生成(default_environment)。不过对于那些掉电需要保存的参数来说,保存在flash上无疑是最可靠的方式。有的uboot还支持冗余存储,也就是存两份做备份。
块上的环境变量(16K)全部读入到0x33ef0000这个起始地址中来,在接下来将堆空间分配好之后,在函数env_relocate中,通过在堆中获得一块区域来存放环境变量,env_ptr指向这块区域,接下来所谓的重新获得环境变量无非就是将原来0x33ef0000开始的16K数据拷贝到env_ptr所指的区域中去。这里分第一次uboot启动(泛指只要在第一次运行saveenv指令之前所启动的uboot过程)和保存过环境变量的情况,但实质是一样的,所不同的是,第一次uboot启动,nand第9块区域中的数据肯定不是什么环境变量,所以这是的crc校验肯定出错,所以这时系统使用了默认的环境变量,但是只要这个默认的环境变量没有写到nand中(运行saveenv)的话,uboot的每次启动都被认为是第一次启动。而保存过环境变量之后的话,在执行env_init的时候,就是从nand中读出了实际存在的环境变量参数,至于修不修改环境变量,保不保存,都没有上面的那种情况出现了。
gd->bd->bi_dram[0].size
的SDRAM开始地址和大小
RAM Configuration:
Bank#0: 30000000 64 MB
…
4.
5.
6.
7.
8.
9.
10.
11.
去掉所有无关紧要的宏和代码,main_loop()函数如下:
void main_loop()
{
int bootdelay;
bootdelay =
s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
//执行启动命令行,smdk2410.h中没有定义CONFIG_BOOTCOMMAND,所以没有命令执行。
//读取键入的命令行到console_buffer
//拷贝命令行到lastcommand.
}
12.
变量,然后将延时bootdelay设置成0,这样当u-boot跑到这里的时候就不会因为用户按下了任意键就进入了命令行模式,可以直接运行bootcmd的命令来直接加载kernel的Image然后移交控制权。如果进入了命令行模式,我们也可以手动输入命令来启动系统,输入的命令也是基本和bootcmd一样。
不过值得一提的是,从这里开始到引导内核的函数do_bootimg_linux()之前,不同
厂商之间做的都和原始的U-boot代码差别挺大,不过万变不离其宗,都是加载各种各样的Image到SDRAM中,不过关于CP部分的Image有的厂商是在这里加载,有的是kernel起来后来有kernel来加载,不过都需要加载的Image就是linuxkernel的Image。为了方便,只讨论加载kernel Image的情况。
Android就没有在ramdisk和zImage上单独重复加头了,不过近期做的mtk的平台,他们有点怪,除了上面的额外信息之外,还在这二者上单独加了标志字符串,ROOTFS和KERNEL。
tftp 0x30800000 uImage;bootm (地址可选)
或者
nand read 0x30800000 0x40000 0x200000 ; bootm
"mcu_clk 260;a7vector_SDRAM;dsp_clk 130;nand read 0x460000000x200000 0x400000;boot_from_flash boot"
很明显,原始U-boot中没有boot_from_flash命令,是经过他们改造过的。不过功能基本一样。所以还是以bootm来引导uImage为例来讨论。
);
在将nand上0x40000开始的2MB数据拷贝到SDRAM的0x30800000之后,就开始执行bootm命令,其所做的工作大致如下:
12.1如果bootm命令没有带地址参数,将会采用默认地址0x30800000,带地址则保存下这个参数地址。
12.2从SDRAM的0x30800000开始拷贝64字节到一个dead结构体中进行crc32校验,校验ok之后将会调用调用函数print_image_hdr()打印出如下信息:
Image Name:
Created:
Image Type:
Data Size:
Load Address: 30008000
Entry Point:
12.3 跳过64字节的head,开始校验kernel的Image数据,校验码ok之后会打印:Verifying Checksum... OK
12.4核对cpu类型
12.5 检查Image的类型
12.6禁止中断,检查内核的压缩类型,这里不是指的image和zImage的区别,而是有没有在这基础上进行ZIP或ZIP2的压缩。通常这里是没有这样的压缩的。所以接下来将0x30800000+64B开始的zImage数据搬运到ih_load(0x30008000)处,这个数据就是kernel的Image数据。
12.7根据head中OS的类型,如果是linux,head中类型值就是IH_OS_LINUX,所以接下来会执行u-boot到kernel的过渡程序。
do_bootm_linux(cmdtp, flag, argc, argv, addr, len_ptr, verify);
12.8定义thekernel函数指针,获取bootargs参数给commandline指针。
12.9 theKernel = (void (*)(int, int,uint))ntohl(hdr->ih_ep),将内核的入口地址赋给thekernel函数指针。
12.10将传递给内核的参数放在0x30000100处,以tag的方式存放,主要放置了memery和cmdline的参数。
12.11关中断,关闭IDCache,同时使ID Cache数据失效。
12.12再次获取bi_arch_number参数为553。
12.13 theKernel (0,bd->bi_arch_number,bd->bi_boot_params)进入内核,第一个参数必须为0,第二个参数为机器类型553,第三个参数为传递给内核参数的其实地址0x30000100。——这里就是uboot传给kernel的tag参数啊!
总结下,U-Boot调用内核之前,下面的条件必须满足:
a.
b.
c.
这里移交控制权之后,u-boot的使命就算是完成了。说起来U-boot命运挺悲惨的,因为它重要而却最不受内核待见。接下来内核的启动更加复杂。
参考网址:
1.
2.
3.
4.
一、zImage是怎样炼成的?
arch/arm/kernel/init_task.o、各子目录下的built-in.o、lib/lib.a、arch/arm/lib/lib.a生成顶层目录下的vmlinux(根据arch/arm/kernel/vmlinux.lds来链接 0xc0008000)
的调试信息,减小映像文件的大小,此时大概3M多,生成arch/arm/boot/Image。
4. gzip -f -9< arch/arm/boot/compressed/../Image >arch/arm/boot/compressed/piggy.gz,读入arch/arm/boot/Image的内容,以最大压缩比进行压缩,生成arch/arm/boot/compressed/目录下的piggy.gz。
5.arm-linux-gnu-gcc,在arch/arm/boot/compressed/piggy.S文件中是直接引入piggy.gz的内容(piggy.gz其实已经是二进制数据了),然后生成arch/arm/boot/compressed/piggy.o文件。下面是piggy.S的内容
其中所选择的行就是加入了piggy.gz的内容,通过编译生成piggy.o文件,以备后面接下来的ld链接。
6.arm-linux-gnu-ld,在arch/arm/boot/compressed/piggy.o的基础上,加入重定位地址和参数地址的同时,加入解压缩的代码(arch/arm/boot/compressed/head.o、misc.o),最后生成arch/arm/boot/compressed目录的vmlinux,此时在解压缩代码中还含有调试信息(根据arch/arm/boot/compressed/vmlinux.lds来链接0x0)vmlinux.lds开始处。
注意到了27行的吗?*(.piggydata)就表示需要将piggydata这个段放在这个位置,而piggydata这个段放的是什么呢?往后翻翻,看看第五步的图片,呵呵,其实就是将按最大压缩比压缩之后的Image,压缩之后叫piggy.gz中的二进制数据。
8. /bin/sh
/home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh -Aarm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n'Linux-2.6.14' -d arch/arm/boot/zImage arch/arm/boot/uImage
调用mkimage在arch/arm/boot/zImage的基础上加入64字节的uImage头,和入口地址,装载地址,最终生成arch/arm/boot/目录下的uImage文件。
1. 确定 processortype
7.最终跳转到start_kernel
(在__switch_data的结束的时候,调用了 b start_kernel)
二、linux的c启动阶段
息。
化,tag参数解析,u-boot传递的cmdline解析,建立mmu工作页表(memtable_init),初始化内存布局,调用mmap_io建立GPIO,IRQ,MEMCTRL,UART,及其他外设的静态映射表,对时钟,定时器,uart进行初始化,cpu_init():{打印一些关于cpu的信息,比如cpu id,cache大小等。另外重要的是设置了IRQ、ABT、UND三种模式的stack空间,分别都是12个字节。最后将系统切换到svc模式}。
3.sched_init():初始化每个处理器的可运行队列,设置系统初始化进程即0号进程。
4. 建立系统内存页区(zone)链表
5.printk(KERN_NOTICE"Kernel command line: %s\n",saved_command_line);打印出从uboot传递过来的command_line字符串,在setup_arch函数中获得的。
6.parse_early_param(),这里分析的是系统能够辨别的一些早期参数(这个函数甚至可以去掉,__setup的形式的参数),而且在分析的时候并不是以setup_arch(&command_line)传出来的command_line为基础,而是以最原生态的saved_command_line为基础的。
7.parse_args("Booting kernel", command_line, __start___param,
8.在前面的setup_arch-àpaging_init-àmemtable_init函数中为系统创建页表的时候,中断向量表的虚地址init_maps,是用alloc_bootmem_low_pages分配的,ARM规定中断向量表的地址只能是0或0xFFFF0000,所以该函数里有部分代码的作用就是映射一个物理页到0或0xFFFF0000。
trap_init函数做了以下的工作:把放在.Lcvectors处的系统8个意外入口跳转指令搬到高端中断向量0xffff0000处,再将__stubs_start到__stubs_end之间的各种意外初始化代码搬到0xffff0200处,等。
9. init_IRQ()
10.softirq_init():内核的软中断机制初始化函数。
12.
初始化系统的控制台结构,该函数执行后调用printk函数将log_buf中所有符合打印级别的系统信息打印到控制台上。
13.profile_init()函数
//profile是用来对系统剖析的,在系统调试的时候有用
//需要打开内核选项,并且在bootargs中有profile这一项才能开启这个功能
MACHINE_END
include/linux/init.h文件中有这些优先级的定义:
#definepure_initcall(fn)
#definecore_initcall(fn)
#definecore_initcall_sync(fn)
#definepostcore_initcall(fn)
#definepostcore_initcall_sync(fn)
#definearch_initcall(fn)
#definearch_initcall_sync(fn)
#definesubsys_initcall(fn)
#definesubsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#definefs_initcall(fn)
#definefs_initcall_sync(fn)
#definerootfs_initcall(fn)
#definedevice_initcall(fn)
#definedevice_initcall_sync(fn) __define_initcall("6s",fn,6s)
#definelate_initcall(fn)
#definelate_initcall_sync(fn)
当然函数的执行属性从1~7,通常我们见到的设备都是6、7级的。另外系统中所有的initcalll函数都是可以从linux根目录下的system.map中查看得到。
接下来的一段代码就是来释放前面提到的ramdisk.img的:
if(!ramdisk_execute_command)
if(sys_access((const char __user *) ramdisk_execute_command, 0) != 0){
}
释放出来的ramdisk呈现出来的目录就是android编译出来之后,在out/…/root的目录一样了,这个目录下有一个init可执行程序,下面就准备启动它。
哈哈,注意init.rc是放在ramdisk.img里的啊,要想执行init.rc(里启动zygote和systemservice),得先等kernel释放出ramdisk.img里所有目录文件先——但是不明白的是,为啥先device和driver注册后,才释放ramdisk.img呢——我怎么记得之前看log是反过来的顺序
接着调用init_post()函数,来打开console设备,这个时候我们的控制台就可以操作了,最后会执行以下代码来寻找和启动init程序:
if(execute_command) {
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No initfound.
这里执行的init程序需要我们在u-boot传给kernel的cmdline中使用init=/init
来告知kernel,或者kernel启动代码中直接写死。否则在上面的那些目录中找不到init的话,系统就用panic机制将这个警告信息保存在nand的panic分区,在下次启动的时候,会自动将这个分区的信息输出。
init进程是linux起来之后启动的第一个用户进程,android系统也就是在这个进
程的基础上启动的。进程号是1。
一、init进程
源码位于system/core/init目录。主要做了以下事情:
1.
act.sa_handler = sigchld_handler;
act.sa_flags = SA_NOCLDSTOP;
act.sa_mask = 0;
act.sa_restorer = NULL;
sigaction(SIGCHLD, &act, 0);
2. 将kernel启动过程中建立好的文件系统框架mount到相应目录。
mount("sysfs", "/sys", "sysfs", 0, NULL);
3.
4.
5.
5.1 先从文件/sys/class/BOOT/BOOT/boot/boot_mode读出启动方式:FactoryMode, '4';ATE Factory Mode, '6'。看是否是facatory模式。
5.2如果是的话,需要读取并解析两个文件:init.factory.rc和init.rc。
5.3 如果是正常启动,则暂时先读取init.rc。
这里在读取解析文件的时候,是以行为最小可执行单位在解析。关于书写init.rc文件的初始化脚本语言的规则,可以上网查找。解析之后并不会马上执行,而是在init进入服务循环之前统一根据其命令本身所带的条件来执行。
6.
7.
需要注意的是:对于service,这里会给每个服务建立一个structservice的结构体,全部挂入链表service_list之中,在init最后才启动。
8.
9.
10.
接着调用函数load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT)从/default.prop文件中加载编译时生成的属性。
11.
12.
property_set("ro.factorytest", "0")
property_set("ro.serialno",
property_set("ro.bootmode",
property_set("ro.baseband",
property_set("ro.carrier",
property_set("ro.bootloader",
property_set("ro.hardware",
snprintf(tmp, PROP_VALUE_MAX,"%d",
property_set("ro.revision", tmp);
13.
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
14.
先读取剩余三个文件中的属性:/system/build.prop、/system/default.prop、/system/default.prop,然后用函数load_persistent_properties()加载persist.开始的属性,这种属性都是保存在目录/data/property下的以属性名为文件名的中。
接下来创建一个名为property_service的socket接口(SOCK_STREAM),然后进入监听状态,等待属性事件到来。
15.
socketpair(AF_UNIX, SOCK_STREAM, 0, s),signal_fd =s[0],signal_recv_fd = s[1]。
17.执行init.rc中以property:开头的属性设置语句,同时使能属性触发方式。
queue_all_property_triggers();
drain_action_queue();
1.
struct pollfd ufds[4];
…
ufds[0].fd =
ufds[0].events = POLLIN;
ufds[1].fd =
ufds[1].events = POLLIN;
ufds[2].fd =
ufds[2].events = POLLIN;
for(;;) {
服务。restart_service_if_needed() à service_start(svc, NULL) àfork()
}
到这里init就进入了死循环中一直在监听ufds中的4个文件描述符的动静,如果有POLLIN的事件,就做相应的处理,所以init并没有退出或者进入idle,而是被当做一个服务在运行。第4个文件描述符是keychord_fd,暂时不清楚这个怎么用,不过通过它也可以启动服务,可参考源码。
下面是init.rc的例子,见附件init.rc
二、init中启动的各种服务
在init中启动起来的服务按照init.rc中的先后顺序,大致有:
console: start a shell,code path:system/bin/sh,其源码中包含常用的shell命令,如ls,cd等。
adbd: start adb daemon,通常带有disabled的选项,表明需要按名字启动,codepath:system/bin/adb。
servicemanager:这个服务管理着系统内所有binder services。code path:frameworks/base/cmds/servicemanager。
Vold: android 的udev,code path: system/vold。
Netd: start ntd daemon, code path: system/netd。
Debuggerd: start debug system, code path:system/core/debuggerd。
zygote:['zaigəut]这是一个非常重要的服务——zygnote也是一个服务,稍后详解。start Android
media: addAudioFlinger,AudioPolicyService,MediaPlayerService andCameraService toservicemanager,同时启动管理binder通讯的机制,依靠这两个类来完成binder机制在android中间层所体现的功能:ProcessState和IPCThreadState。Code path:frameworks/base/media/mediaserver。
bootanim: 开机动画和铃声,code path:frameworks/base/cmds/bootanimation。
接下来就是关于modem的服务,如:ccci_fsd、ccci_mdinit、pppd_gprs、pppd、gsm0710muxd、muxtestapp、sockcli、socksrv、muxreport、ril-daemon等,除了前面2个,后面的都带有disabled的参数,需要按名启动。
Installd: start install package daemon, code path:
frameworks/base/cmds/installd。
后面还有很多关于其他硬件的服务,比如BT、WIFI等。
————看来,media,modemn,wifi,bt都是从启动服务开始
2.1 servicemanager
#define BINDER_SERVICE_MANAGER ((void*)0)
int main(int argc, char **argv)
{
}
一些zygote进程启动的前期工作,如,启动runtime运行时环境(实例),参数分解,设置startSystemServer标志,接着用runtime.start()来执行zygote服务的代码,其实说简单点,就是zygote抢了app_process这个进程的躯壳,改了名字,将后面的代码换成zygote的main函数,这样顺利地过度到了zygote服务进程。这样我们在控制台用ps看系统所有进程,就不会看到app_process,取而代之的是zygote。
这个函数中,会新建并启动一个虚拟机实例来执行com.android.internal.os.ZygoteInit这个包的main函数。这个main函数中会fork一个子进程来启动systemserver,父进程就作为真正的孵化进程存在了,每当系统要求执行一个Android应用程序,Zygote就会收到socket消息FORK出一个子进程来执行该应用程序。因为Zygote进程是在系统启动时产生的,它会完成虚拟机的初始化,库的加载,预置类库的加载和初始化等操作,而在系统需要一个新的虚拟机实例时可以快速地制造出一个虚拟机出来。
2.2.1 app_process
下面重点讨论zygote服务,源码位于frameworks/base/cmds/app_process。
service zygote /system/bin/app_process -Xzygote /system/bin--zygote --start-system-server
参数:/system/bin/app_process -Xzygote /system/bin --zygote--start-system-server
int main(int argc, const char* const argv[])
{
argv++;
// startSystemServer = true,这个bool变量决定着后面执行runtime.start时是否启动systemserver。
} // main()
const bool startSystemServer){
LOGD("\n>>>>>>>>>>>>>>AndroidRuntime START<<<<<<<<<<<<<<\n");
…
goto bail;
}
//启动虚拟机之前需要构造java形式的参数数组,如下:
jclass stringClass;
jobjectArray strArray;
stringClass =env->FindClass("java/lang/String");
strArray = env->NewObjectArray(2, stringClass,NULL);
classNameStr = env->NewStringUTF(className);
env->SetObjectArrayElement(strArray, 0,classNameStr);
startSystemServerStr =env->NewStringUTF(startSystemServer ?
env->SetObjectArrayElement(strArray, 1,startSystemServerStr);
jclass startClass;
jmethodID startMeth;
slashClassName = strdup(className);
for (cp = slashClassName; *cp != '\0'; cp++)
startClass =env->FindClass(slashClassName); //根据路径找到这个包
if (startClass == NULL) {
…
}
}
LOGD("Shutting down VM\n");
bail:
}
} // start()
gc();
if (argv[1].equals("true")) {
} else {
}
closeServerSocket();
}
从这里开始android启动分为两条线走,分别是:
startSystemServer(); ---------- Zygote的子进程
runSelectLoopMode();
2.2.4 systemserver
à invokeStaticMain(startClass, startArgs)
à ZygoteInit.MethodAndArgsCaller(m, argv)
This method is called from Zygote to initialize the system. Thiswill cause the nativeservices (SurfaceFlinger, AudioFlinger, etc..)to be started. After that it will call backup into init2() to startthe Android services.
native public static void init1(String[] args);
public static void main(String[] args) {
System.loadLibrary("android_servers");//libandroid_servers.so是由目录frameworks/base/services/jni下的源码编译所得
init1(args); //init1实际上是一个jni本地接口,位于文件frameworks\base\services\jni\com_android_server_SystemServer.cpp文件中system_init()函数
}
init1接口位于com_android_server_SystemServer.cpp中:
extern "C" int system_init();
static void android_server_SystemServer_init1(JNIEnv* env, jobjectclazz){
}
static JNINativeMethod gMethods[] = {
{"init1","([Ljava/lang/String;)V",(void*)
android_server_SystemServer_init1 },
};
而函数system_init()位于文件
frameworks\base\cmds\system_server\library\System_init.cpp
extern "C" status_t system_init()
{
…
}
com.android.server.SystemServer包的init2函数开启一个ServerThread线程:
public static final void init2() {
}
ServerThread线程的run函数会启动系统中绝大部分的androidservice,并最后进入Loop.loop(),,,,(SystemServer.java)
public void run() {
服务,如:Lights、Vibrator、Alarm、Sensor、Bluetooth、InputMethod、NetStat、NetworkManagement、Connectivity、Mount、Notification、Audio等。在这些服务都启动完了之后。
}
2.2.5 home界面启动
((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady(.)
函数调用的过程中启动,其中systemReady()的参数是一段callback代码,如上面灰色显示的部分。
这个函数的实现部分在文件:ActivityManagerService.java中。
public void systemReady(final Runnable goingCallback) {
}
private final boolean resumeTopActivityLocked(HistoryRecord prev){
}
private boolean startHomeActivityLocked() {
}
三、android启动图示
参考网址:
1.
2.
3.
4.