安卓之启动-初析(三)

9 篇文章 0 订阅
4 篇文章 0 订阅

代码解析

首先引入依赖文件init.rc,这个是init执行依赖的文件。还有uevetd.rc,其他rc文件我就不在这里列举了。关于以rc为后缀的文件的说明请看,它里面介绍了The Android Init Language consists of four broad classes ofstatements,

which are Actions, Commands, Services, and Optionsinit.h头文件也有定义结构。

然后看一个重要的文件Makefile--Android.mk。我摘几行重要的代码:

要进行编译的源文件

LOCAL_SRC_FILES:= \

         builtins.c \

         init.c \

         devices.c \

         property_service.c \

         util.c \

         parser.c \

         logo.c \

         keychords.c \

         signal_handler.c \

         init_parser.c \

         ueventd.c \

         ueventd_parser.c \

         watchdogd.c

ifeq ($(strip$(INIT_BOOTCHART)),true)  可能会编译的文件条件是ifeq成立

LOCAL_SRC_FILES +=bootchart.c

endif

LOCAL_MODULE:= init 编译出来的模块,看见了嘛,这个就是那个可执行的init

# Make a symlink from /sbin/ueventdand /sbin/watchdogd to /init 创建一个符号链接从/sbin/ueventd /sbin/watchdogd /init

SYMLINKS :=\

    $(TARGET_ROOT_OUT)/sbin/ueventd\

$(TARGET_ROOT_OUT)/sbin/watchdogd

然后让我们看一下init.c和其他文件

init.c 三个并列程序流程 uevent、watchdog、init

int main(intargc, char**argv)

