Android 的启动流程

Android 的启动流程

 Android 根文件系统启动过程。

在Android系统启动时,内核引导参数上一般都会设置“init=/init”, 这样的话,如果内核成功挂载了这个文件系统之后,首先运行的就是这个根目录下的init程序。

init程序源码在Android官方源码的system/core/init中,main在init.c里。我们的分析就从main开始。

init:
(1)安装SIGCHLD信号。(如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。因此需要对SIGCHLD信号做出处理,回收僵尸进程的资源,避免造成不必要的资源浪费。)
(2)对umask进行清零。
 

umask是什么?


当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情。umask设置了用户创建文件的默认权限,它与chmod的效果刚好相反,umask设置的是权限“补码”,而chmod设置的是文件权限码。一般在/etc/profile、$ [HOME]/.bash_profile或$[HOME]/.profile中设置umask值。

如何计算umask值?

umask命令允许你设定文件创建时的缺省模式,对应每一类用户(文件属主、同组用户、其他用户)存在一个相应的umask值中的数字。对于文件来说,这一数字的最大值分别是6。系统不允许你在创建一个文本文件时就赋予它执行权限,必须在创建后用chmod命令增加这一权限。目录则允许设置执行权限,这样针对目录来说,umask中各个数字最大可以到7。

该命令的一般形式为:umask nnn
其中nnn为umask置000 - 777。

我们只要记住u m a s k是从权限中“拿走”相应的位即可。下表是umask值与权限的对照表:
umask 文件 目录
--------------------
0 6 7
1 6 6
2 4 5
3 4 4
4 2 3
5 2 2
6 0 1
7 0 0
--------------------

如:umask值为022,则默认目录权限为755,默认文件权限为644。

(3)为rootfs建立必要的文件夹,并挂载适当的分区。
    /dev (tmpfs)
        /dev/pts (devpts)
        /dev/socket
    /proc (proc)
    /sys  (sysfs)
 (4)创建/dev/null和/dev/kmsg节点。
 (5)解析/init.rc,将所有服务和操作信息加入链表。

脚本解析过程: 
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.name = "onrestart"; 
    list_init(&svc->onrestart.commands); 
    //添加该服务到 service_list 列表 
    list_add_tail(&service_list, &svc->slist); 
    return svc; 

服务的表现形式: 
service <name> <pathname> [ <argument> ]* 
<option> 
<option> 
... 

申请一个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/request_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 *value, const char *default_value) 
状态设置函数: 
Property_service.c (system\core\init) 
int property_set(const char *name, const char *value) 
Properties.c (system\core\libcutils) 
int property_set(const char *key, const char *value) 

在终端模式下我们可以通过执行命令 setprop <key> <value> 
setprop 工具源代码所在文件: Setprop.c (system\core\toolbox) 
Getprop.c (system\core\toolbox):        property_get(argv[1], value, default_value); 
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, value) //Init.c (system\core\init) 
      queue_property_triggers(name, value) 
      drain_action_queue() 
只要属性一改变就会被触发,然后执行相应的命令:  
例如: 
在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_queue_tail(act)将其插入到 
队列 action_queue 尾部。drain_action_queue() 从action_list队列中取出队列项 ,然后执行act->commands 
列表中的所有命令。 
所以从  ./system/core/init/init.c mian()函数的程序片段: 
action_for_each_trigger("early-init", action_add_queue_tail); 
drain_action_queue(); 
action_for_each_trigger("init", action_add_queue_tail); 
drain_action_queue(); 
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(); 
可以看出,在解析完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 individually. 
         */ 
    service_for_each_class(args[1], service_start_if_not_disabled); 
    return 0; 

void service_for_each_class(const char *classname, 
                            void (*func)(struct service *svc)) 

    struct listnode *node; 
    struct service *svc; 
    list_for_each(node, &service_list) { 
        svc = node_to_item(node, struct service, slist); 
        if (!strcmp(svc->classname, classname)) { 
            func(svc); 
        } 
    } 



 (6)从/proc/cmdline中提取信息内核启动参数,并保存到全局变量。
