分 析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init 是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内核将会到/sbin/,/bin/ 等目录下查找默认的init,如果没有找到那么就报告出错。
下面是曾经用过的几种开发板的命令行参数:
S3C2410 启动参数:
noinitrd
root=/dev/nfsnfsroot=192.168.2.56:/nfsroot/rootfs
ip=192.168.2.188:192.168.2.56:192.168.2.56:255.255.255.0::eth0:on
console=ttySAC0
S3C2440 启动参数:
setenv bootargs console=ttySAC0
root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs
ip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:on
mem=64M init=/init
marvell 310 启动参数:
boot root=/dev/nfs
nfsroot=192.168.2.56:/nfsroot/rootfs,rsize=1024,wsize=1024
ip=192.168.2.176:192.168.2.201:192.168.2.201:255.255.255.0::eth0:-On
console=ttyS2,115200 mem=64M init=/init
init的源代码在文件:./system/core/init/init.c 中,init会一步步完成下面的任务:
1.初始化log系统

2.解析/init.rc和/init.%hardware%.rc文件

3. 执行 early-init action in the two files parsed in step 2.

4. 设备初始化,例如:在 /dev 下面创建所有设备节点,下载 firmwares.

5.初始化属性服务器,Act lly the property system is working as a share memory.Logically it looks like a registry under Windows system.

6. 执行 init action in the two files parsed in step 2.

7. 开启 属性服务。

8. 执行 early-boot and boot actions in the two files parsed in step 2.

9. 执行 Execute property action in the two files parsed in step 2.

10.进入一个无限循环 to wait for device/property set/child process exit events.例如,如果SD卡被插入,init会收到一个设备插入事件,它会为这个设备创建节点。系统中比较重要的进程都是由init来fork的,所 以如果他们他谁崩溃了,那么init 将会收到一个 SIGCHLD 信号,把这个信号转化为子进程退出事件, 所以在loop中,init 会操作进程退出事件并且执行*.rc 文件中定义的命令。
例如,在init.rc中,因为有:
service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server
socket zygote stream 666
onrestart write /sys/android_power/req st_state wake
onrestart write /sys/power/state on
所以,如果zygote因为启动某些服务导致异常退出后,init将会重新去启动它。
int main(int argc, char **argv)
{

//需要在后面的程序中看打印信息的话,需要屏蔽open_devnull_stdio()函数
open_devnull_stdio();

//初始化log系统
log_init();
//解析/init.rc和/init.%hardware%.rc文件
parse_config_file(”/init.rc”);

snprintf(tmp, sizeof(tmp), “/init.%s.rc”, hardware);
parse_config_file(tmp);

//执行 early-init action in the two files parsed in step 2.
action_for_each_trigger(”early-init”, action_add_q _tail);
drain_action_q ();

/* execute all the boot actions to get us started */
/* 执行 init action in the two files parsed in step 2 */
action_for_each_trigger(”init”, action_add_q _tail);
drain_action_q ();

/* 执行 early-boot and boot actions in the two files parsed in step 2 */
action_for_each_trigger(”early-boot”, action_add_q _tail);
action_for_each_trigger(”boot”, action_add_q _tail);
drain_action_q ();
/* run all property triggers based on current state of the properties */
q _all_property_triggers();
drain_action_q ();
/* enable property triggers */
property_triggers_enabled = 1;

for(;;) {
int nr, timeout = -1;

drain_action_q ();
restart_processes();
if (process_needs_restart) {
timeout = (process_needs_restart – gettime()) * 1000;
if (timeout


重要的数据结构两个列表,一个队列。
static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_q );
*.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。
*.rc 脚本中所有 on 关键开头的项将会被会添加到 action_list 列表中。
每个action列表项都有一个列表,此列表用来保存该段落下的 Commands脚本解析过程:
parse_config_file(”/init.rc”)
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;
parse_config(fn, data);
DUMP();
return 0;
}
static void parse_config(const char *fn, char *s)


case T_NEWLINE:
if (nargs) {
int kw = lookup_keyword(args[0]);
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;
}


parse_config 会逐行对脚本进行解析,如果关键字类型为SECTION ,那么将会执行 parse_new_section() 类型为 SECTION 的关键字有: on 和 sevice 关键字类型定义在 Parser.c (system\core\init) 文件中
Parser.c (system\core\init)
#define SECTION 0×01
#define COMMAND 0×02
#define OPTION0×04
关键字 属性
capability,OPTION,0, 0)
class, OPTION,0, 0)
class_start, COMMAND, 1, do_class_start)
class_stop,COMMAND, 1, do_class_stop)
console, OPTION,0, 0)
critical, OPTION,0, 0)
disabled, OPTION,0, 0)
domainname,COMMAND, 1, do_domainname)
exec, COMMAND, 1, do_exec)
export, COMMAND, 2, do_export)
group, OPTION,0, 0)
hostname, COMMAND, 1, do_hostname)
ifup, COMMAND, 1, do_ifup)
insmod, COMMAND, 1, do_insmod)
import, COMMAND, 1, do_import)
keycodes, OPTION,0, 0)
mkdir, COMMAND, 1, do_mkdir)
mount, COMMAND, 3, do_mount)
on, SECTION, 0, 0)
oneshot, OPTION,0, 0)
onrestart, OPTION,0, 0)
restart, COMMAND, 1, do_restart)
service, SECTION, 0, 0)
setenv, OPTION,2, 0)
setkey, COMMAND, 0, do_setkey)
setprop, COMMAND, 2, do_setprop)
setrlimit, COMMAND, 3, do_setrlimit)
socket, OPTION,0, 0)
start, COMMAND, 1, do_start)
stop, COMMAND, 1, do_stop)
trigger, COMMAND, 1, do_trigger)
symlink, COMMAND, 1, do_symlink)
sysclktz, COMMAND, 1, do_sysclktz)
user, OPTION,0, 0)
write, COMMAND, 2, do_write)
chown, COMMAND, 2, do_chown)
chmod, COMMAND, 2, do_chmod)
loglevel, COMMAND, 1, do_loglevel)
device, COMMAND, 4, do_device)
parse_new_section()中再分别对 service 或者 on 关键字开头的内容进行解析。