{//init的入口,一切从这里开始

   …………

   if(!strcmp(basename(argv[0]),"ueventd"))basename函数作用是去掉输入string中的/。如果传入参数去掉/不等于ueventd则执行ueventd_main

        return 1ueventd_main(argc,argv);

网上说这实际是init启动的自己的进程,它的主要作用根据uevent是创建或删除/dev/xxx(xxx设备名),我们知道在linux下面创建设备节点的接口mknod       

ueventd_main这个函数就是init启动的自己的进程的入口。这个函数比较复杂,并且代码涉及大部分底层API,我个人看起来还是比较吃力的,不过我尽量去给源码加上自己合理的注释,并且粘贴一些我在网上搜索出来的认为比较好的说明。

1###################################################################################################

int ueventd_main(int argc, char **argv)

{//ueventd入口,在ueventd.c

 …………

   import_kernel_cmdline(0, import_kernel_nv);//从设备文件/dev/cmdline(内核读取命令行参数方式)读取hardware硬件名称

   get_hardware_name(hardware, &revision);//变成软件可识别硬件名字,因为需要带有设备号

   ueventd_parse_config_file("/ueventd.rc");//解析配置文件uevevtd.rc

   snprintf(tmp,sizeof(tmp),"/ueventd.%s.rc",hardware);   ueventd_parse_config_file(tmp); //解析ueventd.xxx.rc,xxx是刚刚获取的hardware

device_init();//这个函数比较重要这里做一个流程描述,由于代码过多我就不粘贴出来了。

这里是方法跳转的顺序流,且不是在同一个文件中,大家可以去查看相关源码/system/core/init/*

devicd_init流程(|表示并列、||表示一种流程结束、|表示有先后关系,粗体字表示重要的方法)__start uevent_open_socket(注册socketHandle,为和内核打交道)(system/core/libcutils/uevetd.c)------->

   | coldboot("/sys/class");       |

    | coldboot("/sys/block");       |

    | coldboot("/sys/devices");     |--------> coldboot ----------->do_coldboot--->

    | handle_device_fd(重要的设备处理入口)|---> parse_event -------->

    |handle_firmware_event  |-------->process_firmware_event------>

    | open底层设备驱动open,打开驱动。firmware流程结束||

    |handle_device_event    |--------->

    | fixup_sys_perms   uevent->action={add,change} 安装系统权限/sys下的------------>

    | handle_block_device_event  uevent->subsystem={block} 处理/dev/block下驱------>

    | parse_device_name------->parse_platform_block_device-------->handle_device--->

    | make_device ----->get_device_perm(设置设备权限)----->makedev(等同于linux创建设备                      MKDEV,创建设备dev_tdev)----->setegid(gid)(设置有效组id)------>mknod(path, mode, dev)(真正创建特殊设备文件)------>chown(path, uid, -1)(修改属组)--->setegid(AID_ROOT)|

remove_link删除文件(此方法在make_device后执行)||

    | handle_platform_device_event   uevent->subsystem={platform},处理/devices/platform-------> add_platform_device(添加/devices/platform下设备)------> remove_platform_device(移除相应设备)||

    | handle_generic_device_eventuevent->subsystem ={!platform,!block},处理常规的驱动和相应的驱动映射文件/dev,其中包含usbmnt,相机,input,图形等不一一列举,详细看源代码。--->

handle_device_fd(同上)||

回顾

至此这个device_init就讲完了,流程比较复杂,现在我们在稍微回顾一下:

    首先注册了一个套接字socket和内核互发消息(这个我没太自信看,不是非常确认),之后触发/sys/class,/sys/block,/sys/devices这三个目录及其子目录下的uevent,然后接受并创建设备节点,至此设备节点才算创建。最终handle_device_fd这才是真正处理驱动的入口的开始,会最终调用mknod创建设备节点或者open打开真正的驱动(firmware),流程如下:

handle_device_fd->handle_device_event-> make_device-> mknodfirmware不同,见上面)。

 

继续下面代码:

   ufd.events= POLLIN;

   ufd.fd =get_device_fd();

   while(1){

        ufd.revents = 0;

        nr = poll(&ufd, 1, -1);//Unix 经典poll模式和select模式相似

        if (nr <= 0)

            continue;

        if (ufd.revents == POLLIN)

               handle_device_fd();//接受kernel传过来的uevent,动态创建或删除节点,同上

   }

}

1###################################################################################################

   if(!strcmp(basename(argv[0]),"watchdogd"))同上

        return 2watchdogd_main(argc,argv);启动watchdog,相关看门狗介绍可以google

   umask(0);//把文件掩码设置为空

   //以下是生成安卓系统中的一些基本的系统目录并挂载对应的文件系统3它完成工作和ueventd相似,但是包含一些系统服务进程的启动。

   mkdir("/dev",0755);

   mkdir("/proc",0755);

   mkdir("/sys",0755);

   mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "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);

   close(open("/dev/.booting",O_WRONLY | O_CREAT, 0000));

   open_devnull_stdio();//生成类似linux/dev/null的设备

   klog_init();//日志系统

   property_init();//打开/dev/__properties__,使之成为共享区域,用来保存一些属性

   get_hardware_name(hardware, &revision);//同上面讲到的

   process_kernel_cmdline();//同上

#ifdef HAVE_SELINUX //是否启用了安全linux,如果启动了设置一些SELinux处理器

    union selinux_callback cb;

    cb.func_log = klog_write;

    selinux_set_callback(SELINUX_CB_LOG, cb);

    cb.func_audit = audit_callback;

    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    if (selinux_android_load_policy() <0) {

            selinux_enabled = 0;        } else {

            selinux_init_all_handles();

    }

    restorecon("/dev");

    restorecon("/dev/socket");

#endif

   is_charger = !strcmp(bootmode, "charger");

   property_load_boot_defaults();//设置加载boot默认properties,放在共享区域

    // 解析 /init.rc 和 /init.$hardware.rc 脚本,其中 $hardware 参数从 /proc/cpuinfo 中读取

   init_parse_config_file("/init.rc");//我们终于看到这个了init.rc

   //寻找 early-init 触发器,加到 action_queue 

   action_for_each_trigger("early-init", action_add_queue_tail);

  //等待完成coldboot动作同上ueventd

   queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");

   queue_builtin_action(keychord_init_action, "keychord_init");//keychord初始化

   queue_builtin_action(console_init_action, "console_init");//控制台初始化

    //为开始执行所有引导的actions,执行action(不管is_charger是什么模式) initproperty_service_initsignal_initcheck_startupchargerqueue_property_triggers,非is_charger模式下下执行early-fsfspost-fspost-fs-dataearly-bootboot

   action_for_each_trigger("init", action_add_queue_tail);

    if(!is_charger) {

      action_for_each_trigger("early-fs",action_add_queue_tail);

      action_for_each_trigger("fs",action_add_queue_tail);

      action_for_each_trigger("post-fs",action_add_queue_tail);

      action_for_each_trigger("post-fs-data",action_add_queue_tail);

   }

   queue_builtin_action(property_service_init_action, "property_service_init");

   queue_builtin_action(signal_init_action, "signal_init");

   queue_builtin_action(check_startup_action, "check_startup");

   if(is_charger) {

        action_for_each_trigger("charger",action_add_queue_tail);

   } else{

        action_for_each_trigger("early-boot",action_add_queue_tail);

        action_for_each_trigger("boot", action_add_queue_tail);

   }

   queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

#if BOOTCHART //是否启动bootchart_init,这里就不介绍了

    queue_builtin_action(bootchart_init_action,"bootchart_init");

#endif

//init进程开始执行逻辑

   for(;;){

        int nr, i, timeout = -1;

        execute_one_command();//command是存放在队列中的,具体actioncommandservice等信息定义在init.h头中,我不一一详细介绍了,可以查看相关文档和代码。但是这一个函数就是执行一条command,这个时候就启动了如下的服务,摘自init.rc

service adbd /sbin/adbd  //adb

service servicemanager/system/bin/servicemanager //servicemanager

service surfaceflinger/system/bin/surfaceflinger//触摸

service zygote/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  //zygote就在这个时候启动的                         

        restart_processes();//重启进程

        if (!property_set_fd_init &&get_property_set_fd() > 0) {//启动相关计数

            ufds[fd_count].fd =get_property_set_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            property_set_fd_init = 1;

        }

        if (!signal_fd_init && get_signal_fd()> 0) {//信号处理相关计数

            ufds[fd_count].fd = get_signal_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            signal_fd_init = 1;

        }

        if (!keychord_fd_init &&get_keychord_fd() > 0) {//keychord相关计数

            ufds[fd_count].fd = get_keychord_fd();

            ufds[fd_count].events = POLLIN;

            ufds[fd_count].revents = 0;

            fd_count++;

            keychord_fd_init = 1;

        }

        …………

       nr = poll(ufds, fd_count, timeout);//开始进入poll模式,注册各类handle,都很复杂

        if (nr <= 0)

            continue;

        for (i = 0; i < fd_count; i++) {

            if (ufds[i].revents == POLLIN) {

                if (ufds[i].fd == get_property_set_fd())

                    handle_property_set_fd();

                else if (ufds[i].fd ==get_keychord_fd())

                    handle_keychord();

                else if (ufds[i].fd ==get_signal_fd())

                    handle_signal();

            }

        }

   }

   return0;

}

 

回顾

         至此init相关的代码我们就差不多讲完了,大家都看了init在安卓启动过程中都做了些什么,而且都是怎么做的。

         我稍作说明:

         引自ueventd.rc

    /dev/binder               0666   root      root

    看到binder驱动映射的时候了吧,呵呵。

         引自init.rc

    serviceadbd /sbin/adbd 

         service servicemanager/system/bin/servicemanager

         service zygote /system/bin/app_process-Xzygote /system/bin --zygote --start-system-server

看到adb启动了吧,看到servicemanager启动了吧,看到zygote启动了吧。所以我们的疑惑是不是打开了呢,我们不管底层linux做了什么有一些什么,但是我们经常会听到别人提前什么binder驱动binder通讯,什么zygoteservicemanager等,今天我们终于知道它是在init的时候启动的了。并且按照我上面代码流程,你会在源文件中看见详细的启动过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值