(7)先从上一步获得的全局变量中获取信息硬件信息和版本号,如果没有则从/proc/cpuinfo中提取,并保存到全局变量。
(8)根据硬件信息选择一个/init.(硬件).rc,并解析,将服务和操作信息加入链表。
    在G1的ramdisk根目录下有两个/init.(硬件).rc:init.goldfish.rc和init.trout.rc,init程序会根据上一步获得的硬件信息选择一个解析。
(9)执行链表中带有“early-init”触发的的命令。
(10)遍历/sys文件夹,是内核产生设备添加事件(为了自动产生设备节点)。
(11)初始化属性系统,并导入初始化属性文件。
(12)从属性系统中得到ro.debuggable,若为1,則初始化keychord监听。
(13)打開console,如果cmdline中沒有指定console則打開默認的/dev/console
(14)开机文字("A N D R I O D") 
Android 系统启动后,init.c中main()调用load_565rle_image()函数读取/initlogo.rle(一张565 rle压缩的位图),如果读取成功,则在/dev/graphics/fb0显示Logo图片;如果读取失败,则将/dev/tty0设为TEXT模式,并打开/dev/tty0,输出文本“A N D R I O D”字样。
定义加载图片文件名称 
#define INIT_IMAGE_FILE "/initlogo.rle" 
int load_565rle_image( char *file_name ); 
#endif 
init.c中main()加载/initlogo.rle文件。

 

if( load_565rle_image(INIT_IMAGE_FILE) ) {
    fd = open("/dev/tty0", O_WRONLY);
    if (fd >= 0) {
        const char *msg;
            msg = "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"  // console is 40 cols x 30 lines
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "             A N D R O I D ";
        write(fd, msg, strlen(msg));
        close(fd);
    }
    }

 

相关代码: 
/system/core/init/init.c 
    
/system/core/init/init.h 
/system/core/init/init.rc 
/system/core/init/logo.c 
*.rle文件的制作步骤: 
a. 使用GIMP或者Advanced Batch Converter软件,将图象转换为RAW格式; 
b. 使用android自带的rgb2565工具,将RAW格式文件转换为RLE格式(如:rgb2565 -rle < initlogo.raw > initlogo.rle)。

 

 

 


(15)判斷cmdline 中的參數,并设置属性系统中的参数:
         1、 如果 bootmode為
         - factory,設置ro.factorytest值為1
         - factory2,設置ro.factorytest值為2
         - 其他的設ro.factorytest值為0
       2、如果有serialno参数,則設置ro.serialno,否則為""
       3、如果有bootmod参数,則設置ro.bootmod,否則為"unknown"
       4、如果有baseband参数,則設置ro.baseband,否則為"unknown"
       5、如果有carrier参数,則設置ro.carrier,否則為"unknown"
       6、如果有bootloader参数,則設置ro.bootloader,否則為"unknown"
       7、通过全局变量(前面从/proc/cpuinfo中提取的)設置ro.hardware和ro.version。
(16)執行所有触发标识为init的action。
(17)開始property服務,讀取一些property文件,這一動作必須在前面那些ro.foo設置后做,以便/data/local.prop不能干預到他們。
      - /system/build.prop
      - /system/default.prop
      - /data/local.prop
      - 在讀取默認的property后讀取presistent propertie,在/data/property中
(18)為sigchld handler創建信號機制
(19)確認所有初始化工作完成:
    device_fd(device init 完成)
    property_set_fd(property server start 完成)
    signal_recv_fd (信號機制建立)
(20) 執行所有触发标识为early-boot的action
(21) 執行所有触发标识为boot的action
(22)基于當前property狀態,執行所有触发标识为property的action
(23)注冊輪詢事件:
      - device_fd
      - property_set_fd
      -signal_recv_fd
      -如果有keychord,則注冊keychord_fd
(24)如果支持BOOTCHART,則初始化BOOTCHART
(25)進入主進程循環:
      - 重置輪詢事件的接受狀態,revents為0
      - 查詢action隊列,并执行。
      - 重啟需要重啟的服务
      - 輪詢注冊的事件
          - 如果signal_recv_fd的revents為POLLIN,則得到一個信號,獲取并處理
          - 如果device_fd的revents為POLLIN,調用handle_device_fd
          - 如果property_fd的revents為POLLIN,調用handle_property_set_fd
          - 如果keychord_fd的revents為POLLIN,調用handle_keychord

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值