Chinaunix首页 | 论坛 | 博客
  • 博客访问: 667990
  • 博文数量: 237
  • 博客积分: 4285
  • 博客等级: 上校
  • 技术积分: 2701
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-15 14:05
文章分类

全部博文(237)

文章存档

2014年(2)

2013年(3)

2012年(47)

2011年(15)

2010年(68)

2009年(102)

我的朋友

分类: LINUX

2011-01-10 10:00:15

分析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init 是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内核将会到/sbin/, /bin/ 等目录下查找默认的init,如果没有找到那么就报告出错。
下面是曾经用过的几种开发板的命令行参数:
S3C2410 启动参数:
noinitrd root=/dev/nfs  nfsroot=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. 初始化属性服务器,Actually 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/request_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_queue_tail);
    drain_action_queue();
    ...
    /* 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_queue_tail);
    drain_action_queue();
    ...
    /* 执行 early-boot and boot actions in the two files parsed in step 2 */
    action_for_each_trigger("early-boot", action_add_queue_tail);
    action_for_each_trigger("boot", action_add_queue_tail);
    drain_action_queue();

    /* run all property triggers based on current state of the properties */
    queue_all_property_triggers();
    drain_action_queue();

    /* enable property triggers */  
    property_triggers_enabled = 1;    
    ...
    for(;;) {
        int nr, timeout = -1;
    ...
        drain_action_queue();
        restart_processes();

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
     ...
        nr = poll(ufds, 3, 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);

        if (ufds[1].revents == POLLIN)
    {
            handle_property_set_fd(property_set_fd);
    }
    }

    return 0;
}


2.解析init.rc脚本
init.rc 脚本的具体语法可以参考下面文档

名词解释:
Android初始化語言由四大类声明组成:行为类(Actions),命令类(Commands),服务类(Services),选项类(Options).
初始化语言以行为单位,由以空格间隔的语言符号組成。C风格的反斜杠转义符可以用来插入空白到语言符号。双引号也可以用来防止文本被空格分成多个语言符号。当反斜杠在行末时,作为换行符。
* 以#开始(前面允许空格)的行为注释。
* Actions和Services隐含声明一个新的段落。所有该段落下Commands或Options的声明属于该段落。第一段落前的Commands或Options被忽略。
* Actions和Services拥有唯一的命名。在他们之后声明相同命名的类将被当作错误并忽略。
Actions是一系列命令的命名。Actions拥有一个触发器(trigger)用来決定action何時执行。当一个action在符合触发条件被执行时,如果它还没被加入到待执行队列中的话,則加入到队列最后。
队列中的action依次执行,action中的命令也依次执行。Init在执行命令的中间处理其他活动(设备创建/销毁,property 设置,进程重启)。 

Actions的表现形式:
on
 
 
 
重要的数据结构
两个列表,一个队列。
static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);
*.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 0x01
#define COMMAND 0x02
#define OPTION  0x04
关键字        属性       
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(struct 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(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;
    if (nargs < 3) {
        parse_error(state, "services must have a name and a program\n");
        return 0;
    }
    if (!valid_name(args[1])) {
        parse_error(state, "invalid service name '%s'\n", args[1]);
        return 0;
    }
    //如果服务已经存在service_list列表中将会被忽略
    svc = service_find_by_name(args[1]);
    if (svc) {
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }
   
    nargs -= 2;
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    svc->args[nargs] = 0;
    svc->nargs = nargs;
    svc-> = "onrestart";
    list_init(&svc->onrestart.commands);
    //添加该服务到 service_list 列表
    list_add_tail(&service_list, &svc->slist);
    return svc;
}
服务的表现形式:
service [ ]*

Simple Guide for Porting Android Kernel

http://feixf1974.javaeye.com/blog/187773

阅读(651) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~