0
点赞
收藏
分享

微信扫一扫

深入理解init_1----init分析(基于Android 2.2,源码来自Google)


深入理解init_1—-init分析

1、概述
Init是一个进程,确切的说,它是Linux系统中用户空间的第一个进程。由于android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1.作为天字第一号进程,init被赋予了很多及其重要的工作职责,下面就关注其中两个比较重要的职责:
1) Init 进程负责创建系统中几个关键进程,尤其是下一章要介绍的zygote,它更是java世界的开创者。那么init进程如何创建zygote的呢?
2) Android系统有很多属性,于是init就提供了一个property service (属性服务)来管理他们。那么这个属性服务怎样工作的呢?

下面我们就这两个问题来分析init:
(1) Init 如何创建 zygote?
(2) Init的属性服务是如何工作的。

深入理解init_1----init分析(基于Android 2.2,源码来自Google)_配置文件

init.c 主要函数功能概略图

2、init分析
2.1、文件位置
System/core/init.c
2.2部分代码分析

int main(int argc, char **argv)
{
    int device_fd = -1;
    int property_set_fd = -1;
    int signal_recv_fd = -1;
    int keychord_fd = -1;
    int fd_count;
    int s[2];
    int fd;
    struct sigaction act;
    char tmp[PROP_VALUE_MAX];
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;

//设置子进程退出的信号处理函数,函数为 sigchld_handler.
    act.sa_handler = sigchld_handler;
    act.sa_flags = SA_NOCLDSTOP;
    act.sa_mask = 0;
act.sa_restorer = NULL;
sigaction(SIGCHLD, &act, 0);

/* clear the umask */
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
//创建一些文件夹,并挂载设备,这些是与Linux相关的
    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);

    /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
//重定向标准输入/输出/错误输出到 /dev/_null_
    open_devnull_stdio();
/*
设置init的日志输出设备为 /dev/_kmsg_,不过这个文件被打开后,会立即被UNLINK了,这样,其他进程就没有办法打开这个文件读取日志信息。
*/

log_init();

    //解析init.rc 配置信息
    INFO("reading config file\n");
    parse_config_file("/init.rc");

/* pull the kernel commandline and ramdisk properties file in */
    qemu_init();
    import_kernel_cmdline(0);

//下面这个函数通过读取/proc/cpuinfo 得到机器的Hardware名,笔者手机是HTC G7,手机为bravo。
    get_hardware_name();
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
//解析这个和机器相关的的配置文件,笔者的G7手机对应的文件为init.bravo.rc
    parse_config_file(tmp);
/*解析完上述两个配置文件,会得到一些列的Action,下面两句代码将执行那些处于early-init阶段的Action,init将动作执行的时间划分为四个阶段early-init 、init、early-boot、boot 。由于有些动作必须在其它动作完成后才能执行,所以就有了先后之分,哪些动作属于哪个阶段由配置文件决定,后面会介绍配置文件的相关信息*/

action_for_each_trigger("early-init", action_add_queue_tail);
    drain_action_queue();
//创建利用Uevent与Linux内核交互的Socket
    INFO("device init\n");
    device_fd = device_init();
//初始化和属性相关的资源
    property_init();

    // only listen for keychords if ro.debuggable is true
//初始化 /dev/keychord设备
    keychord_fd = open_keychord();

    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);
/*INIT_IMAGE_FILE 定义为“/initlogo.rle”,下面这个函数将加载这个文件作为系统的开机画面,注意,它不是开机动画控制程序bootanimation加载的开机动画文件,如果加载 initlogo.rle文件失败(可能是没有这个文件),则会打开 /dev/ty0设备,并会输出“ANDROID” 字样作为开机动画,在模拟器上看到的开机动画就是它*/
    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);
    }
    }

    if (qemu[0])
        import_kernel_cmdline(1); 

    if (!strcmp(bootmode,"factory"))
        property_set("ro.factorytest", "1");
    else if (!strcmp(bootmode,"factory2"))
        property_set("ro.factorytest", "2");
    else
        property_set("ro.factorytest", "0");

    property_set("ro.serialno", serialno[0] ? serialno : "");
    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
