zoukankan      html  css  js  c++  java
  • Android Init进程分析

    之前在看android启动过程总是带着完成工作任务的目的去分析代码,但是对于一些代码的细节并不是很清楚,在这里就分析一下Init进程的执行过程。

    Init进程是android系统起来之后启动的第一个进程,对于研究android系统的启动过程很重要。

    直接根据代码来分析整个进程的执行过程。

    int main(int argc, char **argv)
    {
        int fd_count = 0;
        struct pollfd ufds[4];//存放pollfd
        char *tmpdev;
        char* debuggable;
        char tmp[32];
        int property_set_fd_init = 0;
        int signal_fd_init = 0;
        int keychord_fd_init = 0;
    
        if (!strcmp(basename(argv[0]), "ueventd"))
            return ueventd_main(argc, argv);//ueventd是init的软链接,执行这个进程的时候相当于执行init进程,然后根据进程名进入相应的执行流程
    
        /* 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.
             */
        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.
             */
    

    以上部分不是比较浅显,不是分析的重点

        open_devnull_stdio();//重定向标准输入,输入,错误到/dev/__null__(dup2复制文件句柄,0,1,2分别代表标准输入 输出 错误)
        log_init();//设置log信息输出设备/dev/__kmsg__,unlink之后其他进程无法访问
        
        INFO("reading config file\n");
        init_parse_config_file("/init.rc");//分析配置文件
    
        /* pull the kernel commandline and ramdisk properties file in */
        import_kernel_cmdline(0);

    这里导入相应的处理函数,分析执行过程

    static void import_kernel_cmdline(int in_qemu)
    {
        char cmdline[1024];
        char *ptr;
        int fd;
    
        fd = open("/proc/cmdline", O_RDONLY);
        if (fd >= 0) {
            int n = read(fd, cmdline, 1023);
            if (n < 0) n = 0;
    
            /* get rid of trailing newline, it happens */
            if (n > 0 && cmdline[n-1] == '\n') n--;
    //读取/proc/cmdline中的信息,存放在cmdline字符数组并进行处理
            cmdline[n] = 0;
            close(fd);
        } else {
            cmdline[0] = 0;
        }
    
        ptr = cmdline;
        while (ptr && *ptr) {
            char *x = strchr(ptr, ' ');
            if (x != 0) *x++ = 0;
            import_kernel_nv(ptr, in_qemu);//根据' '间断符逐行分析文本
            ptr = x;
        }
    
            /* don't expose the raw commandline to nonpriv processes */
        chmod("/proc/cmdline", 0440);
    }
    
    static void import_kernel_nv(char *name, int in_qemu)
    {
        char *value = strchr(name, '=');
    
        if (value == 0) {
    	if (!strcmp(name, "calibration"))
    	    calibration = 1;//表示要校准还是什么?
    	return;
        }
        *value++ = 0;
        if (*name == 0) return;
    
        if (!in_qemu)
        {
            /* on a real device, white-list the kernel options */
            if (!strcmp(name,"qemu")) {
                strlcpy(qemu, value, sizeof(qemu));
            } else if (!strcmp(name,"androidboot.console")) {
                strlcpy(console, value, sizeof(console));
            } else if (!strcmp(name,"androidboot.mode")) {
                strlcpy(bootmode, value, sizeof(bootmode));//启动模式
            } else if (!strcmp(name,"androidboot.serialno")) {
                strlcpy(serialno, value, sizeof(serialno));
            } else if (!strcmp(name,"androidboot.baseband")) {
                strlcpy(baseband, value, sizeof(baseband));//基带
            } else if (!strcmp(name,"androidboot.carrier")) {
                strlcpy(carrier, value, sizeof(carrier));
            } else if (!strcmp(name,"androidboot.bootloader")) {
                strlcpy(bootloader, value, sizeof(bootloader));
            } else if (!strcmp(name,"androidboot.hardware")) {
                strlcpy(hardware, value, sizeof(hardware));
            }//将以上设备信息存放在定义的字符数组中
        } else {
            /* in the emulator, export any kernel option with the
             * ro.kernel. prefix */
            char  buff[32];
            int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
            if (len < (int)sizeof(buff)) {
                property_set( buff, value );
            }
        }
    }
    
    
        get_hardware_name(hardware, &revision);
        snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
        init_parse_config_file(tmp);//分析相应硬件版本的rc文件
    
    

    init.rc文件有自己相应的语法,分析rc文件也是根据对应的语法来分析,这里引入一片简单介绍init.rc语法的文章

    Android init.rc脚本解析

    int init_parse_config_file(const char *fn)
    {
        char *data;
        data = read_file(fn, 0);//这里通过read_file函数将fn文件中的数据全部读取到字符数组中,malloc分配空间
        if (!data) return -1;
        //这里开始真正分析脚本中的命令
        parse_config(fn, data);
        DUMP();
        return 0;
    }
    
    
    static void parse_config(const char *fn, char *s)
    {
        struct parse_state state;
        char *args[INIT_PARSER_MAXARGS];//允许解析出来的命令行最多有64个参数
        int nargs;
    
        nargs = 0;
        state.filename = fn;
        state.line = 1;
        state.ptr = s;
        state.nexttoken = 0;
        state.parse_line = parse_line_no_op;
        for (;;) {
            switch (next_token(&state)) {//通过next_token函数来寻找字符数组中的关键标记
            //这里面省略了一些字符的处理(如‘\r’, '\t', '"', ' '等),只针对有效字符进行处理('\0', '\n'等)
            //#define T_EOF 0    #define T_TEXT 1    #define T_NEWLINE 2
            case T_EOF:
                state.parse_line(&state, 0, 0);
                return;
            case T_NEWLINE:
                if (nargs) {
                    int kw = lookup_keyword(args[0]);//这里将分析第一个参数所代表的关键字
                    //根据字符匹配返回已定义好的宏定义
                    if (kw_is(kw, SECTION)) {//当关键字是on或service
                        state.parse_line(&state, 0, 0); 
                        parse_new_section(&state, kw, nargs, args);//对state.parse_line进行赋值
                        //这里表示的是一段新的SECTION,此时会在action或这service双向链表中加入新的结点
                        //首先是将action或者service加入到相应的链表尾部
                    } else {
                        state.parse_line(&state, nargs, args);
                        //如果是command,将这些cmamand加入到所属的action链表的尾部
                    }
                    nargs = 0;
                }
                break;
            case T_TEXT:
                if (nargs < INIT_PARSER_MAXARGS) {
                    args[nargs++] = state.text;
                }
                break;
            }
        }
    }
    

    宏定义如下:

    enum {
        K_UNKNOWN,
    #endif
        KEYWORD(capability,  OPTION,  0, 0)
        KEYWORD(chdir,       COMMAND, 1, do_chdir)
        KEYWORD(chroot,      COMMAND, 1, do_chroot)
        KEYWORD(class,       OPTION,  0, 0)
        KEYWORD(class_start, COMMAND, 1, do_class_start)
        KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
        KEYWORD(class_reset, COMMAND, 1, do_class_reset)
        KEYWORD(console,     OPTION,  0, 0)
        KEYWORD(critical,    OPTION,  0, 0)
        KEYWORD(disabled,    OPTION,  0, 0)
        KEYWORD(domainname,  COMMAND, 1, do_domainname)
        KEYWORD(exec,        COMMAND, 1, do_exec)
        KEYWORD(export,      COMMAND, 2, do_export)
        KEYWORD(group,       OPTION,  0, 0)
        KEYWORD(hostname,    COMMAND, 1, do_hostname)
        KEYWORD(ifup,        COMMAND, 1, do_ifup)
        KEYWORD(insmod,      COMMAND, 1, do_insmod)
        KEYWORD(import,      SECTION, 1, 0)
        KEYWORD(keycodes,    OPTION,  0, 0)
        KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
        KEYWORD(mount,       COMMAND, 3, do_mount)
        KEYWORD(on,          SECTION, 0, 0)
        KEYWORD(oneshot,     OPTION,  0, 0)
        KEYWORD(onrestart,   OPTION,  0, 0)
        KEYWORD(restart,     COMMAND, 1, do_restart)
        KEYWORD(rm,          COMMAND, 1, do_rm)
        KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
        KEYWORD(service,     SECTION, 0, 0)
        KEYWORD(setenv,      OPTION,  2, 0)
        KEYWORD(setkey,      COMMAND, 0, do_setkey)
        KEYWORD(setprop,     COMMAND, 2, do_setprop)
        KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
        KEYWORD(socket,      OPTION,  0, 0)
        KEYWORD(start,       COMMAND, 1, do_start)
        KEYWORD(stop,        COMMAND, 1, do_stop)
        KEYWORD(trigger,     COMMAND, 1, do_trigger)
        KEYWORD(symlink,     COMMAND, 1, do_symlink)
        KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
        KEYWORD(user,        OPTION,  0, 0)
        KEYWORD(wait,        COMMAND, 1, do_wait)
        KEYWORD(write,       COMMAND, 2, do_write)
        KEYWORD(copy,        COMMAND, 2, do_copy)
        KEYWORD(chown,       COMMAND, 2, do_chown)
        KEYWORD(chmod,       COMMAND, 2, do_chmod)
        KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
        KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
        KEYWORD(ioprio,      OPTION,  0, 0)
    #ifdef __MAKE_KEYWORD_ENUM__
        KEYWORD_COUNT,
    };

    这里还涉及到一些结构体Action及对应的Command,Service也是如此

    struct command
    {
            /* list of commands in an action */
        struct listnode clist;
    
        int (*func)(int nargs, char **args);
        int nargs;
        char *args[1];
    };
        
    struct action {
            /* node in list of all actions */
        struct listnode alist;
            /* node in the queue of pending actions */
        struct listnode qlist;
            /* node in list of actions for a trigger */
        struct listnode tlist;
    
        unsigned hash;
        const char *name;
        
        struct listnode commands;
        struct command *current;
    };
        
        action_for_each_trigger("early-init",action_add_queue_tail);
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
      

    以上两个函数功能其实是一样的,action_for_each_trigger是将action放到即将执行的链表中(确保了执行顺序),而queue_builtin_action是将action放到整体的链表中

    正常的执行顺序是early-init——>init——>early-fs——>fs——>early-boot——>boot

        queue_builtin_action(property_init_action, "property_init");
        queue_builtin_action(keychord_init_action, "keychord_init");
        queue_builtin_action(console_init_action, "console_init");
        queue_builtin_action(set_init_properties_action, "set_init_properties");
    
            /* execute all the boot actions to get us started */
        action_for_each_trigger("init", action_add_queue_tail);
        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);
    
        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");
    
        /* execute all the boot actions to get us started */
        action_for_each_trigger("early-boot", action_add_queue_tail);
        action_for_each_trigger("boot", action_add_queue_tail);
    
            /* run all property triggers based on current state of the properties */
        queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");
    
    
    #if BOOTCHART
        queue_builtin_action(bootchart_init_action, "bootchart_init");
    #endif
    
    

    以下又是分析的重点

        for(;;) {
            int nr, i, timeout = -1;
    
            execute_one_command();//从链表中取出结点相应执行然后remove
            //分析过这个函数,在这里还有个疑问,该函数都是从action队列中去结点执行,但是系统的service是怎么执行的
            //难道service链表不可能只注册不执行
            //这里注意on boot section中最后一个command(class_start default),最终调用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;
    }

    执行service_list中的结点

    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);
            }
        }
    }

    这里就将链表中的所有结点都执行完毕,over

    #define list_for_each(node, list) \
        for (node = (list)->next; node != (list); node = node->next)
    
            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) {//组合键检测
                ufds[fd_count].fd = get_keychord_fd();
                ufds[fd_count].events = POLLIN;
                ufds[fd_count].revents = 0;
                fd_count++;
                keychord_fd_init = 1;
            }
    
            if (process_needs_restart) {
                timeout = (process_needs_restart - gettime()) * 1000;
                if (timeout < 0)
                    timeout = 0;
            }
    
            if (!action_queue_empty() || cur_action)
                timeout = 0;
    
    #if 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
    
            nr = poll(ufds, fd_count, timeout);
            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();
                }
            }
        }
    
        return 0;
    }
    



  • 相关阅读:
    介绍我的一位同事的开源RSS阅读器
    开源协议概谈[转载]
    编译错误CS1595
    JAVA和C#,武当和少林之争!
    IBatisNet之获取和操作SQL语句
    Linux能否靠架构取得胜利
    开源CMS Alfresco 1.0 发布
    在asp.net页面上得到Castle容器的实例
    IBatisNet 之 自动生成主关键字
    onvaluechange事件
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/2998586.html
Copyright © 2011-2022 走看看