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



  • 相关阅读:
    Windows Azure Storage (17) Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)
    SQL Azure (15) SQL Azure 新的规格
    Azure China (5) 管理Azure China Powershell
    Azure China (4) 管理Azure China Storage Account
    Azure China (3) 使用Visual Studio 2013证书发布Cloud Service至Azure China
    Azure China (2) Azure China管理界面初探
    Azure China (1) Azure公有云落地中国
    SQL Azure (14) 将云端SQL Azure中的数据库备份到本地SQL Server
    [New Portal]Windows Azure Virtual Machine (23) 使用Storage Space,提高Virtual Machine磁盘的IOPS
    Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())的注意点
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/2998586.html
Copyright © 2020-2023  润新知