//调用property_set 函数设置属性项,一个属性项包括属性名和属性值。
    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");

    property_set("ro.hardware", hardware);
    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
    property_set("ro.revision", tmp);

        /* execute all the boot actions to get us started */
//执行位于init阶段的过程
    action_for_each_trigger("init", action_add_queue_tail);
    drain_action_queue();

        /* read any property files on system or data and
         * fire up the property service.  This must happen
         * after the ro.foo properties are set above so
         * that /data/local.prop cannot interfere with them.
         */
//启动属性服务
    property_set_fd = start_property_service();

    /* create a signalling mechanism for the sigchld handler */
//调用socketpair函数创建两个已经connect好的socket。Socketpair是Linux系统调用。不熟悉的可以使用 man socketpair 查询相关信息
    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);
    }

    /* make sure we actually have all the pieces we need */
    if ((device_fd < 0) ||
        (property_set_fd < 0) ||
        (signal_recv_fd < 0)) {
        ERROR("init startup failure\n");
        return 1;
    }

    /* execute all the boot actions to get us started */
//执行文件中的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();

        /* 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;     
//init关注来自四个方面的事情
    ufds[0].fd = device_fd;         //device_fd用于监听来自内核的Uevent事件
    ufds[0].events = POLLIN;
    ufds[1].fd = property_set_fd;    //property_set_fd用于监听来自属性服务器的事件
    ufds[1].events = POLLIN;
    ufds[2].fd = signal_recv_fd;    //signal_recv_fd由socketpair创建,它的时间来自于另一个socket
    ufds[2].events = POLLIN;
    fd_count = 3;

    if (keychord_fd > 0) {
        ufds[3].fd = keychord_fd;       //如果keychord设备初始化成功,则init也会关注来自这个设备的事件
        ufds[3].events = POLLIN;
        fd_count++;
    } else {
        ufds[3].events = 0;
        ufds[3].revents = 0;
    }

#if BOOTCHART
// Boot chart 是一个小工具,它能对系统的性能进行分析,并且生成系统启动过程的图表,以提供一些有价值的信息,而这些信息的作用是帮助提升系统的启动速度。
    bootchart_count = bootchart_init();
    if (bootchart_count < 0) {
        ERROR("bootcharting init failure\n");
    } else if (bootchart_count > 0) {
        NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
    } else {
        NOTICE("bootcharting ignored\n");
    }
#endif
//从此init将进入一个无限循环。
    for(;;) {
        int nr, i, timeout = -1;

        for (i = 0; i < fd_count; i++)
            ufds[i].revents = 0;
//在循环中执行动作
        drain_action_queue();
        restart_processes();  //重启那些已经死去的进程

        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }

#if BOOTCHART
//和  BOOTCHART 相关
        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

//调用PULL等待一些事情的发生
        nr = poll(ufds, fd_count, timeout);
        if (nr <= 0)
            continue;
//ufds[2] 保存的是signal_recv_fd,用于接收来自socket的消息。
        if (ufds[2].revents == POLLIN) {
            /* we got a SIGCHLD - reap and restart as needed */
/*有一个子进程去世,init要处理这个事情*/
            read(signal_recv_fd, tmp, sizeof(tmp));
            while (!wait_for_one_process(0))
                ;
            continue;
        }

        if (ufds[0].revents == POLLIN)
            handle_device_fd(device_fd);      //处理Uevent事件

        if (ufds[1].revents == POLLIN)
            handle_property_set_fd(property_set_fd);    //处理属性服务其事件
        if (ufds[3].revents == POLLIN)
            handle_keychord(keychord_fd);      //处理keychord事件
    }

    return 0;
}

从上面的代码可以得到,init的工作任务很重,上面的代码省略了不少,可结果还是很长,从上面分析的结果来看,可将init的工作流程精简为以下四点:

1) 解析两个配置文件,我们将分析其中一个init.rc 文件。
2) 执行各个阶段的动作,创建zygote就是在其中某个阶段完成的。
3) 调用property_init ,初始化属性相关的资源,并且通过property_start_service启动属性服务。
4) Init进入一个无限循环,并且等待一些事情的发生。重点可关注init如何处理来自socket和来自属性服务的相关事情。

文档参考:

整理抄录自 — 《深入理解Android卷1》,邓凡平著。


举报

相关推荐

Android-深入理解init

0 条评论