case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
case K_on:
state->context = parse_action(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_action;
return;
}
break;
}

对 on 关键字开头的内容进行解析
static void *parse_action(str t parse_state *state, int nargs, char **args)
{

act = calloc(1, sizeof(*act));
act->name = args[1];
list_init(&act->commands);
list_add_tail(&action_list, &act->alist);

}
对 service 关键字开头的内容进行解析
static void *parse_service(str t parse_state *state, int nargs, char **args)
{
str t service *svc;
if (nargs name = args[1];
svc->classname = “default”;
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
svc->args[nargs] = 0;
svc->nargs = nargs;
svc->onrestart.name = “onrestart”;
list_init(&svc->onrestart.commands);
//添加该服务到 service_list 列表
list_add_tail(&service_list, &svc->slist);
return svc;
}
服务的表现形式:
service []*

申 请一个service结构体,然后挂接到service_list链表上,name 为服务的名称 pathname 为执行的命令 argument 为命令的参数。之后的 option 用来控制这个service结构体的属性,parse_line_service 会对 service关键字后的 内容进行解析并填充到 service 结构中 ,当遇到下一个service或者on关键字的时候此service选项解析结束。
例如:
service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server
socket zygote stream 666
onrestart write /sys/android_power/req st_state wake
服务名称为: zygote
启动该服务执行的命令: /system/bin/app_process
命令的参数: -Xzygote /system/bin –zygote –start-system-server
socket zygote stream 666: 创建一个名为:/dev/socket/zygote 的 socket ,类型为:stream
当*.rc 文件解析完成以后:
action_list 列表项目如下:
on init
on boot
on property:ro.kernel.qemu=1
on property:persist.service.adb.enable=1
on property:persist.service.adb.enable=0
init.marvell.rc 文件
on early-init
on init
on early-boot
on boot
service_list 列表中的项有:
service console
service adbd
service servicemanager
service mountd
service debuggerd
service ril-daemon
service zygote
service media
service bootsound
service dbus
service hcid
service hfag
service hsag
service installd
service flash_recovery
状态服务器相关:
在init.c 的main函数中启动状态服务器。
property_set_fd = start_property_service();
状态读取函数:
Property_service.c (system\core\init)
const char* property_get(const char *name)
Properties.c (system\core\libcutils)
int property_get(const char *key, char *val , const char *default_val )
状态设置函数:
Property_service.c (system\core\init)
int property_set(const char *name, const char *val )
Properties.c (system\core\libcutils)
int property_set(const char *key, const char *val )
在终端模式下我们可以通过执行命令 setprop
setprop 工具源代码所在文件: Setprop.c (system\core\toolbox)
Getprop.c (system\core\toolbox): property_get(argv[1], val , default_val );
Property_service.c (system\core\init)
中定义的状态读取和设置函数仅供init进程调用,
handle_property_set_fd(property_set_fd);
property_set() //Property_service.c (system\core\init)
property_changed(name, val ) //Init.c (system\core\init)
q _property_triggers(name, val )
drain_action_q ()
只要属性一改变就会被触发,然后执行相应的命令:
例如:
在init.rc 文件中有
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd
所以如果在终端下输入:
setprop property:persist.service.adb.enable 1或者0
那么将会开启或者关闭adbd 程序。
执行action_list 中的命令:
从action_list 中取出 act->name 为 early-init 的列表项,再调用 action_add_q _tail(act)将其插入到 队列 action_q 尾部。drain_action_q () 从action_list队列中取出队列项 ,然后执行act->commands
列表中的所有命令。
所以从./system/core/init/init.c mian()函数的程序片段:
action_for_each_trigger(”early-init”, action_add_q _tail);
drain_action_q ();
action_for_each_trigger(”init”, action_add_q _tail);
drain_action_q ();
action_for_each_trigger(”early-boot”, action_add_q _tail);
action_for_each_trigger(”boot”, action_add_q _tail);
drain_action_q ();
/* run all property triggers based on current state of the properties */
q _all_property_triggers();
drain_action_q ();
可以看出,在解析完init.rc init.marvell.rc 文件后,action 命令执行顺序为:
执行act->name 为 early-init,act->commands列表中的所有命令
执行act->name 为 init, act->commands列表中的所有命令
执行act->name 为 early-boot,act->commands列表中的所有命令
执行act->name 为 boot, act->commands列表中的所有命令
关键的几个命令:
class_start default 启动所有service 关键字定义的服务。
class_start 在act->name为boot的 act->commands列表中,所以当 class_start 被触发后,实际上调用的是函数 do_class_start()
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
* which are explicitly disabled.They must
* be started individ lly.
*/
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
void service_for_each_class(const char *classname,
void (*func)(str t service *svc))
{
str t listnode *node;
str t service *svc;
list_for_each(node, &service_list) {
svc = node_to_item(node, str t service, slist);
if (!strcmp(svc->classname, classname)) {
func(svc);
}
}
}
因为在调用 parse_service() 添加服务列表的时候,所有服务 svc->classname 默认取值:”default”,
所以 service_list 中的所有服务将会被执行。
参考文档:
http://blog.chinaunix.net/ /38994/showart_1775465.html
http://blog.chinaunix.net/ /38994/showart_1168440.html
浅析kernel启动的第1个用户进程init如何解读init.rc脚本
http://blog.chinaunix.net/ /38994/showart_1168440.html

更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. 如何在后台运行Linux命令?
  3. 一款常用的 Squid 日志分析工具
  4. No.11 使用firewall配置的防火墙策略的生效模式
  5. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  6. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  7. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  8. [原创] 在Android系统使用socket在framework和native之间通信
  9. Android* 教程:使用英特尔® 线程构建模块编写多线程应用

随机推荐

  1. Android -- 屏幕亮度
  2. Android LGame入门
  3. Android中json操作数据的使用
  4. android闹钟(四):实现计时器
  5. Please use Android Tools > Fix Project
  6. 获取sdcard文件
  7. Android全屏设置方法
  8. Android Studio中Gradle编译配置
  9. Android常用mimetype类型
  10. android摄像头,linux摄像头设备,编程