第二部分:linux启动
一、zImage是怎样炼成的?
zImage是linux内核编译之后产生的最终文件,它的生成过程比较复杂,这里不谈编译过程,只聊聊编译的最后阶段:
1. arm-linux-gnu-ld用arch/arm/kernel/vmlinux.lds、arch/arm/kernel/head.o、
arch/arm/kernel/init_task.o、各子目录下的built-in.o、lib/lib.a 、arch/arm/lib/lib.a生成顶层目录下的vmlinux (根据arch/arm/kernel/vmlinux.lds来链接 0xc0008000)
2. 生成system.map, 置于顶层目录之下。
3. arm-linux-gnu-objcopy,去掉顶层vmlinux两个段-R .note -R .comment
的调试信息,减小映像文件的大小,此时大概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中的二进制数据。
7. arm-linux-gnu-objcopy,去掉解压缩代码中的调试信息段,最后生成arch/arm/boot/目录下的zImage。
8. /bin/sh
/home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh -A arm -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文件。
实际上zImage是经过了高压缩之后在和解压缩程序合并在一起生成的。知道了这些之后,我们就可以给linux的启动大致分成3段:zImage解压缩、kernel的汇编启动阶段、kernel的c启动阶段。
前两个阶段因为都是汇编写成的,代码读起来晦涩难懂,内存分布复杂,涉及MMU、解压缩等众多知识。如果有对这部分感兴趣的,可以自行分析,遇到问题可以上网查资料或者找我,这里就不详细分析了。下面是第二阶段汇编启动的主线,可以了解下:
1. 确定 processor type
2. 确定 machine type
3. 手动创建页表
4. 调用平台特定的cpu setup函数,设置中断地址,刷新Cache,开启Cache
(在struct proc_info_list中,in proc-arm920.S)
5. 开启mmu I、D cache ,设置cp15的控制寄存器,设置TTB寄存器为0x30004000
6. 切换数据(根据需要赋值数据段,清bss段,保存processor ID 和 machine type
和 cp15的控制寄存器值)
7. 最终跳转到start_kernel
(在__switch_data的结束的时候,调用了 b start_kernel)
二、linux的c启动阶段
经过解压缩和汇编启动两个阶段,将会进入init/Main.c中的start_kernel()函数去继续执行。(2.6.1x、2.6.2x和2.6.3x之间的差异比较大,下面的分析基于2.6.14)
注:uboot入口在:arch/arm/lib/board.c中,有board_init_r,和board_init_f函数。键盘进入命令行在common/main.c
1. printk(linux_banner)打印内核的一些信息,版本,作者,编译器版本,日期等信
息。
2. 接下来执行是一个及其重要的函数setup_arch(),主要做一些板级初始化,cpu初始
化,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)链表 build_all_zonelists()。
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,
__stop___param - __start___param,
&unknown_bootoption);
对于比较新的版本真正起作用的函数,与parse_early_param()相比,此处对解析列表的处理范围加大了,解析列表中除了包括系统以setup定义的启动参数,还包括模块中定义的param参数以及系统不能辨别的参数。
__start___param是param参数的起始地址,在System.map文件中能看到
__stop___param - __start___param是参数个数
unknown_bootoption是对应与启动参数不是param的相应处理函数(查看parse_one()就知道怎么回事)。
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()
初始化系统中所有的中断描述结构数组:irq_desc[NR_IRQS]。接着执行init_arch_irq函数,该函数是在setup_arch函数最后初始化的一个全局函数指针,指向了smdk2410_init_irq函数(in mach-smdk2410.c),实际上是调用了s3c24xx_init_irq函数。在该函数中,首先清除所有的中断未决标志,之后就初始化中断的触发方式和屏蔽位,还有中断句柄初始化,这里不是最终用户的中断函数,而是do_level_IRQ或者do_edge_IRQ函数,在这两个函数中都使用过__do_irq函数来找到真正最终驱动程序注册在系统中的中断处理函数。
10. softirq_init():内核的软中断机制初始化函数。
12. console_init():
初始化系统的控制台结构,该函数执行后调用printk函数将log_buf中所有符合打印级别的系统信息打印到控制台上。
13. profile_init()函数
/* 对系统剖析做相关初始化, 系统剖析用于系统调用*/
//profile是用来对系统剖析的,在系统调试的时候有用
//需要打开内核选项,并且在bootargs中有profile这一项才能开启这个功能/*
profile只是内核的一个调试性能的工具,这个可以通过menuconfig中profiling support打开。
14. vfs_caches_init()
该函数主要完成的是文件系统相关的初始化,cache、inode等高速缓存的建
立,在mnt_init()函数中有注册并初始化sysfs、rootfs文件系统,这里只是在内存中建立他们的架构,创建了超级块,并没有真正挂载上去。关于这个rootfs需要说明的是,这个文件系统生命期更加短暂的,为什么?之前说的ramdisk大家是否还记得,ramdisk即将在后面释放到内存空间,来代替这里的rootfs出现在根目录之下,而这个rootfs则退居二线,隐藏在一个二级目录中。本来在非android的系统上,这个 ramdisk也是一个暂时的文件系统,之后也会被真正的yaffs2之类的文件系统替换。不过呢,在android上,这个ramdisk还是挂载在根目录下的,只是将system、userdata等真实文件系统挂载了对应的二级目录下。
关于这部分ramdisk内容,有兴趣的下来可以继续探讨。
15. mem_init():
最后内存初始化,释放前边标志为保留的所有页面,这个函数结束之后就不能再使
用alloc_bootmem(),alloc_bootmem_low(),alloc_bootmem_pages()等申请低端内存的函数来申请内存,也就不能申请大块的连续物理内存了。
16. 中间还省略了很多内容,涉及到很多东西,这里也没有时间详细讨论,有兴趣的自
己研究代码吧!下面直接跳到start_kernel()函数的最后的一个重要函数:rest_init()。
17. rest_init函数创建了两个线程之后,自己调用cpu_idle()函数隐退了。
创建的第一个线程,习惯上我们将其叫做1号内核线程,第二个线程叫2号内核线程,因为创建它们的父进程叫0号启动进程。
说明一下:2.6.14的内核这里只创建了一个内核线程叫init线程,而上面创建两
个线程的内核版本至少都是2.6.2x了,所以为了后面能和android的启动接上,所以这里开始linux转到2.2.29去。
static noinline void __init_refok rest_init(void) __releases(kernel_lock)
{
int pid;
…
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
…
cpu_idle();
}
kthreadd这个线程之前的部门交流会上讨论过,新版本的linux将线程创建这个艰巨的工作专门交给了这个叫kthreadd的线程来完成。
接下来既然0号启动进程idle了,那么剩下的工作就都转移到线程kernel_init中去了。
18. kernel_init()
这个线程的任务还是比较艰巨的,第一个重要任务就是调用函数
do_basic_setup(),先调用driver_init()来构建sysfs的目录架构,然后调用do_initcalls()函数来一次执行linux编译时设置的系统函数。
这里主要工作就是注册系统设备的驱动程序,关于driver和device的注册顺序,是可以互相交换,例如通常的三星平台都有一个struct machine_desc结构体来描述平台相关的启动代码:
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
MACHINE_END
所有devices的注册都是在smdk2410_init()函数中调用函数:
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
来完成,所以drivers的注册就放在后面了。不过这样注册是有一个坏处的,就是不能准确地控制driver代码中probe的执行先后顺序。
现在mtk平台上的devices和drivers注册顺序想法,也就是先注册上drivers,然后再注册devices,这样的话,就可以控制probe函数的执行先后。
include/linux/init.h文件中有这些优先级的定义:
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
当然函数的执行属性从1~7,通常我们见到的设备都是6、7级的。另外系统中所有的initcalll函数都是可以从linux根目录下的system.map中查看得到。
接下来的一段代码就是来释放前面提到的ramdisk.img的:
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
释放出来的ramdisk呈现出来的目录就是android编译出来之后,在out/…/root的目录一样了,这个目录下有一个init可执行程序,下面就准备启动它。
接着调用init_post()函数,来打开console设备,这个时候我们的控制台就可以操作了,最后会执行以下代码来寻找和启动init程序:
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
这里执行的init程序需要我们在u-boot传给kernel的cmdline中使用init=/init
来告知kernel,或者kernel启动代码中直接写死。否则在上面的那些目录中找不到init的话,系统就用panic机制将这个警告信息保存在nand的panic分区,在下次启动的时候,会自动将这个分区的信息输出。
init进程是linux起来之后启动的第一个用户进程,android系统也就是在这个进
程的基础上启动的。进程号是1。
第三部分:Android启动
Android的启动过程是从进程init开始的,所以它是后续所有进程的祖先进程。
一、init进程
源码位于system/core/init目录。主要做了以下事情:
system/core/init/signal_handler.c =》signal_init(void)
1. 重新设置子进程终止时信号SIGCHLD的处理函数。
act.sa_handler = sigchld_handler; //调用了wait函数等待子进程退出。
act.sa_flags = SA_NOCLDSTOP;
act.sa_mask = 0;
act.sa_restorer = NULL;
sigaction(SIGCHLD, &act, 0);
2. 将kernel启动过程中建立好的文件系统框架mount到相应目录。
system/core/init/init.c
mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
…
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
3. open_devnull_stdio(),将init进程的标准输入、输出、出错设备设置为新建的设备节点/dev/__null__。
4. log_init(),创建并打开设备节点/dev/__kmsg__。
5. 读取并解析rc配置文件。
5.1 先从文件/sys/class/BOOT/BOOT/boot/boot_mode读出启动方式:Factory Mode, '4';ATE Factory Mode, '6'。看是否是facatory模式。
5.2 如果是的话,需要读取并解析两个文件:init.factory.rc和init.rc。
5.3 如果是正常启动,则暂时先读取init.rc。
这里在读取解析文件的时候,是以行为最小可执行单位在解析。关于书写init.rc文件的初始化脚本语言的规则,可以上网查找。解析之后并不会马上执行,而是在init进入服务循环之前统一根据其命令本身所带的条件来执行。
6. 导入kernel的cmdline,也就是u-boot传递给kernel的参数,查看其中是否具有 androidboot.xxx(androidboot.mode、androidboot.hardware等)参数,如果有,将其保存在同名字的 xxx(mode、hardware)全局变量中。这里特别说明的是hardware这个参数,从kernel中导出一部分之后,又要从/proc /cpuinfo中导出一部分来组合成完整的hardware参数,因为后面接下来会读取并解析以特定平台的rc文件。
7. 读取特定平台相关的initrc文件,如:init.mt6516.rc。
需要注意的是:对于service,这里会给每个服务建立一个struct service的结构体,全部挂入链表service_list之中,在init最后才启动。
8. 检查解析出来的所有命令行当中是否有属于early-init的,如果有,将其提出来加入到链表action_queue之中,马上将其执行掉。
9. device_init()函数将会打开uevent的netlink socket,遍历/sys/class、/sys/block、/sys/devices目录,检查各级目录的uevent文件,处理在vold服务起 来之前由kernel所发出来的device add, remove等事件。
10. property_init(), 顾名思义,是属性初始化。首先创建一个名字为system_properties的匿名共享内存区域,对并本init进程做mmap读写映射,其余共享它 的进程只有读的权限。然后将这个prop_area结构体通过全局变量__system_property_area__传递给property services。
接着调用函数load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT)从/default.prop文件中加载编译时生成的属性。
11. 如果在root目录下有initlogo.rle文件存在,这个是两张android字样的缕空图片,将其读入fb中显示到LCD上。同时也要往串口上输出" A N D R O I D "。如果图片不存在,就没有这两项的输出。
12. 设置相应的属性:
property_set("ro.factorytest", "0")
property_set("ro.serialno", serialno[0] ? serialno : "");
property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
property_set("ro.baseband", baseband[0] ? baseband : "unknown");
property_set("ro.carrier", carrier[0] ? carrier : "unknown");
property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
property_set("ro.hardware", hardware);
snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
property_set("ro.revision", tmp);
13. 开始执行以init为trigger的命令行:
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
前面有执行过eraly-init的。
14. 启动property(属性) service:property_set_fd = start_property_service();
先 读取剩余三个文件中的属性:/system/build.prop、/system/default.prop、/system /default.prop,然后用函数load_persistent_properties()加载persist.开始的属性,这种属性都是保存在 目录/data/property下的以属性名为文件名的中。
接下来创建一个名为property_service的socket接口(SOCK_STREAM),然后进入监听状态,等待属性事件到来。
15. 创建一对socket,用来做信号方面的处理。
socketpair(AF_UNIX, SOCK_STREAM, 0, s),signal_fd = s[0],signal_recv_fd = s[1]。
16.执行eraly-boot和boot为trigger的命令
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
drain_action_queue();
17.执行init.rc中以property:开头的属性设置语句,同时使能属性触发方式。
queue_all_property_triggers();
drain_action_queue();
property_triggers_enabled = 1; // 可以执行那些以属性为条件的init语句。
1. 接下来就是利用poll机制监听前面创建的几个fd的动态。
struct pollfd ufds[4];
…
ufds[0].fd = device_fd;
ufds[0].events = POLLIN;
ufds[1].fd = property_set_fd;
ufds[1].events = POLLIN;
ufds[2].fd = signal_recv_fd;
ufds[2].events = POLLIN;
for(;;) {
int nr, i, timeout = -1;
for (i = 0; i < fd_count; i++)
ufds[i].revents = 0;
drain_action_queue(); //执行action_queue链表中后来新出现的command。
restart_processes(); // 第一次启动所有服务,也包括后来restart这些
服务。restart_service_if_needed() à service_start(svc, NULL) à fork()
…
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
if (ufds[2].revents == POLLIN) {
/* we got a SIGCHLD - reap and restart as needed */
read(signal_recv_fd, tmp, sizeof(tmp));
while (!wait_for_one_process(0))
;
continue;
}
if (ufds[0].revents == POLLIN)
handle_device_fd(device_fd); // Vold的netlink类型的socket
if (ufds[1].revents == POLLIN)
handle_property_set_fd(property_set_fd);//property service的socket
if (ufds[3].revents == POLLIN)
handle_keychord(keychord_fd);
}
到 这里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的选项,表明需要按名字启动,code path: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: ['zaigut]这是一个非常重要的服务,稍后详解。start Android Java Runtime and start systemserver。code path:frameworks/base/cmds/app_process。
media: add AudioFlinger,AudioPolicyService,MediaPlayerService and CameraService to servicemanager,同时启动管理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等。
2.1 servicemanager //frameworks/base/cmds/servicemanager/service_manager.c
这个服务进程代码比较简单,功能也简单,c实现的,用来管理系统中所有的binder service,不管是本地的c++实现的还是java语言实现的都需要这个进程来统一管理,最主要的管理就是,注册添加服务,获取服务。
这些binder服务在对外公开之前都必须将自身拥有的binder实体注册到SMgr中,而其他进程如果需要使用binder service的功能,也必须先经过SMgr取得 binder service中的binder实体在SMgr中对应的引用号(binder的引用号和进程中文件描述符概念类似,有效范围只限于本进程)。
#define BINDER_SERVICE_MANAGER ((void*) 0)
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
// 打开binder设备,mmap映射当前进程的binder接收缓冲区,返回一个binder_state结构体 。
if (binder_become_context_manager(bs)) {
// 通过这个函数将当前进程设置成服务管理进程MSgr,整个系统就这一个。
…
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
/*svcmgr_handler作为处理函数,所能完成的操作有:获取service,查看service是否存在,添加service ,列出service清单。其中用的最多的就是获取、添加。*/
return 0;
}
2.2 zygote //frameworks/base/cmds/app_process/app_main.cpp
zygote服务进程也叫做孵化进程,在linux的用户空间,进程app_process会做
一 些zygote进程启动的前期工作,如,启动runtime运行时环境(实例),参数分解,设置startSystemServer标志,接着用 runtime.start()来执行zygote服务的代码,其实说简单点,就是zygote抢了app_process这个进程的躯壳,改了名字,将 后面的代码换成zygote的main函数,这样顺利地过度到了zygote服务进程。这样我们在控制台用ps看系统所有进程,就不会看到 app_process,取而代之的是zygote。
而前面runtime.start()这个函数实际上是类函数AndroidRuntime::start(),在
这 个函数中,会新建并启动一个虚拟机实例来执行com.android.internal.os.ZygoteInit这个包的main函数。这个 main函数中会fork一个子进程来启动systemserver,父进程就作为真正的孵化进程存在了,每当系统要求执行一个 Android应用程序,Zygote就会收到socket消息FORK出一个子进程来执行该应用程序。因为Zygote进程是在系统启动时产生的,它会 完成虚拟机的初始化,库的加载,预置类库的加载和初始化等操作,而在系统需要一个新的虚拟机实例时可以快速地制造出一个虚拟机出来。
每一个Android应用都运行在一个Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管 理,Mutex等等都是依赖底层linux实现的。所以android应用程序中创建了线程将会实际调用到linux的线程创建接口,和虚拟机所在进程共 享一个虚拟机实例对java代码执行。
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[])
{
…
AppRuntime runtime; // 这里启动runtime运行时环境(实例),AppRuntime是AndroidRuntime的子类,在创建这个对象的时候会依次调用基类和子类的构造函数。
const char *arg;
const char *argv0;
…
argv0 = argv[0];
// ignore argv[0]
argc--;
argv++;
/*
argc = 4;
argv[0] = “-Xzygote”;
argv[1] = “/system/bin”;
argv[2] = “--zygote”;
argv[3] = “–start-system-server”;
*/
int i = runtime.addVmArguments(argc, argv); // 找到参数中第一个不是以单个-开始的参数,这里很明显是第二个参数:/system/bin
if (i < argc) {
runtime.mParentDir = argv[i++]; // 将命令目录保存在mParentDir中,之后i = 2。
}
if (i < argc) {
arg = argv[i++];
if (0 == strcmp("--zygote", arg)) { // 通常这个分支是成立的
bool startSystemServer = (i < argc) ?
strcmp(argv[i], "--start-system-server") == 0 : false;
// startSystemServer = true ,这个bool变量决定着后面执行runtime.start时是否启动systemserver。
setArgv0(argv0, "zygote");
set_process_name("zygote");
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);
} else {
… // 只启动AndroidRuntime
}
}else{
… // 错误处理
}
} // main()
2.2.2 AndroidRuntime
下面进一步分析
runtime.start("com.android.internal.os.ZygoteInit",startSystemServer);
位于文件frameworks/base/core/jni/ AndroidRuntime.cpp,类定义于:
frameworks/base/include/android_runtime/AndroidRuntime.h
void AndroidRuntime::start(const char* className,
const bool startSystemServer){
LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
…
JNIEnv* env;
…
/* start the virtual machine , mJavaVM是类AndroidRuntime的私有变量,env该函数中局部变量 */
if (startVm(&mJavaVM, &env) != 0)
goto bail;
/* Register android functions.向刚刚新建的虚拟机注册JNI本地接口。frameworks/base/core/jni/这个目录下的所有jni接口。*/
if (startReg(env) < 0) {
…
goto bail;
}
//启动虚拟机之前需要构造java形式的参数数组,如下:
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
jstring startSystemServerStr;
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 ?
"true" : "false");
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
/* strArray[0] = “com.android.internal.os.ZygoteInit”
strArry[1] = “true”*/
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
jclass startClass;
jmethodID startMeth;
slashClassName = strdup(className);
for (cp = slashClassName; *cp != '\0'; cp++)
if (*cp == '.')
*cp = '/'; // 将包名换成路径
startClass = env->FindClass(slashClassName); // 根据路径找到这个包
// com.android.internal.os.ZygoteInit,这个类位于文件
// com/ndroid/nternal/os/ZygoteInit.java
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
/*这个参数决定了我们接下来执行的是zygoteInit.java的main函数。*/
startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
// 虚拟机启动,将会调用到com.android.internal.os.ZygoteInit包的main函数。
…
}
}
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
bail:
free(slashClassName);
}
} // start()
2.2.3 ZygoteInit
下面进入com.android.internal.os.ZygoteInit 包的main函数:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
try {
…
registerZygoteSocket(); // Registers a server socket for zygote command
// load classs and resources
preloadClasses();
preloadResources();
…
// Do an initial gc to clean up after startup
gc();/*初始化GC垃圾回收机制*/
/* 通过从前面传递过来的第二个参数startsystemserver=”true” 启动systemserver, 在startSystemServer()中会fork一个新的进程命名为system_server, 执行的是com.android.server包中的SystemServer.java文件中的main函数, 源码位置:frameworks/base/services/java/com/android/server/ SystemServer.java。*/
if (argv[1].equals("true")) {
startSystemServer(); ///*************
} else if(…)
…
if (ZYGOTE_FORK_MODE) {
runForkMode(); /* ZYGOTE_FORK_MODE 永远为flase */
} else {
runSelectLoopMode();/* Zygote进程进入无限循环,不再返回。接下来的zygote将会作为一个孵化服务进程来运行。*/
}
closeServerSocket();
}
…
} // end main()
从这里开始android启动分为两条线走,分别是:
startSystemServer(); ---------- Zygote的子进程
runSelectLoopMode(); /* Zygote进程进入无限循环,不再返回,执行孵化工作。*/
2.2.4 systemserver
上面的startSystemServer()函数中,做了如下的调用:
startSystemServer()
à Zygote.forkSystemServer()
à 父进程直接返回true,子进程执行handleSystemServerProcess()
à RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
/*
* Pass the remaining arguments to SystemServer.
* "--nice-name=system_server com.android.server.SystemServer"
*/
/*上面这个RuntimeInit包定于于文件frameworks\base\core\java\com\android\internal\os\RuntimeInit.java */
à invokeStaticMain(startClass, startArgs)
/* 通过该函数调用执行startClass类的main函数,带参数 system_server 。*/
à ZygoteInit.MethodAndArgsCaller(m, argv)
/* 得到包com.android.server.SystemServer的main()函数。然后执行。*/
下面就开始调用到com.android.server.SystemServer类的main函数,源码位于:
frameworks/base/services/java/com/android/server/SystemServer.java
This method is called from Zygote to initialize the system. This will cause the nativeservices (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call backup into init2() to start the Android services.
// main--->init1(system_init)--->init2(systemThread)
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, jobject clazz){
system_init();
}
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"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()
{
…
char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsurfaceflinger", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
// Start the SurfaceFlinger
SurfaceFlinger::instantiate();
}
if (!proc->supportsProcesses()) {
// Start the AudioFlinger
AudioFlinger::instantiate();
// Start the media playback service
MediaPlayerService::instantiate();
// Start the camera service
CameraService::instantiate();
// Start the audio policy service
AudioPolicyService::instantiate();
//start appc service
APPCService::instantiate();
}
…
AndroidRuntime* runtime = AndroidRuntime::getRuntime();
runtime->callStatic("com/android/server/SystemServer", "init2");
// 执行com.android.server.SystemServer类的init2函数
…
}
com.android.server.SystemServer包的init2函数开启一个ServerThread线程:
public static final void init2() {
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
}
ServerThread线程的run函数会启动系统中绝大部分的android service,并最后进入Loop.loop(),,,,(SystemServer.java)
public void run() {
…
// Critical services...
try {
…
Slog.i(TAG, "Power Manager");
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
Slog.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
…
Slog.i(TAG, "Package Manager");
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF);
…
Slog.i(TAG, "Content Manager");
ContentService.main(context,
factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);
…
Slog.i(TAG, "Battery Service");
battery = new BatteryService(context);
ServiceManager.addService("battery", battery);
…
其余addservice的过程类似,只是启动的不同服务罢了,后面还启动了很多
服 务,如:Lights、Vibrator、Alarm、Sensor、Bluetooth、Input Method、NetStat、NetworkManagement、Connectivity、Mount、Notification、Audio等。 在这些服务都启动完了之后。
…
…
… // run()函数的后半部分
// It is now time to start up the app processes...
使用xxx.systemReady()通知各个服务,系统已经就绪。
…
((ActivityManagerService)ActivityManagerNative.getDefault())
.systemReady(new Runnable() {
public void run() {
…
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
if (uiModeF != null) uiModeF.systemReady();
if (recognitionF != null) recognitionF.systemReady();
Watchdog.getInstance().start();
…
}
});
…
Looper.loop(); // Run the message queue in this thread。
…
}
2.2.5 home界面启动
Home在
((ActivityManagerService)ActivityManagerNative.getDefault()).systemReady(.)
函数调用的过程中启动,其中systemReady()的参数是一段callback代码,如上面灰色显示的部分。
这个函数的实现部分在文件:ActivityManagerService.java中。
public void systemReady(final Runnable goingCallback) {
…
if (mSystemReady) {
if (goingCallback != null) goingCallback.run();
return; // 执行回调
}
…
resumeTopActivityLocked(null);
}
private final boolean resumeTopActivityLocked(HistoryRecord prev) {
…
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
return startHomeActivityLocked();
}
…
}
private boolean startHomeActivityLocked() {
…
if (aInfo != null) {
…
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() |
Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false); // 这里启动home
}
}
…
}
三、android启动图示
*****************************apk***********************************************
ApplicationsProvider.apk //应用程序支持服务(删除后不能连接电脑同步电话号码)
BackupRestoreConfirmation.apk //
BasicDreams.apk //
Browser.apk //谷歌浏览器(可删)
CertInstaller.apk //证书安装程序
DefaultContainerService.apk //
DeviceTest_Rk_4.2.apk
DrmProvider.apk //DRM数字版权管理
FusedLocation.apk //
GmsCore.apk
HTMLViewer.apk //在手机上可以看html格式小说(可删)
HoloSpiralWallpaper.apk //
InputDevices.apk
KeyChain.apk
LatinIME.apk
Launcher2.apk
MagicSmokeWallpapers.apk
MediaFloat.apk
MediaProvider.apk
MediaUploader.apk
NetworkLocation.apk
NoiseField.apk
OneTimeInitializer.apk
OpenWnn.apk
PackageInstaller.apk
PhaseBeam.apk
PhotoTable.apk
PinyinIME.apk
Provision.apk
RKUpdateService.apk
RkApkinstaller.apk
RkExplorer.apk
RkVideoPlayer.apk
Settings.apk
SettingsProvider.apk
SharedStorageBackup.apk
Stk.apk
SystemUI.apk
UserDictionaryProvider.apk
VpnDialogs.apk
flashplayer
***********************************************************