android开机过程.docx
《android开机过程.docx》由会员分享,可在线阅读,更多相关《android开机过程.docx(30页珍藏版)》请在冰豆网上搜索。
android开机过程
一、Android开机启动流程简介
1、OS-level:
由bootloader载入linuxkernel后kernel开始初始化,并载入built-in的驱动程序。
Kernel完成开机后,载入initprocess,切换至user-space。
Init进程是第一个在user-space启动的进程。
2、Android-level:
由initprocess读取init.rc,Native服务启动,并启动重要的外部程序,例如:
servicemanager、Zygote以及SystemServer等。
由initprocess根据硬件类型读取init.xxx.rc。
由init.xxx.rc加载init.xxx.sh。
由init.xxx.sh加载特定的硬件驱动。
如hi_tuner.ko、hi_demux.ko等。
3、Zygote-Mode:
Zygote启动完SystemServer后,进入ZygoteMode,在Socket等候命令。
随后,使用者将看到一个桌面环境(HomeScreen)。
桌面环境由一个名为[Launcher]的应用程序负责提供。
本文档重点研究Android-level中的启动流程。
启动流程如下图所示:
二、initprocess流程分析
init进程简介
init进程是第一个在user-space启动的进程。
由内核启动参数[init]传递给内核,如果该项没有设置,内核会按
/etc/init,/bin/init,/sbin/init,/bin/sh的顺序进行尝试,如果都有的都没找到,内核会抛出kernelpanic:
的错误。
init进程包含在跟文件系统中,跟文件系统被直接编译到了内核。
对init进程的修改,需要重新编译烧写内核。
编译命令:
#cdkernel
#makeuImag
CONFIG_INITRAMFS_SOURCE=../out/target/product/Hi3716C/root/-j8
#cpkernel/arch/arm/boot/uImageout/target/product/Hi3716C/kernel-afv
源代码路径:
froyo\system\core\init
编译的文件:
builtins.c
init.c
devices.c
property_service.c
util.c
parser.c
logo.c
编译命令:
makeinit
生成的文件:
/out/target/product/Hi3716C/root/init
init进程启动流程
1.安装SIGCHLD信号。
sigaction(SIGCHLD,&act,0);
如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。
因此需要对SIGCHLD信号做出处理,回收僵尸进程的资源,避免造成不必要的资源浪费。
2.对umask进行清零。
umask(0);
umask是什么?
登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?
这就是umask干的事情。
umask设置了用户创建文件的默认权限,它与chmod的效果刚好相反,umask设置的是权限“补码”,而chmod设置的是文件权限码。
设置文件的默认权限,umask和chmod的权限刚好反的,umask的0000相当于chmod的0777。
3.为rootfs建立必要的文件夹,并挂载适当的分区。
mkdir("/dev",0755);
mkdir("/proc",0755);
mkdir("/sys",0755);
mount("tmpfs","/dev","tmpfs",0,"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);
4.创建/dev/null和/dev/kmsg节点。
创建/dev/null结点,重定向标准输入,输出以及标准出错。
open_devnull_stdio();
创建/dev/kmsg设备结点,我们可以利用这个设备输出调试信息
log_init();
5.解析/init.rc,将所有服务和操作信息加入链表。
解析初始化脚本,这里只是parse,将脚本解析到一个链表中,并没有执行。
parse_config_file("/init.rc");
6.从/proc/cmdline中提取信息内核启动参数,并保存到全局变量。
qemu_init();
import_kernel_cmdline(0);
7.根据6获得的hardware参数信息,解析额外的硬件相关init脚本.
hi3726c中为init.godbox.rc
get_hardware_name();
snprintf(tmp,sizeof(tmp),"/init.%s.rc",hardware);
parse_config_file(tmp);
8.找到init.rcpaser链表中为early-init属性的项目,将其添加到actionqueue中,并执行执行这些queue中的动作。
action_for_each_trigger("early-init",action_add_queue_tail);
drain_action_queue();
9.遍历/sys文件夹,是内核产生设备添加事件(为了自动产生设备节点)。
device_fd=device_init();
10.初始化属性系统,并导入初始化属性文件。
property_init();
每个属性都有一个名称和值,它们都是字符串格式。
属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。
属性是在整个系统中全局可见的。
每个进程可以get/set属性。
在系统初始化时,Android将分配一个共享内存区来存储的属性,这里主要是从/default.prop属性文件读取属性。
类似于像Windows下的注册表的作用。
/default.pro的内容
ro.secure=0
ro.allow.mock.location=1
ro.debuggable=1
persist.service.adb.enable=1
11.从属性系统中得到ro.debuggable,若为1,則初始化keychord监听。
keychord_fd=open_keychord();
12.判断是否有控制台,如果没有,就尝试是否是可以打缺省的控制台。
缺省控制台名称:
staticchar*console_name="/dev/console";
if(console[0]){
snprintf(tmp,sizeof(tmp),"/dev/%s",console);
console_name=strdup(tmp);
}
fd=open(console_name,O_RDWR);
if(fd>=0)
have_console=1;
close(fd);
13.读取/initlogo.rle,如果成功則在/dev/graphics/fb0显示Logo,如果失败则将/dev/tty0设为文本模式,并打开/dev/tty0,输出文本“ANDROID”。
if(load_565rle_image(INIT_IMAGE_FILE)){
fd=open("/dev/tty0",O_WRONLY);
if(fd>=0){
constchar*msg;
msg="\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"/*consoleis40colsx30lines*/
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"ANDROID";
write(fd,msg,strlen(msg));
close(fd);
}
}
14.判断是否使用模拟器运行,如果时,就加载内核命令行参数。
if(qemu[0])
import_kernel_cmdline
(1);
15.根据内核命令行参数来设置工厂模式测试。
if(!
strcmp(bootmode,"factory"))
property_set("ro.factorytest","1");
elseif(!
strcmp(bootmode,"factory2"))
property_set("ro.factorytest","2");
else
property_set("ro.factorytest","0");
16.设置序列号属性值。
property_set("ro.serialno",serialno[0]?
serialno:
"");
17.设置启动模式属性值。
property_set("ro.bootmode",bootmode[0]?
bootmode:
"unknown");
18.设置基带频率属性值。
property_set("ro.baseband",baseband[0]?
baseband:
"unknown");
19.设置硬件载波方式属性值。
property_set("ro.carrier",carrier[0]?
carrier:
"unknown");
20.设置引导程序的版本号属性值。
property_set("ro.bootloader",bootloader[0]?
bootloader:
"unknown");
21.设置硬件信息属性值。
property_set("ro.hardware",hardware);
22.设置硬件版本号属性值
snprintf(tmp,PROP_VALUE_MAX,"%d",revision);
property_set("ro.revision",tmp);
23.添加所有的init命令到队列,然后再执行。
action_for_each_trigger("init",action_add_queue_tail);
drain_action_queue();
24.加载system和data目录下的属性,并启动属性监听服务。
property_set_fd=start_property_service();
加载的属性文件为:
#definePROP_PATH_SYSTEM_BUILD"/system/build.prop"
#definePROP_PATH_SYSTEM_DEFAULT"/system/default.prop"
#definePROP_PATH_LOCAL_OVERRIDE"/data/local.prop"
25.创建一个全双工的通讯机制的两个SOCKET,信号可以在signal_fd和signal_recv_fd双向通讯,从而建立起沟通的管道。
这个信号管理,就是用来让init进程与它的子进程进行沟通的,子进程从signal_fd写入信息,init进程从signal_recv_fd收到信息,然后再做处理。
if(socketpair(AF_UNIX,SOCK_STREAM,0,s)==0){
signal_fd=s[0];
signal_recv_fd=s[1];
fcntl(s[0],F_SETFD,FD_CLOEXEC);
fcntl(s[0],F_SETFL,O_NONBLOCK);
fcntl(s[1],F_SETFD,FD_CLOEXEC);
fcntl(s[1],F_SETFL,O_NONBLOCK);
}
26.判断设备文件系统device_fd是否成功初始化,属性服务property_set_fd是否成功初始化,信号通讯机制signal_recv_fd是否成功初始化。
if((device_fd<0)||(property_set_fd<0)||(signal_recv_fd<0)){
ERROR("initstartupfailure\n");
return1;
}
27.执行early-boot和boot属性的命令。
action_for_each_trigger("early-boot",action_add_queue_tail);
action_for_each_trigger("boot",action_add_queue_tail);
drain_action_queue();
28.执行当前载入的属性命令。
并标明属性触发器已经初始化完成。
queue_all_property_triggers();
drain_action_queue();
property_triggers_enabled=1;
29.保存设备文件系统device_fd,属性服务property_set_fd,信号通讯机制signal_recv_fd,以便后面轮询使用。
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;
fd_count=3;
30.判断是否处理组合键轮询。
if(keychord_fd>0){
ufds[3].fd=keychord_fd;
ufds[3].events=POLLIN;
fd_count++;
}else{
ufds[3].events=0;
ufds[3].revents=0;
}
31.初始化linux程序启动速度的性能分析工具。
#ifBOOTCHART
bootchart_count=bootchart_init();
if(bootchart_count<0){
ERROR("bootchartinginitfailure\n");
}elseif(bootchart_count>0){
NOTICE("bootchartingstarted(period=%dms)\n",\
bootchart_count*BOOTCHART_POLLING_MS);
}else{
NOTICE("bootchartingignored\n");
}
#endif
32.进入主进程循环,重置轮询事件状态、查询action队列,并执行、重启需要重启的服务、轮询注册的事件状态。
for(;;){
intnr,i,timeout=-1;
for(i=0;iufds[i].revents=0;
}
drain_action_queue();
restart_processes();
if(process_needs_restart){
timeout=(process_needs_restart-gettime())*1000;
if(timeout<0)
timeout=0;
}
#ifBOOTCHART
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;
if(ufds[2].revents==POLLIN){
/*wegotaSIGCHLD-reapandrestartasneeded*/
read(signal_recv_fd,tmp,sizeof(tmp));
while(!
wait_for_one_process(0))
;
continue;
}
if(ufds[0].revents==POLLIN)
handle_device_fd(device_fd);
if(ufds[1].revents==POLLIN)
handle_property_set_fd(property_set_fd);
if(ufds[3].revents==POLLIN)
handle_keychord(keychord_fd);
}
三、init.rc脚本加载流程分析
1、init.rc脚本语法介绍
Android初始化语言由四大类声明组成:
行为类(Actions),命令类(Commands),服务类(Services),选项类(Options)。
*初始化语言以行为单位,由以空格间隔的语言符号组成。
C风格的反斜杠转义符可以用来插入空白到语言符号。
双引号也可以用来防止文本被空格分成多个语言符号。
当反斜杠在行末时,作为折行符。
*以#开始(前面允许有空格)的行为注释行。
*Actions和Services隐含声明一个新的段落。
所有该段落下Commands或
Options的声明属于该段落。
第一段落前的Commands或Options被忽略。
*Actions和Services拥有独一无二的命名。
在它们之后声明相同命名的类将
被当作错误并忽略。
●Actions
Actions是一系列命令的命名。
Actions拥有一个触发器(trigger)用来决定action何时执行。
当一个action在符合触发条件被执行时,如果它还没被加入到待执行队列中的话,则加入到队列最后。
队列中的action依次执行,action中的命令也依次执行。
Init在执行命令的中间处理其它活动(设备创建/销毁,property设置,进程重启)。
Actions语法形式为:
on
...
●Services
Services是由init启动,在它们退出时重启(可选)。
Service表现形式为:
service[]*
...
●Options
Options是Services的修饰,它们影响init何时、如何运行service.
critical
这是一个设备关键服务(device-criticalservice).
如果它在4分钟内退出超过4次,设备将重启并进入恢复模式。
disabled
该服务将不会自动启动,它必须被依照服务名指定启动才可以启动。
setenv
设置已启动的进程的环境变量的值
socket[[]]
创建一个名为/dev/socket/的unixdominsocket,并传送它的fd
到已启动的进程。
必须为"dgram"或"stream".用户和组默认为0.
user
在执行服务前改变用户名。
group[]*
在执行服务前改变组。
在第一个组后的组将设为进程附加组
(通过setgroups()).当前默认为root.
oneshot
在服务退出后不重启。
class
为service指定一个类别名。
同样类名的所有的服务可以一起启动或停止。
如果没有指定类别的服务默认为"default"类。
onrestart
当服务重启时执行一个命令。
●Triggers
触发器是一个字符串,可以用来匹配某种类型的事件并执行一个action。
Boot
这是当init开始后执行的第一个触发器(当/init.conf被加载)
=
当property被设为指定的值时触发。
device-added-
device-removed-
当设备节点被添加或移除时触发。
service-exited-
当指定的服务存在时触发
●Commands
exec[]*
Fork并执行一个程序().这将被block直到程序执行完毕。
最好避免
执行例如内建命令以外的程序,它可能会导致init被阻塞不动。
export
设定全局环境变量的值,当这个命令执行后所有的进程都可
以取得该环境变量的值。
ifup
使网络接口联机。
import
解析一个init配置文件,扩展当前配置文件。
hostname
设置主机名
chmod
改变文件访问权限
chown
改变文件所属和组
class_start
当指定类别的服务没有运行,启动该类别所有的服务。
class_stop
当指定类别的服务正在运行,停止该类别所有的服务。
domainname
设置域名。
insmod
加载该路径的模块
mkdir[mode][owner][group]
在创建一个目录,可选选项:
mod,owner,group.如果没有指定,目录以
755权限,owner为root,group为root创建。
mount[]*
尝试mount到目录.可以用mtd@name格式以命名指
定一个mtd块设备。
包含"ro","rw","remount","noatime".
setprop
设置系统property的值<