Android init 进程源码分析

来源:互联网 发布:网络平台都有哪些功能 编辑:程序博客网 时间:2024/05/14 19:45

基于Linux内核的android系统,在内核启动完成后将创建一个Init用户进程,实现了内核空间到用户空间的转变。在Android 启动过程介绍一文中介绍了Android系统的各个启动阶段,init进程启动后会读取init.rc配置文件,通过fork系统调用启动init.rc文件中配置的各个Service进程。init进程首先启动启动android的服务大管家ServiceManager服务,然后启动Zygote进程。Zygote进程的启动开创了Java世界,无论是SystemServer进程还是android的应用进程都是Zygote的子进程,Zygote进程启动过程的源代码分析一文中详细介绍了Zygote进程的启动过程,System Server进程启动过程源码分析则详细介绍了在Zygote进程启动完成后创建的第一个进程SystemServer进程的启动过程,SystemServer进程的启动包括两个阶段,在第一阶段主要是启动C++相关的本地服务,如SurfaceFlinger等,在第二阶段通过在ServerThread线程中启动android的各大关键Java服务。Zygote孵化应用进程过程的源码分析一文中详细介绍了Zygote进程创建android应用进程的过程,当用户点击Luncher上的应用图标时,Luncher进程通过socket向Zygote进程发送进程创建请求,Zygote进程接受客户端的请求后,通过fork系统调用为应用程序创建相应的进程。本文则介绍android用户进程的始祖Init进程,Init进程是Linux系统中用户空间的第一个进程,负责创建系统中的关键进程,同时提供属性服务来管理系统属性。

Android进程模型

Linux通过调用start_kernel函数来启动内核,当内核启动模块启动完成后,将启动用户空间的第一个进程——Init进程,下图为Android系统的进程模型图:

从上图可以看出,Linux内核在启动过程中,创建一个名为Kthreadd的内核进程,PID=2,用于创建内核空间的其他进程;同时创建第一个用户空间Init进程,该进程PID = 1,用于启动一些本地进程,比如Zygote进程,而Zygote进程也是一个专门用于孵化Java进程的本地进程,上图清晰地描述了整个Android系统的进程模型,为了证明以上进程模型的正确性,可以通过ps命令来查看进程的PID级PPID,下图显示了Init进程的PID为1,其他的本地进程的PPID都是1,说明它们的父进程都是Init进程,都是由Init进程启动的。

下图显示kthreadd进程的PID=2,有一部分内核进程如binder、dhd_watchdog等进程的PPID=2,说明这些进程都是由kthreadd进程创建:

上图中显示zygote进程PID=107,下图显示了zygote进程创建的子进程,从图中可以看到,zygote进程创建的都是Java进程,证明了zygote进程开创了Android系统的Java世界。

上面介绍了Android系统的进程模型设计,接下来将详细分析Init进程。

Init进程源码分析

上节介绍了Init进程在Linux内核启动时被创建的,那它是如何启动的呢?

Init进程启动分析

在Linux内核启动过程中,将调用Start_kernel来初始化配置:

[cpp] view plain copy
  1. asmlinkage void __init start_kernel(void)  
  2. {  
  3.     .............. //执行初始化工作  
  4.     rest_init();   
  5. }  
start_kernel函数调用一些初始化函数完成初始化工作后,调用rest_init()函数来创建新的进程:

[cpp] view plain copy
  1. static noinline void __init_refok rest_init(void)  
  2.     __releases(kernel_lock)  
  3. {  
  4.     int pid;  
  5.   
  6.     rcu_scheduler_starting();  
  7.     //创建一个kernel_init进程,该进程实质上是Init进程,用于启动用户空间进程  
  8.     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);   
  9.     numa_default_policy();  
  10.     //创建一个kthreadd内核线程,用于创建新的内核进程  
  11.     pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);  
  12.    
  13.     rcu_read_lock();  
  14.     kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);  
  15.     rcu_read_unlock();  
  16.     complete(&kthreadd_done);  
  17.     unlock_kernel();  
  18.   
  19.     /* 
  20.      * The boot idle thread must execute schedule() 
  21.      * at least once to get things moving: 
  22.      */  
  23.     init_idle_bootup_task(current);  
  24.     preempt_enable_no_resched();  
  25.     schedule();   
  26.     preempt_disable();  
  27.   
  28.     /* Call into cpu_idle with preempt disabled */  
  29.     cpu_idle();  
  30. }  
在rest_init函数里完成两个新进程的创建:Init进程和kthreadd进程,因为Init进程创建在先,所以其PID=1而kthreadd的PID=2,本文只对Init进程进行详细分析,如果读者对kthreadd进行感兴趣,可自行分析。

kernel_thread函数仅仅调用了fork系统调用来创建新的进程,创建的子进程和父进程都执行在fork函数调用之后的代码,子进程是父进程的一个拷贝。

[cpp] view plain copy
  1. static int __init kernel_init(void * unused)  
  2. {  
  3.     /* 
  4.      * Wait until kthreadd is all set-up. 
  5.      */  
  6.     wait_for_completion(&kthreadd_done);  
  7.     /* 
  8.      * init can allocate pages on any node 
  9.      */  
  10.     set_mems_allowed(node_states[N_HIGH_MEMORY]);  
  11.     /* 
  12.      * init can run on any cpu. 
  13.      */  
  14.     set_cpus_allowed_ptr(current, cpu_all_mask);  
  15.   
  16.     cad_pid = task_pid(current);  
  17.   
  18.     smp_prepare_cpus(setup_max_cpus);  
  19.     //执行保存在__initcall_start与__early_initcall_end之间的函数  
  20.     do_pre_smp_initcalls();  
  21.     lockup_detector_init();  
  22.     //smp 多核初始化处理  
  23.     smp_init();  
  24.     sched_init_smp();  
  25.     //内核驱动模块初始化  
  26.     do_basic_setup();  
  27.   
  28.     /* Open the /dev/console on the rootfs, this should never fail */  
  29.     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)  
  30.         printk(KERN_WARNING "Warning: unable to open an initial console.\n");  
  31.   
  32.     (void) sys_dup(0);  
  33.     (void) sys_dup(0);  
  34.     /* 
  35.      * check if there is an early userspace init.  If yes, let it do all 
  36.      * the work 
  37.      */  
  38.     if (!ramdisk_execute_command)  
  39.         ramdisk_execute_command = "/init";  
  40.   
  41.     if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  42.         ramdisk_execute_command = NULL;  
  43.         prepare_namespace();  
  44.     }  
  45.     /* 
  46.      * Ok, we have completed the initial bootup, and 
  47.      * we're essentially up and running. Get rid of the 
  48.      * initmem segments and start the user-mode stuff.. 
  49.      * 进入用户空间,执行用户空间代码 
  50.      */  
  51.   
  52.     init_post();  
  53.     return 0;  
  54. }  
在kernel_init函数中调用__initcall_start到__initcall_end之间保存的函数进行驱动模块初始化,然后直接调用init_post()函数进入用户空间,执行Init 进程代码。

[cpp] view plain copy
  1. static noinline int init_post(void)  
  2. {  
  3.     /* need to finish all async __init code before freeing the memory */  
  4.     async_synchronize_full();  
  5.     free_initmem();  
  6.     mark_rodata_ro();  
  7.     system_state = SYSTEM_RUNNING;  
  8.     numa_default_policy();  
  9.   
  10.     current->signal->flags |= SIGNAL_UNKILLABLE;  
  11.     //如果ramdisk_execute_command不为空,ramdisk_execute_command下的Init程序  
  12.     if (ramdisk_execute_command) {  
  13.         run_init_process(ramdisk_execute_command);  
  14.         printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);  
  15.     }  
  16.     //如果execute_command不为空,execute_command下的Init程序  
  17.     if (execute_command) {  
  18.         run_init_process(execute_command);  
  19.         printk(KERN_WARNING "Failed to execute %s.  Attempting ""defaults...\n", execute_command);  
  20.     }  
  21.     //如果以上路径下都没有init程序,就从/sbin、/etc、/bin三个路径下寻找init程序,同时启动一个sh进程  
  22.     run_init_process("/sbin/init");  
  23.     run_init_process("/etc/init");  
  24.     run_init_process("/bin/init");  
  25.     run_init_process("/bin/sh");  
  26.     //如果以上路径都没有找到init程序,调用内核panic  
  27.     panic("No init found.  Try passing init= option to kernel. "  
  28.           "See Linux Documentation/init.txt for guidance.");  
  29. }  
当根文件系统顶层目录中不存在init进程,或未指定启动选项"init="时,内核会到/sbin、/etc、/bin目录下查找init文件。如果在这些目录中仍未找到init文件,内核就会中止执行init进程,并引发Kernel Panic。run_init_process函数通过系统调用do_execve从内核空间跳转到用户空间,并且执行用户空间的Init程序的入口函数。

[cpp] view plain copy
  1. static void run_init_process(const char *init_filename)  
  2. {  
  3.     argv_init[0] = init_filename;  
  4.     kernel_execve(init_filename, argv_init, envp_init);  
  5. }  
这里就介绍完了内核启动流程,run_init_process函数的将执行Init程序的入口函数,Init的入口函数位于/system/core/init/init.c

Init进程源码分析

Android的init进程主要功能:
1)、分析init.rc启动脚本文件,根据文件内容执行相应的功能;
2)、当一些关键进程死亡时,重启该进程;
3)、提供Android系统的属性服务;

[cpp] view plain copy
  1. int main(int argc, char **argv)  
  2. {  
  3.     int fd_count = 0;  
  4.     struct pollfd ufds[4];  
  5.     char *tmpdev;  
  6.     char* debuggable;  
  7.     char tmp[32];  
  8.     int property_set_fd_init = 0;  
  9.     int signal_fd_init = 0;  
  10.     int keychord_fd_init = 0;  
  11.     bool is_charger = false;  
  12.   
  13.     if (!strcmp(basename(argv[0]), "ueventd"))  
  14.         return ueventd_main(argc, argv);  
  15.   
  16.     /* clear the umask */  
  17.     umask(0);  
  18.     //挂载tmpfs,devpts,proc,sysfs 4类文件系统  
  19.     mkdir("/dev", 0755);  
  20.     mkdir("/proc", 0755);  
  21.     mkdir("/sys", 0755);  
  22.     mount("tmpfs""/dev""tmpfs", MS_NOSUID, "mode=0755");  
  23.     mkdir("/dev/pts", 0755);  
  24.     mkdir("/dev/socket", 0755);  
  25.     mount("devpts""/dev/pts""devpts", 0, NULL);  
  26.     mount("proc""/proc""proc", 0, NULL);  
  27.     mount("sysfs""/sys""sysfs", 0, NULL);  
  28.   
  29.     /* indicate that booting is in progress to background fw loaders, etc */  
  30.     close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));  
  31.     //屏蔽标准的输入输出,即标准的输入输出定向到NULL设备。  
  32.     open_devnull_stdio();  
  33.     // log 初始化  
  34.     klog_init();  
  35.     // 属性存储空间初始化  
  36.     property_init();  
  37.     //读取机器硬件名称  
  38.     get_hardware_name(hardware, &revision);  
  39.     //设置基本属性  
  40.     process_kernel_cmdline();  
  41.   
  42. #ifdef HAVE_SELINUX  
  43.     INFO("loading selinux policy\n");  
  44.     selinux_load_policy();  
  45. #endif  
  46.     //判断当前启动模式  
  47.     is_charger = !strcmp(bootmode, "charger");  
  48.       
  49.     INFO("property init\n");  
  50.     if (!is_charger)  
  51.         //读取默认的属性文件  
  52.         property_load_boot_defaults();  
  53.     //解析init.rc文件  
  54.     INFO("reading config file\n");  
  55.     init_parse_config_file("/init.rc");  
  56.     //将early-init动作添加到链表action_queue中   
  57.     action_for_each_trigger("early-init", action_add_queue_tail);  
  58.     //创建wait_for_coldboot_done 动作并添加到链表action_queue和action_list中  
  59.     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  60.     //创建keychord_init动作并添加到链表action_queue和action_list中  
  61.     queue_builtin_action(keychord_init_action, "keychord_init");  
  62.     //创建console_init动作并添加到链表action_queue和action_list中  
  63.     queue_builtin_action(console_init_action, "console_init");  
  64.     //将init动作添加到链表action_queue中  
  65.     action_for_each_trigger("init", action_add_queue_tail);  
  66.     //将early-fs动作添加到链表action_queue中  
  67.     action_for_each_trigger("early-fs", action_add_queue_tail);  
  68.     //将fs动作添加到链表action_queue中  
  69.     action_for_each_trigger("fs", action_add_queue_tail);  
  70.     //将post-fs动作添加到链表action_queue中  
  71.     action_for_each_trigger("post-fs", action_add_queue_tail);  
  72.     //非充电模式下,将post-fs-data动作添加到链表action_queue中  
  73.     if (!is_charger) {  
  74.         action_for_each_trigger("post-fs-data", action_add_queue_tail);  
  75.     }  
  76.     //创建property_service_init动作并添加到链表action_queue和action_list中  
  77.     queue_builtin_action(property_service_init_action, "property_service_init");  
  78.     //创建signal_init动作并添加到链表action_queue和action_list中  
  79.     queue_builtin_action(signal_init_action, "signal_init");  
  80.     //创建check_startup动作并添加到链表action_queue和action_list中  
  81.     queue_builtin_action(check_startup_action, "check_startup");  
  82.   
  83.     if (!strcmp(bootmode, "alarm")) {  
  84.         action_for_each_trigger("alarm", action_add_queue_tail);  
  85.     }  
  86.       
  87.     if (is_charger) {  
  88.         //充电模式下,将charger动作添加到链表action_queue中  
  89.         action_for_each_trigger("charger", action_add_queue_tail);  
  90.     } else {  
  91.         //非充电模式下,将early-boot、boot动作添加到链表action_queue中  
  92.         action_for_each_trigger("early-boot", action_add_queue_tail);  
  93.         action_for_each_trigger("boot", action_add_queue_tail);  
  94.     }  
  95.     //创建queue_property_triggers动作并添加到链表action_queue和action_list中  
  96.     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  97.   
  98.   
  99. #if BOOTCHART  
  100.     //如果BOOTCHART宏定义了,创建bootchart_init动作并添加到链表action_queue和action_list中  
  101.     queue_builtin_action(bootchart_init_action, "bootchart_init");  
  102. #endif  
  103.   
  104.     for(;;) {  
  105.         int nr, i, timeout = -1;  
  106.         //按序执行action_queue里的action  
  107.         execute_one_command();  
  108.         //重启一些关键进程  
  109.         restart_processes();  
  110.         //添加事件句柄到句柄次  
  111.         if (!property_set_fd_init && get_property_set_fd() > 0) {  
  112.             ufds[fd_count].fd = get_property_set_fd();  
  113.             ufds[fd_count].events = POLLIN;  
  114.             ufds[fd_count].revents = 0;  
  115.             fd_count++;  
  116.             property_set_fd_init = 1;  
  117.         }  
  118.         if (!signal_fd_init && get_signal_fd() > 0) {  
  119.             ufds[fd_count].fd = get_signal_fd();  
  120.             ufds[fd_count].events = POLLIN;  
  121.             ufds[fd_count].revents = 0;  
  122.             fd_count++;  
  123.             signal_fd_init = 1;  
  124.         }  
  125.         if (!keychord_fd_init && get_keychord_fd() > 0) {  
  126.             ufds[fd_count].fd = get_keychord_fd();  
  127.             ufds[fd_count].events = POLLIN;  
  128.             ufds[fd_count].revents = 0;  
  129.             fd_count++;  
  130.             keychord_fd_init = 1;  
  131.         }  
  132.         //计算超时时间  
  133.         if (process_needs_restart) {  
  134.             timeout = (process_needs_restart - gettime()) * 1000;  
  135.             if (timeout < 0)  
  136.                 timeout = 0;  
  137.         }  
  138.   
  139.         if (!action_queue_empty() || cur_action)  
  140.             timeout = 0;  
  141.   
  142. #if BOOTCHART  
  143.         if (bootchart_count > 0) {  
  144.             if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)  
  145.                 timeout = BOOTCHART_POLLING_MS;  
  146.             if (bootchart_step() < 0 || --bootchart_count == 0) {  
  147.                 bootchart_finish();  
  148.                 bootchart_count = 0;  
  149.             }  
  150.         }  
  151. #endif  
  152.         //监控句柄池中的事件  
  153.         nr = poll(ufds, fd_count, timeout);  
  154.         if (nr <= 0)  
  155.             continue;  
  156.         //事件处理  
  157.         for (i = 0; i < fd_count; i++) {  
  158.             if (ufds[i].revents == POLLIN) {  
  159.                 if (ufds[i].fd == get_property_set_fd())  
  160.                     handle_property_set_fd();  
  161.                 else if (ufds[i].fd == get_keychord_fd())  
  162.                     handle_keychord();  
  163.                 else if (ufds[i].fd == get_signal_fd())  
  164.                     handle_signal();  
  165.             }  
  166.         }  
  167.     }  
  168.     return 0;  
  169. }  

文件系统简介

tmpfs文件系统

    tmpfs是一种虚拟内存文件系统,因此它会将所有的文件存储在虚拟内存中,并且tmpfs下的所有内容均为临时性的内容,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。tmpfs是一个独立的文件系统,不是块设备,只要挂接,立即就可以使用。

devpts文件系统   

    devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。

proc文件系统

    proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。

sysfs文件系统

    与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。

屏蔽标准的输入输出

[cpp] view plain copy
  1. void open_devnull_stdio(void)  
  2. {  
  3.     int fd;  
  4.     //创建一个字符专用文件/dev/__null__   
  5.     static const char *name = "/dev/__null__";  
  6.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {  
  7.         //获取/dev/__null__的文件描述符,并输出该文件  
  8.         fd = open(name, O_RDWR);  
  9.         unlink(name);  
  10.         if (fd >= 0) {  
  11.         //将与进程相关的标准输入(0),标准输出(1),标准错误输出(2),均定向到NULL设备  
  12.             dup2(fd, 0);  
  13.             dup2(fd, 1);  
  14.             dup2(fd, 2);  
  15.             if (fd > 2) {  
  16.                 close(fd);  
  17.             }  
  18.             return;  
  19.         }  
  20.     }  
  21.   
  22.     exit(1);  
  23. }  
将标准输入输出,错误输出重定向到/dev/_null_设备中

初始化内核log系统

[cpp] view plain copy
  1. void klog_init(void)  
  2. {  
  3.     static const char *name = "/dev/__kmsg__";  
  4.     //创建/dev/__kmsg__设备节点  
  5.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {  
  6.         klog_fd = open(name, O_WRONLY);  
  7.         //当进程在进行exec系统调用时,要确保log_fd是关闭的  
  8.         fcntl(klog_fd, F_SETFD, FD_CLOEXEC);  
  9.         unlink(name);  
  10.     }  
  11. }  

属性存储空间初始化

[cpp] view plain copy
  1. void property_init(void)  
  2. {  
  3.     init_property_area();  
  4. }  
关于Android的属性系统,请查看Android 系统属性SystemProperty分析一文,在这篇文章中详细分析了Android的属性系统。

读取机器硬件名称

从/proc/cpuinfo中获取“Hardware”字段信息写入<hw>;“Reversion” 字段信息写入<reversion>

[cpp] view plain copy
  1. void get_hardware_name(char *hardware, unsigned int *revision)  
  2. {  
  3.     char data[1024];  
  4.     int fd, n;  
  5.     char *x, *hw, *rev;  
  6.     /* Hardware string was provided on kernel command line */  
  7.     if (hardware[0])  
  8.         return;  
  9.     //打开/proc/cpuinfo文件  
  10.     fd = open("/proc/cpuinfo", O_RDONLY);  
  11.     if (fd < 0) return;  
  12.     //读取/proc/cpuinfo文件内容  
  13.     n = read(fd, data, 1023);  
  14.     close(fd);  
  15.     if (n < 0) return;  
  16.     data[n] = 0;  
  17.     hw = strstr(data, "\nHardware");  
  18.     rev = strstr(data, "\nRevision");  
  19.     if (hw) {  
  20.         x = strstr(hw, ": ");  
  21.         if (x) {  
  22.             x += 2;  
  23.             n = 0;  
  24.             while (*x && *x != '\n') {  
  25.                 if (!isspace(*x))  
  26.                     hardware[n++] = tolower(*x);  
  27.                 x++;  
  28.                 if (n == 31) break;  
  29.             }  
  30.             hardware[n] = 0;  
  31.         }  
  32.     }  
  33.     if (rev) {  
  34.         x = strstr(rev, ": ");  
  35.         if (x) {  
  36.             *revision = strtoul(x + 2, 0, 16);  
  37.         }  
  38.     }  
  39. }  
get_hardware_name函数从/proc/cpuinfo文件中读取硬件名称等信息,/proc/cpuinfo文件内容如下:

[plain] view plain copy
  1. Processor   : ARMv7 Processor rev 1 (v7l)  
  2. BogoMIPS    : 1024.00  
  3. Features    : swp half thumb fastmult vfp edsp thumbee neon vfpv3   
  4. CPU implementer : 0x41  
  5. CPU architecture: 7  
  6. CPU variant : 0x0  
  7. CPU part    : 0xc05  
  8. CPU revision    : 1  
  9. Hardware    : sc7710g  
  10. Revision    : 0000  
  11. Serial      : 0000000000000000  

设置命令行参数属性

[cpp] view plain copy
  1. static void process_kernel_cmdline(void)  
  2. {  
  3.     /* don't expose the raw commandline to nonpriv processes */  
  4.     chmod("/proc/cmdline", 0440);  
  5.   
  6.     /* first pass does the common stuff, and finds if we are in qemu. 
  7.      * second pass is only necessary for qemu to export all kernel params 
  8.      * as props. 
  9.      */  
  10.     import_kernel_cmdline(0, import_kernel_nv);  
  11.     if (qemu[0])  
  12.         import_kernel_cmdline(1, import_kernel_nv);  
  13.   
  14.     /* now propogate the info given on command line to internal variables 
  15.      * used by init as well as the current required properties 
  16.      */  
  17.     export_kernel_boot_props();  
  18. }  
process_kernel_cmdline函数首先修改/proc/cmdline文件权限,然后调用import_kernel_cmdline函数来读取/proc/cmdline文件的内容,并查找格式为:<key> = <value> 的字串,调用import_kernel_nv函数来设置属性。函数export_kernel_boot_props()用于设置内核启动时需要的属性。

[cpp] view plain copy
  1. void import_kernel_cmdline(int in_qemu,void (*import_kernel_nv)(char *name, int in_qemu))  
  2. {  
  3.     char cmdline[1024];  
  4.     char *ptr;  
  5.     int fd;  
  6.     //打开并读取/proc/cmdline文件  
  7.     fd = open("/proc/cmdline", O_RDONLY);  
  8.     if (fd >= 0) {  
  9.         int n = read(fd, cmdline, 1023);  
  10.         if (n < 0) n = 0;  
  11.         /* get rid of trailing newline, it happens */  
  12.         if (n > 0 && cmdline[n-1] == '\n') n--;  
  13.         cmdline[n] = 0;  
  14.         close(fd);  
  15.     } else {  
  16.         cmdline[0] = 0;  
  17.     }  
  18.       
  19.     ptr = cmdline;  
  20.     while (ptr && *ptr) {  
  21.         char *x = strchr(ptr, ' ');  
  22.         if (x != 0) *x++ = 0;  
  23.         //回调import_kernel_nv函数,in_qemu =0  
  24.         import_kernel_nv(ptr, in_qemu);  
  25.         ptr = x;  
  26.     }  
  27. }  
/proc/cmdline文件内容如下:

[plain] view plain copy
  1. initrd=0x4c00000,0x1118e8 lpj=3350528 apv="sp7710ga-userdebug 4.1.2 JZO54K W13.23.2-010544 test-keys" mem=256M init=/init mtdparts=sprd-nand:256k(spl),512k(2ndbl),256k(params),512k(vmjaluna),10m(modem),3840k(fixnv),3840k(backupfixnv),5120k(dsp),3840k(runtimenv),10m(boot),10m(recovery),260m(system),160m(userdata),20m(cache),256k(misc),1m(boot_logo),1m(fastboot_logo),3840k(productinfo),512k(kpanic),15m(firmware) console=null  lcd_id=ID18 ram=256M  
[cpp] view plain copy
  1. static void import_kernel_nv(char *name, int for_emulator)  
  2. {  
  3.     char *value = strchr(name, '=');  
  4.     int name_len = strlen(name);  
  5.     if (value == 0) return;  
  6.     *value++ = 0;  
  7.     if (name_len == 0) return;  
  8.   
  9. #ifdef HAVE_SELINUX  
  10.     if (!strcmp(name,"enforcing")) {  
  11.         selinux_enforcing = atoi(value);  
  12.     } else if (!strcmp(name,"selinux")) {  
  13.         selinux_enabled = atoi(value);  
  14.     }  
  15. #endif  
  16.     //判断是否为模拟器  
  17.     if (for_emulator) {  
  18.         /* in the emulator, export any kernel option with the 
  19.          * ro.kernel. prefix */  
  20.         char buff[PROP_NAME_MAX];  
  21.         int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );  
  22.         if (len < (int)sizeof(buff))  
  23.             property_set( buff, value );  
  24.         return;  
  25.     }  
  26.     //如果/proc/cmdline文件中有qemu关键字  
  27.     if (!strcmp(name,"qemu")) {  
  28.         strlcpy(qemu, value, sizeof(qemu));  
  29.     //如果/proc/cmdline文件中有以androidboot.开头的关键字  
  30.     } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {  
  31.         const char *boot_prop_name = name + 12;  
  32.         char prop[PROP_NAME_MAX];  
  33.         int cnt;  
  34.         //格式化为ro.boot.xx 属性  
  35.         cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);  
  36.         if (cnt < PROP_NAME_MAX)  
  37.             property_set(prop, value);  
  38.     }  
  39. }  
最后调用函数export_kernel_boot_props设置内核启动属性

[cpp] view plain copy
  1. static void export_kernel_boot_props(void)  
  2. {  
  3.     char tmp[PROP_VALUE_MAX];  
  4.     const char *pval;  
  5.     unsigned i;  
  6.     //属性表  
  7.     struct {  
  8.         const char *src_prop;  
  9.         const char *dest_prop;  
  10.         const char *def_val;  
  11.     } prop_map[] = {  
  12.         { "ro.boot.serialno""ro.serialno""", },  
  13.         { "ro.boot.mode""ro.bootmode""unknown", },  
  14.         { "ro.boot.baseband""ro.baseband""unknown", },  
  15.         { "ro.boot.bootloader""ro.bootloader""unknown", },  
  16.     };  
  17.     //循环读取ro.boot.xxx属性值,并设置ro.xxx属性  
  18.     for (i = 0; i < ARRAY_SIZE(prop_map); i++) {  
  19.         pval = property_get(prop_map[i].src_prop);  
  20.         property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);  
  21.     }  
  22.     //读取ro.boot.console属性值  
  23.     pval = property_get("ro.boot.console");  
  24.     if (pval)  
  25.         strlcpy(console, pval, sizeof(console));  
  26.     //读取ro.bootmode属性值  
  27.     strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));  
  28.     //读取ro.boot.hardware属性值  
  29.     pval = property_get("ro.boot.hardware");  
  30.     if (pval)  
  31.         strlcpy(hardware, pval, sizeof(hardware));  
  32.     //设置ro.hardware属性  
  33.     property_set("ro.hardware", hardware);  
  34.     //设置ro.revision属性  
  35.     snprintf(tmp, PROP_VALUE_MAX, "%d", revision);  
  36.     property_set("ro.revision", tmp);  
  37.     //设置ro.factorytest属性  
  38.     if (!strcmp(bootmode,"factory"))  
  39.         property_set("ro.factorytest""1");  
  40.     else if (!strcmp(bootmode,"factory2"))  
  41.         property_set("ro.factorytest""2");  
  42.     else  
  43.         property_set("ro.factorytest""0");  
  44. }  

init.rc 文件解析

[cpp] view plain copy
  1. init_parse_config_file(const char *fn)  
  2. {  
  3.     char *data;  
  4.     //读取/init.rc文件内容  
  5.     data = read_file(fn, 0);  
  6.     if (!data) return -1;  
  7.     //解析读取到的文件内容  
  8.     parse_config(fn, data);  
  9.     DUMP();  
  10.     return 0;  
  11. }  
函数首先调用read_file函数将init.rc文件的内容读取保存到data中,在调用parse_config对其进行解析
[cpp] view plain copy
  1. void *read_file(const char *fn, unsigned *_sz)  
  2. {  
  3.     char *data;  
  4.     int sz;  
  5.     int fd;  
  6.     struct stat sb;  
  7.     data = 0;  
  8.     //打开/init.rc文件  
  9.     fd = open(fn, O_RDONLY);  
  10.     if(fd < 0) return 0;  
  11.   
  12.     // for security reasons, disallow world-writable  
  13.     // or group-writable files  
  14.     if (fstat(fd, &sb) < 0) {  
  15.         ERROR("fstat failed for '%s'\n", fn);  
  16.         goto oops;  
  17.     }  
  18.     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {  
  19.         ERROR("skipping insecure file '%s'\n", fn);  
  20.         goto oops;  
  21.     }  
  22.     //将文件指针移到文件尾部,得到文件内容长度  
  23.     sz = lseek(fd, 0, SEEK_END);  
  24.     if(sz < 0) goto oops;  
  25.   
  26.     if(lseek(fd, 0, SEEK_SET) != 0) goto oops;  
  27.     //分配buffer  
  28.     data = (char*) malloc(sz + 2);  
  29.     if(data == 0) goto oops;  
  30.     //读取文件  
  31.     if(read(fd, data, sz) != sz) goto oops;  
  32.     close(fd);  
  33.     data[sz] = '\n';  
  34.     data[sz+1] = 0;  
  35.     if(_sz) *_sz = sz;  
  36.     return data;  
  37. oops:  
  38.     close(fd);  
  39.     if(data != 0) free(data);  
  40.     return 0;  
  41. }  

init.rc文件语法介绍

在Android根文件系统下存在多个.rc文件,该文件为Android启动配置脚本文件,文件内容如下:

[plain] view plain copy
  1. # Copyright (C) 2012 The Android Open Source Project  
  2. #  
  3. # IMPORTANT: Do not create world writable files or directories.  
  4. # This is a common source of Android security bugs.  
  5. #  
  6.   
  7. import /init.${ro.hardware}.rc  
  8. import /init.usb.rc  
  9. import /init.trace.rc  
  10.   
  11. on early-init  
  12.     # Set init and its forked children's oom_adj.  
  13.     write /proc/1/oom_adj -16  
  14.     start ueventd  
  15.     mkdir /mnt 0775 root system  
  16.   
  17. on init  
  18.     sysclktz 0  
  19.     loglevel 3  
  20. # setup the global environment  
  21.     export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin  
  22.     export LD_LIBRARY_PATH /vendor/lib:/system/lib  
  23.     export ANDROID_BOOTLOGO 1  
  24.     export ANDROID_ROOT /system  
  25.     export ANDROID_ASSETS /system/app  
  26.     export ANDROID_DATA /data  
  27.     export ASEC_MOUNTPOINT /mnt/asec  
  28.     export LOOP_MOUNTPOINT /mnt/obb  
  29.     export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar  
  30.   
  31. # Backward compatibility  
  32.     symlink /system/etc /etc  
  33.     symlink /sys/kernel/debug /d  
  34.   
  35. # Right now vendor lives on the same filesystem as system,  
  36. # but someday that may change.  
  37.     symlink /system/vendor /vendor  
  38.   
  39. # Create cgroup mount point for cpu accounting  
  40.     mkdir /acct  
  41.     mount cgroup none /acct cpuacct  
  42.     mkdir /acct/uid  
  43.   
  44.     mkdir /system  
  45.     mkdir /data 0771 system system  
  46.     mkdir /cache 0770 system cache  
  47.     mkdir /runtimenv 0774 system system  
  48.     mkdir /backupfixnv 0774 system system  
  49.     mkdir /productinfo 0774 system system  
  50.     mkdir /fixnv 0774 system system  
  51.     mkdir /config 0500 root root  
  52.   
  53. # Create cgroup mount points for process groups  
  54.     mkdir /dev/cpuctl  
  55.     mount cgroup none /dev/cpuctl cpu  
  56.     chown system system /dev/cpuctl  
  57.     chown system system /dev/cpuctl/tasks  
  58.     chmod 0660 /dev/cpuctl/tasks  
  59.     write /dev/cpuctl/cpu.shares 1024  
  60.     write /dev/cpuctl/cpu.rt_runtime_us 950000  
  61.     write /dev/cpuctl/cpu.rt_period_us 1000000  
  62.   
  63.     mkdir /dev/cpuctl/apps  
  64.     chown system system /dev/cpuctl/apps/tasks  
  65.     chmod 0666 /dev/cpuctl/apps/tasks  
  66.     write /dev/cpuctl/apps/cpu.shares 1024  
  67.     write /dev/cpuctl/apps/cpu.rt_runtime_us 800000  
  68.     write /dev/cpuctl/apps/cpu.rt_period_us 1000000  
  69.   
  70. on fs  
  71. # mount mtd partitions  
  72.     # Mount /system rw first to give the filesystem a chance to save a checkpoint  
  73.   
  74.     chmod 0744 /modem_control  
  75.     start modem_control  
  76.   
  77.     mount yaffs2 mtd@system /system  
  78.     mount yaffs2 mtd@system /system ro remount  
  79.     mount yaffs2 mtd@userdata /data nosuid nodev  
  80.     mount yaffs2 mtd@cache /cache nosuid nodev  
  81.   
  82. on post-fs  
  83.     # once everything is setup, no need to modify /  
  84.     mount rootfs rootfs / ro remount  
  85.   
  86.     mount yaffs2 mtd@fixnv /fixnv nosuid nodev no-checkpoint  
  87.     chown system system /fixnv  
  88.     chmod 0774 /fixnv  
  89.   
  90.     mount yaffs2 mtd@runtimenv /runtimenv nosuid nodev no-checkpoint  
  91.     chown system system /runtimenv  
  92.     chmod 0774 /runtimenv  
  93.   
  94.     # We chown/chmod /cache again so because mount is run as root + defaults  
  95.     chown system cache /cache  
  96.     chmod 0770 /cache  
  97.   
  98.     mount yaffs2 mtd@backupfixnv /backupfixnv nosuid nodev no-checkpoint  
  99.     chown system system /backupfixnv  
  100.     chmod 0774 /backupfixnv  
  101.   
  102.     mount yaffs2 mtd@productinfo /productinfo nosuid nodev no-checkpoint  
  103.     chown system system /productinfo  
  104.     chmod 0774 /productinfo  
  105.   
  106.     chmod 0660 /fixnv/fixnv.bin  
  107.     chmod 0660 /backupfixnv/fixnv.bin  
  108.     chmod 0660 /productinfo/productinfo.bin  
  109.     chmod 0660 /productinfo/productinfobkup.bin  
  110.     chown system system /fixnv/fixnv.bin  
  111.     chown system system /backupfixnv/fixnv.bin  
  112.     chown system system /productinfo/productinfo.bin  
  113.     chown system system /productinfo/productinfobkup.bin  
  114.   
  115.     # This may have been created by the recovery system with odd permissions  
  116.     chown system cache /cache/recovery  
  117.     chmod 0770 /cache/recovery  
  118.   
  119.     #change permissions on vmallocinfo so we can grab it from bugreports  
  120.     chown root log /proc/vmallocinfo  
  121.     chmod 0440 /proc/vmallocinfo  
  122.   
  123.     #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks  
  124.     chown root system /proc/kmsg  
  125.     chmod 0440 /proc/kmsg  
  126.     chown root system /proc/sysrq-trigger  
  127.     chmod 0220 /proc/sysrq-trigger  
  128.   
  129.     # create the lost+found directories, so as to enforce our permissions  
  130.     mkdir /cache/lost+found 0770 root root  
  131.   
  132. on post-fs-data  
  133.     # create basic filesystem structure  
  134.     mkdir /data/misc 01771 system misc  
  135.     mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth  
  136.     mkdir /data/misc/bluetooth 0770 system system  
  137.     mkdir /data/misc/keystore 0700 keystore keystore  
  138.     mkdir /data/misc/keychain 0771 system system  
  139.     mkdir /data/misc/vpn 0770 system vpn  
  140.     mkdir /data/misc/systemkeys 0700 system system  
  141.   
  142. on boot  
  143. # basic network init  
  144.     ifup lo  
  145.     hostname localhost  
  146.     domainname localdomain  
  147.   
  148. # set RLIMIT_NICE to allow priorities from 19 to -20  
  149.     setrlimit 13 40 40  
  150.   
  151. # Memory management.  Basic kernel parameters, and allow the high  
  152. # level system server to be able to adjust the kernel OOM driver  
  153. # parameters to match how it is managing things.  
  154.     write /proc/sys/vm/overcommit_memory 1  
  155.     write /proc/sys/vm/min_free_order_shift 4  
  156.     chown root system /sys/module/lowmemorykiller/parameters/adj  
  157.   
  158.     # Tweak background writeout  
  159.     write /proc/sys/vm/dirty_expire_centisecs 200  
  160.     write /proc/sys/vm/dirty_background_ratio  5  
  161.   
  162.     class_start core  
  163.     class_start main  
  164.   
  165. on nonencrypted  
  166.     class_start late_start  
  167.   
  168. on charger  
  169.     class_start core  
  170.     class_start charger  
  171.   
  172. on alarm  
  173.     insmod /system/lib/modules/ft5306_ts.ko  
  174.     class_start core  
  175.     start media  
  176.     exec /bin/poweroff_alarm  
  177.   
  178. on property:vold.decrypt=trigger_reset_main  
  179.     class_reset main  
  180.   
  181. on property:vold.decrypt=trigger_load_persist_props  
  182.     load_persist_props  
  183.   
  184. on property:vold.decrypt=trigger_post_fs_data  
  185.     trigger post-fs-data  
  186.   
  187. on property:vold.decrypt=trigger_restart_min_framework  
  188.     class_start main  
  189.   
  190. on property:vold.decrypt=trigger_restart_framework  
  191.     class_start main  
  192.     class_start late_start  
  193.   
  194. on property:vold.decrypt=trigger_shutdown_framework  
  195.     class_reset late_start  
  196.     class_reset main  
  197.   
  198. ## Daemon processes to be run by init.  
  199. ##  
  200. service ueventd /sbin/ueventd  
  201.     class core  
  202.     critical  
  203.   
  204. service console /system/bin/sh  
  205.     class core  
  206.     console  
  207.     disabled  
  208.     user shell  
  209.     group log  
  210.   
  211. on property:ro.debuggable=1  
  212.     start console  
  213.   
  214. # adbd is controlled via property triggers in init.<platform>.usb.rc  
  215. service adbd /sbin/adbd  
  216.     class core  
  217.     disabled  
  218.   
  219. # adbd on at boot in emulator  
  220. on property:ro.kernel.qemu=1  
  221.     start adbd  
  222.   
  223. # This property trigger has added to imitiate the previous behavior of "adb root".  
  224. # The adb gadget driver used to reset the USB bus when the adbd daemon exited,  
  225. # and the host side adb relied on this behavior to force it to reconnect with the  
  226. # new adbd instance after init relaunches it. So now we force the USB bus to reset  
  227. # here when adbd sets the service.adb.root property to 1.  We also restart adbd here  
  228. # rather than waiting for init to notice its death and restarting it so the timing  
  229. # of USB resetting and adb restarting more closely matches the previous behavior.  
  230. on property:service.adb.root=1  
  231.     write /sys/class/android_usb/android0/enable 0  
  232.     restart adbd  
  233.     write /sys/class/android_usb/android0/enable 1  
  234.   
  235. service servicemanager /system/bin/servicemanager  
  236.     class core  
  237.     user system  
  238.     group system  
  239.     critical  
  240.     onrestart restart zygote  
  241.     onrestart restart media  
  242.     onrestart restart surfaceflinger  
  243.     onrestart restart drm  
  244.   
  245. service vold /system/bin/vold  
  246.     class core  
  247.     socket vold stream 0660 root mount  
  248.     ioprio be 2  
  249.   
  250. service netd /system/bin/netd  
  251.     class main  
  252.     socket netd stream 0660 root system  
  253.     socket dnsproxyd stream 0660 root inet  
  254.     socket mdns stream 0660 root system  
  255.   
  256. service debuggerd /system/bin/debuggerd  
  257.     class main  
  258.   
  259. #service ril-daemon /system/bin/rild  
  260. #    class main  
  261. #    socket rild stream 660 root radio  
  262. #    socket rild-debug stream 660 radio system  
  263. #    user root  
  264. #    group radio cache inet misc audio sdcard_r sdcard_rw log  
  265.   
  266. service surfaceflinger /system/bin/surfaceflinger  
  267.     class main  
  268.     user system  
  269.     group graphics  
  270.     onrestart restart zygote  
  271.   
  272. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
  273.     class main  
  274.     socket zygote stream 660 root system  
  275.     onrestart write /sys/android_power/request_state wake  
  276.     onrestart write /sys/power/state on  
  277.     onrestart restart media  
  278.     onrestart restart netd  
  279.   
  280. service bootanim /system/bin/bootanimation  
  281.     class main  
  282.     user graphics  
  283.     group graphics  
  284.     disabled  
  285.     oneshot  
  286.   
  287. service dbus /system/bin/dbus-daemon --system --nofork  
  288.     class main  
  289.     socket dbus stream 660 bluetooth bluetooth  
  290.     user bluetooth  
  291.     group bluetooth net_bt_admin  
  292.   
  293. service bluetoothd /system/bin/bluetoothd -n  
  294.     class main  
  295.     socket bluetooth stream 660 bluetooth bluetooth  
  296.     socket dbus_bluetooth stream 660 bluetooth bluetooth  
  297.     # init.rc does not yet support applying capabilities, so run as root and  
  298.     # let bluetoothd drop uid to bluetooth with the right linux capabilities  
  299.     group bluetooth net_bt_admin misc  
  300.     disabled  
  301.   
  302. service installd /system/bin/installd  
  303.     class main  
  304.     socket installd stream 600 system system  
  305.   
  306. service flash_recovery /system/etc/install-recovery.sh  
  307.     class main  
  308.     oneshot  
  309.   
  310. service racoon /system/bin/racoon  
  311.     class main  
  312.     socket racoon stream 600 system system  
  313.     # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.  
  314.     group vpn net_admin inet  
  315.     disabled  
  316.     oneshot  
  317.   
  318. service mtpd /system/bin/mtpd  
  319.     class main  
  320.     socket mtpd stream 600 system system  
  321.     user vpn  
  322.     group vpn net_admin inet net_raw  
  323.     disabled  
  324.     oneshot  
  325.   
  326. service keystore /system/bin/keystore /data/misc/keystore  
  327.     class main  
  328.     user keystore  
  329.     group keystore drmrpc  
  330.     socket keystore stream 666  
init.rc是一个可配置的初始化文件,通常定制厂商可以配置额外的初始化配置,如果关键字中有空格,处理方法类似于C语言,使用/表示转义,使用“”防止关键字被断开,另外注意/在末尾表示换行,由 # (前面允许有空格)开始的行都是注释行。init.rc包含4种状态类别:Actions/Commands/Services/Options。当声明一个service或者action的时候,它将隐式声明一个section,它之后跟随的command或者option都将属于这个section,action和service不能重名,否则忽略为error。

Action

actions就是在某种条件下触发一系列的命令,通常有一个trigger,形式如:  

on <trigger>
      <command>
      <command>

trigger主要包括:

boot 当/init.conf加载完毕时
<name>=<value> 当<name>被设置为<value>时
device-added-<path> 设备<path>被添加时
device-removed-<path> 设备<path>被移除时
service-exited-<name> 服务<name>退出时

Service

service就是要启动的本地服务进程

service <name> <pathname> [ <argument> ]*
      <option>
     <option>

Option

option是service的修饰词,由它来指定何时并且如何启动Services程序,主要包括:
     critical  表示如果服务在4分钟内存在多于4次,则系统重启到recovery mode
     disabled   表示服务不会自动启动,需要手动调用名字启动
     setEnv <name> <value>  设置启动环境变量
     socket <name> <type> <permission> [<user> [<group>]] 开启一个unix域的socket,名字为/dev/socket/<name> , <type>只能是dgram或者stream,<user>和<group>默认为0
     user <username> 表示将用户切换为<username>,用户名已经定义好了,只能是system/root
     group <groupname>  表示将组切换为<groupname>
     oneshot 表示这个service只启动一次
     class <name> 指定一个要启动的类,这个类中如果有多个service,将会被同时启动。默认的class将会是“default”
     onrestart  在重启时执行一条命令

Command

comand主要包括:

 exec <path> [ <argument> ]*执行一个<path>指定的程序
 export <name> <value> 设置一个全局变量
 ifup <interface> 使网络接口<interface>连接
 import <filename> 引入其他的配置文件
 hostname <name> 设置主机名
 chdir <directory> 切换工作目录
 chmod <octal-mode> <path> 设置访问权限
 chown <owner> <group> <path> 设置用户和组
 chroot <directory> 设置根目录
 class_start <serviceclass> 启动类中的service
 class_stop <serviceclass> 停止类中的service
 domainname <name> 设置域名
 insmod <path> 安装模块
 mkdir <path> [mode] [owner] [group] 创建一个目录,并可以指定权限,用户和组
 mount <type> <device> <dir> [ <mountoption> ]* 加载指定设备到目录下<mountoption> 包括"ro", "rw", "remount", "noatime"
 setprop <name> <value> 设置系统属性
 setrlimit <resource> <cur> <max> 设置资源访问权限
 start <service> 开启服务
 stop <service> 停止服务
 symlink <target> <path> 创建一个动态链接
 sysclktz <mins_west_of_gmt> 设置系统时钟
 trigger <event> 触发事件
 write <path> <string> [ <string> ]* 向<path>路径的文件写入多个<string>

Properties(属性)

Init更新一些系统属性以提供对正在发生的事件的监控能力:
       init.action 此属性值为正在被执行的action的名字,如果没有则为""。
       init.command  此属性值为正在被执行的command的名字,如果没有则为""。
       init.svc.<name> 名为<name>的service的状态("stopped"(停止), "running"(运行), "restarting"(重启))


在默认情况下,程序在被init执行时会将标准输出和标准错误都重定向到/dev/null(丢弃)。若你想要获得调试信息,你可以通过Andoird系统中的logwrapper程序执行你的程序。它会将标准输出/标准错误都重定向到Android日志系统(通过logcat访问)。
例如:
    service akmd /system/bin/logwrapper /sbin/akmd

init.rc解析过程

1. 扫描init.rc中的token
    找到其中的 文件结束EOF/文本TEXT/新行NEWLINE,其中的空格‘ ’、‘\t’、‘\r’会被忽略,#开头的行也被忽略掉;而对于TEXT,空格‘ ’、‘\t’、‘\r’、‘\n’都是TEXT的结束标志。
2. 对每一个TEXT token,都加入到args[]数组中
3. 当遇到新一行(‘\n’)的时候,用args[0]通过lookup_keyword()检索匹配关键字;

   1) 对Section(on和service),调用parse_new_section() 解析:
     - 对on section,调用parse_action(),并设置解析函数parse_line为parse_line_action()
     - 对service section,调用parse_service(),并设置解析函数parse_line为parse_line_service()
   2) 对其他关键字的行(非on或service开头的地方,也就是没有切换section)调用parse_line()
     - 对于on section内的命令行,调用parse_line_action()解析;
     - 对于service section内的命令行,调用parse_line_service()解析。

Token的定义

[cpp] view plain copy
  1. #define T_EOF 0  
  2. #define T_TEXT 1  
  3. #define T_NEWLINE 2  
 解析过程中的双向循环链表的使用,android用到了一个非常巧妙的链表实现方法,一般情况下如果链表的节点是一个单独的数据结构的话,那么针对不同的数据结构,都需要定义不同链表操作。而在初始化过程中使用到的链表则解决了这个问题,它将链表的节点定义为了一个非常精简的结构,只包含前向和后向指针,那么在定义不同的数据结构时,只需要将链表节点嵌入到数据结构中即可。链表节点定义如下:

[cpp] view plain copy
  1. struct listnode  
  2. {  
  3.     struct listnode *next;  
  4.     struct listnode *prev;  
  5. };  
对于Action数据结构为例:
[cpp] view plain copy
  1. struct action {  
  2.     /* node in list of all actions */  
  3.     struct listnode alist;  
  4.     /* node in the queue of pending actions */  
  5.     struct listnode qlist;  
  6.     /* node in list of actions for a trigger */  
  7.     struct listnode tlist;  
  8.   
  9.     unsigned hash;  
  10.     const char *name;  
  11.      
  12.     struct listnode commands;  
  13.     struct command *current;  
  14. };  
这样的话,所有的链表的基本操作,例如插入,删除等只会针对listnode进行操作,而不是针对特定的数据结构,链表的实现得到了统一,即精简了代码,又提高了效率。 但是这样的链表实现,存在一个问题,链表节点listnode中只有前向和后向指针,并且前向和后向指针均指向listnode,那么我们通过什么方式来访问数据结构action的内容呢?我们使用offsetof宏来计算链表节点在数据结构中的偏移量,从而计算数据结构实例的地址。

[cpp] view plain copy
  1. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  

[cpp] view plain copy
  1. #define node_to_item(node, container, member) \  
  2.     (container *) (((char*) (node)) - offsetof(container, member))  
这种链表的优点:(1)所有链表基本操作都是基于listnode指针的,因此添加类型时,不需要重复写链表基本操作函数(2)一个container数据结构可以含有多个listnode成员,这样就可以同时挂到多个不同的链表中。

Service数据结构定义:

[cpp] view plain copy
  1. struct service {  
  2.         /* list of all services */  
  3.     struct listnode slist;  
  4.     const char *name;  
  5.     const char *classname;  
  6.     unsigned flags;  
  7.     pid_t pid;  
  8.     time_t time_started;    /* time of last start */  
  9.     time_t time_crashed;    /* first crash within inspection window */  
  10.     int nr_crashed;         /* number of times crashed within window */  
  11.       
  12.     uid_t uid;  
  13.     gid_t gid;  
  14.     gid_t supp_gids[NR_SVC_SUPP_GIDS];  
  15.     size_t nr_supp_gids;  
  16.   
  17. #ifdef HAVE_SELINUX  
  18.     char *seclabel;  
  19. #endif  
  20.     struct socketinfo *sockets;  
  21.     struct svcenvinfo *envvars;  
  22.     struct action onrestart;  /* Actions to execute on restart. */    
  23.     /* keycodes for triggering this service via /dev/keychord */  
  24.     int *keycodes;  
  25.     int nkeycodes;  
  26.     int keychord_id;  
  27.     int ioprio_class;  
  28.     int ioprio_pri;  
  29.     int nargs;  
  30.     /* "MUST BE AT THE END OF THE STRUCT" */  
  31.     char *args[1];  
  32. };  
对于某些Service可能采用Socket来实现进程间通信,因此该Service需要创建多个socket,比如:

[cpp] view plain copy
  1. service wril-daemon /system/bin/rild_sp -l /system/lib/libreference-ril_sp.so -m w -n 0  
  2.     class core  
  3.     socket rild stream 660 root radio  
  4.     socket rild-debug stream 660 radio system  
  5.     disabled  
  6.     user root  
  7.     group radio cache inet misc audio sdcard_rw log  
该service需要创建rild 和rild-debug socket,这些socket的信息在解析init.rc文件时保存在Service的成员变量sockets链表中。socketinfo 数据结构定义如下:

[cpp] view plain copy
  1. struct socketinfo {  
  2.     struct socketinfo *next;  
  3.     const char *name;  
  4.     const char *type;  
  5.     uid_t uid;  
  6.     gid_t gid;  
  7.     int perm;  
  8. };  

某些Service在运行时需要设置环境变量,这些环境变量被保存在Service的成员变量envvars链表中,svcenvinfo 数据结构定义如下:

[cpp] view plain copy
  1. struct svcenvinfo {  
  2.     struct svcenvinfo *next;  
  3.     const char *name;  
  4.     const char *value;  
  5. };  
在每个Action或Service下可能需要执行多个Command,关于command数据结构定义如下:

[cpp] view plain copy
  1. struct command  
  2. {  
  3.         /* list of commands in an action */  
  4.     struct listnode clist;  
  5.   
  6.     int (*func)(int nargs, char **args);  
  7.     int nargs;  
  8.     char *args[1];  
  9. };  

在Init进程中分别使用了3个链表来存储init.rc文件中的Action和Service:

[cpp] view plain copy
  1. static list_declare(service_list);  
  2. static list_declare(action_list);  
  3. static list_declare(action_queue);  
service_list链表用于保存init.rc文件中的Service配置信息,service_list链表的存储如下图所示:

service_list 链表保存init.rc文件中的所有service,每个service下的所有socket信息保存在该service的成员变量sockets链表中,当该service重启时,需要重启某些服务,对于重启某些服务的命令以Action的形式保存在Service的成员变量onrestart链表中,而真正执行的命令却存放在该Action下的commands链表里。

action_list用于保存init.rc文件中的所有以on开头的section,action_list链表的存储如下图所示:

从上图可以看出action_queue和action_list都是用来保存所有的Action,它们之间的区别是action_list用于保存从init.rc中解析出来的所有Action,而action_queue却是用于保存待执行的Action,action_queue是一个待执行队列。

在system\core\init\keywords.h文件中定义了解析关键字,其内容如下:

[cpp] view plain copy
  1. #ifndef KEYWORD  
  2. int do_chroot(int nargs, char **args);  
  3. int do_chdir(int nargs, char **args);  
  4. int do_class_start(int nargs, char **args);  
  5. int do_class_stop(int nargs, char **args);  
  6. int do_class_reset(int nargs, char **args);  
  7. int do_domainname(int nargs, char **args);  
  8. int do_exec(int nargs, char **args);  
  9. int do_export(int nargs, char **args);  
  10. int do_hostname(int nargs, char **args);  
  11. int do_ifup(int nargs, char **args);  
  12. int do_insmod(int nargs, char **args);  
  13. int do_mkdir(int nargs, char **args);  
  14. int do_mount_all(int nargs, char **args);  
  15. int do_mount(int nargs, char **args);  
  16. int do_restart(int nargs, char **args);  
  17. int do_restorecon(int nargs, char **args);  
  18. int do_rm(int nargs, char **args);  
  19. int do_rmdir(int nargs, char **args);  
  20. int do_setcon(int nargs, char **args);  
  21. int do_setenforce(int nargs, char **args);  
  22. int do_setkey(int nargs, char **args);  
  23. int do_setprop(int nargs, char **args);  
  24. int do_setrlimit(int nargs, char **args);  
  25. int do_setsebool(int nargs, char **args);  
  26. int do_start(int nargs, char **args);  
  27. int do_stop(int nargs, char **args);  
  28. int do_trigger(int nargs, char **args);  
  29. int do_symlink(int nargs, char **args);  
  30. int do_sysclktz(int nargs, char **args);  
  31. int do_write(int nargs, char **args);  
  32. int do_copy(int nargs, char **args);  
  33. int do_chown(int nargs, char **args);  
  34. int do_chmod(int nargs, char **args);  
  35. int do_loglevel(int nargs, char **args);  
  36. int do_load_persist_props(int nargs, char **args);  
  37. int do_pipe(int nargs, char **args);  
  38. int do_wait(int nargs, char **args);  
  39. #define __MAKE_KEYWORD_ENUM__  
  40. #define KEYWORD(symbol, flags, nargs, func) K_##symbol,  
  41. enum {  
  42.     K_UNKNOWN,  
  43. #endif  
  44.     KEYWORD(capability,  OPTION,  0, 0)  
  45.     KEYWORD(chdir,       COMMAND, 1, do_chdir)  
  46.     KEYWORD(chroot,      COMMAND, 1, do_chroot)  
  47.     KEYWORD(class,       OPTION,  0, 0)  
  48.     KEYWORD(class_start, COMMAND, 1, do_class_start)  
  49.     KEYWORD(class_stop,  COMMAND, 1, do_class_stop)  
  50.     KEYWORD(class_reset, COMMAND, 1, do_class_reset)  
  51.     KEYWORD(console,     OPTION,  0, 0)  
  52.     KEYWORD(critical,    OPTION,  0, 0)  
  53.     KEYWORD(disabled,    OPTION,  0, 0)  
  54.     KEYWORD(domainname,  COMMAND, 1, do_domainname)  
  55.     KEYWORD(exec,        COMMAND, 1, do_exec)  
  56.     KEYWORD(export,      COMMAND, 2, do_export)  
  57.     KEYWORD(group,       OPTION,  0, 0)  
  58.     KEYWORD(hostname,    COMMAND, 1, do_hostname)  
  59.     KEYWORD(ifup,        COMMAND, 1, do_ifup)  
  60.     KEYWORD(insmod,      COMMAND, 1, do_insmod)  
  61.     KEYWORD(import,      SECTION, 1, 0)  
  62.     KEYWORD(keycodes,    OPTION,  0, 0)  
  63.     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)  
  64.     KEYWORD(mount_all,   COMMAND, 1, do_mount_all)  
  65.     KEYWORD(mount,       COMMAND, 3, do_mount)  
  66.     KEYWORD(on,          SECTION, 0, 0)  
  67.     KEYWORD(oneshot,     OPTION,  0, 0)  
  68.     KEYWORD(onrestart,   OPTION,  0, 0)  
  69.     KEYWORD(restart,     COMMAND, 1, do_restart)  
  70.     KEYWORD(restorecon,  COMMAND, 1, do_restorecon)  
  71.     KEYWORD(rm,          COMMAND, 1, do_rm)  
  72.     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)  
  73.     KEYWORD(seclabel,    OPTION,  0, 0)  
  74.     KEYWORD(service,     SECTION, 0, 0)  
  75.     KEYWORD(setcon,      COMMAND, 1, do_setcon)  
  76.     KEYWORD(setenforce,  COMMAND, 1, do_setenforce)  
  77.     KEYWORD(setenv,      OPTION,  2, 0)  
  78.     KEYWORD(setkey,      COMMAND, 0, do_setkey)  
  79.     KEYWORD(setprop,     COMMAND, 2, do_setprop)  
  80.     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)  
  81.     KEYWORD(setsebool,   COMMAND, 1, do_setsebool)  
  82.     KEYWORD(socket,      OPTION,  0, 0)  
  83.     KEYWORD(start,       COMMAND, 1, do_start)  
  84.     KEYWORD(stop,        COMMAND, 1, do_stop)  
  85.     KEYWORD(trigger,     COMMAND, 1, do_trigger)  
  86.     KEYWORD(symlink,     COMMAND, 1, do_symlink)  
  87.     KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)  
  88.     KEYWORD(user,        OPTION,  0, 0)  
  89.     KEYWORD(wait,        COMMAND, 1, do_wait)  
  90.     KEYWORD(write,       COMMAND, 2, do_write)  
  91.     KEYWORD(copy,        COMMAND, 2, do_copy)  
  92.     KEYWORD(chown,       COMMAND, 2, do_chown)  
  93.     KEYWORD(chmod,       COMMAND, 2, do_chmod)  
  94.     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)  
  95.     KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)  
  96.     KEYWORD(pipe,        COMMAND, 2, do_pipe)  
  97.     KEYWORD(ioprio,      OPTION,  0, 0)  
  98. #ifdef __MAKE_KEYWORD_ENUM__  
  99.     KEYWORD_COUNT,  
  100. };  
  101. #undef __MAKE_KEYWORD_ENUM__  
  102. #undef KEYWORD  
  103. #endif  
宏KEYWORD并未定义,因此将定义宏__MAKE_KEYWORD_ENUM__ 及KEYWORD,KEYWORD宏定义如下:

[cpp] view plain copy
  1. #define KEYWORD(symbol, flags, nargs, func) K_##symbol,  
同时定义了枚举:

[cpp] view plain copy
  1. enum {  
  2.     K_UNKNOWN,  
  3.     KEYWORD(capability,  OPTION,  0, 0)  
  4.     KEYWORD(chdir,       COMMAND, 1, do_chdir)  
  5.     KEYWORD(chroot,      COMMAND, 1, do_chroot)  
  6.     KEYWORD(class,       OPTION,  0, 0)  
  7.     KEYWORD(class_start, COMMAND, 1, do_class_start)  
  8.     KEYWORD(class_stop,  COMMAND, 1, do_class_stop)  
  9.     KEYWORD(class_reset, COMMAND, 1, do_class_reset)  
  10.     KEYWORD(console,     OPTION,  0, 0)  
  11.     KEYWORD(critical,    OPTION,  0, 0)  
  12.     KEYWORD(disabled,    OPTION,  0, 0)  
  13.     KEYWORD(domainname,  COMMAND, 1, do_domainname)  
  14.     KEYWORD(exec,        COMMAND, 1, do_exec)  
  15.     KEYWORD(export,      COMMAND, 2, do_export)  
  16.     KEYWORD(group,       OPTION,  0, 0)  
  17.     KEYWORD(hostname,    COMMAND, 1, do_hostname)  
  18.     KEYWORD(ifup,        COMMAND, 1, do_ifup)  
  19.     KEYWORD(insmod,      COMMAND, 1, do_insmod)  
  20.     KEYWORD(import,      SECTION, 1, 0)  
  21.     KEYWORD(keycodes,    OPTION,  0, 0)  
  22.     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)  
  23.     KEYWORD(mount_all,   COMMAND, 1, do_mount_all)  
  24.     KEYWORD(mount,       COMMAND, 3, do_mount)  
  25.     KEYWORD(on,          SECTION, 0, 0)  
  26.     KEYWORD(oneshot,     OPTION,  0, 0)  
  27.     KEYWORD(onrestart,   OPTION,  0, 0)  
  28.     KEYWORD(restart,     COMMAND, 1, do_restart)  
  29.     KEYWORD(restorecon,  COMMAND, 1, do_restorecon)  
  30.     KEYWORD(rm,          COMMAND, 1, do_rm)  
  31.     KEYWORD(rmdir,       COMMAND, 1, do_rmdir)  
  32.     KEYWORD(seclabel,    OPTION,  0, 0)  
  33.     KEYWORD(service,     SECTION, 0, 0)  
  34.     KEYWORD(setcon,      COMMAND, 1, do_setcon)  
  35.     KEYWORD(setenforce,  COMMAND, 1, do_setenforce)  
  36.     KEYWORD(setenv,      OPTION,  2, 0)  
  37.     KEYWORD(setkey,      COMMAND, 0, do_setkey)  
  38.     KEYWORD(setprop,     COMMAND, 2, do_setprop)  
  39.     KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)  
  40.     KEYWORD(setsebool,   COMMAND, 1, do_setsebool)  
  41.     KEYWORD(socket,      OPTION,  0, 0)  
  42.     KEYWORD(start,       COMMAND, 1, do_start)  
  43.     KEYWORD(stop,        COMMAND, 1, do_stop)  
  44.     KEYWORD(trigger,     COMMAND, 1, do_trigger)  
  45.     KEYWORD(symlink,     COMMAND, 1, do_symlink)  
  46.     KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)  
  47.     KEYWORD(user,        OPTION,  0, 0)  
  48.     KEYWORD(wait,        COMMAND, 1, do_wait)  
  49.     KEYWORD(write,       COMMAND, 2, do_write)  
  50.     KEYWORD(copy,        COMMAND, 2, do_copy)  
  51.     KEYWORD(chown,       COMMAND, 2, do_chown)  
  52.     KEYWORD(chmod,       COMMAND, 2, do_chmod)  
  53.     KEYWORD(loglevel,    COMMAND, 1, do_loglevel)  
  54.     KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)  
  55.     KEYWORD(pipe,        COMMAND, 2, do_pipe)  
  56.     KEYWORD(ioprio,      OPTION,  0, 0)  
  57.     KEYWORD_COUNT,  
  58. };  
该枚举的通过宏展开后定义为:

[cpp] view plain copy
  1. enum {  
  2.     K_UNKNOWN,  
  3.     K_capability,  
  4.     K_chdir,  
  5.     K_chroot,  
  6.     K_class,  
  7.     K_class_start,  
  8.     K_class_stop,  
  9.     K_class_reset,  
  10.     K_console,  
  11.     K_critical,  
  12.     K_disabled,  
  13.     K_domainname,  
  14.     K_exec,  
  15.     K_export,  
  16.     K_group,  
  17.     K_hostname,  
  18.     K_ifup,  
  19.     K_insmod,  
  20.     K_import,  
  21.     K_keycodes,  
  22.     K_mkdir,  
  23.     K_mount_all,  
  24.     K_mount,  
  25.     K_on,  
  26.     K_oneshot,  
  27.     K_onrestart,  
  28.     K_restart,  
  29.     K_restorecon,  
  30.     K_rm,  
  31.     K_rmdir  
  32.     K_seclabel  
  33.     K_service  
  34.     K_setcon  
  35.     K_setenforce  
  36.     K_setenv  
  37.     K_setkey  
  38.     K_setprop  
  39.     K_setrlimit  
  40.     K_setsebool  
  41.     K_socket  
  42.     K_start  
  43.     K_stop  
  44.     K_trigger  
  45.     K_symlink  
  46.     K_sysclktz  
  47.     K_user  
  48.     K_wait  
  49.     K_write  
  50.     K_copy  
  51.     K_chown  
  52.     K_chmod  
  53.     K_loglevel  
  54.     K_load_persist_props  
  55.     K_pipe  
  56.     K_ioprio  
  57.     KEYWORD_COUNT,  
  58. };  
该枚举的定义主要是为每个命令指定对应的序号。在keywords.h文件最后取消了宏__MAKE_KEYWORD_ENUM__ 及KEYWORD的定义,在system\core\init\init_parser.c文件中又重定义了KEYWORD宏:

[cpp] view plain copy
  1. #define KEYWORD(symbol, flags, nargs, func) \  
  2.     [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },  
该宏的定义是为了给接下来定义的keyword_info这个关键字信息数组的赋值,keyword_info定义如下:

[cpp] view plain copy
  1. struct {  
  2.     const char *name;  
  3.     int (*func)(int nargs, char **args);  
  4.     unsigned char nargs;  
  5.     unsigned char flags;  
  6. } keyword_info[KEYWORD_COUNT] = {  
  7.     [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },  
  8. #include "keywords.h"  
  9. };  
keyword_info数组元素是keywords.h文件中的内容,因为此时KEYWORD宏已经被定义了同时__MAKE_KEYWORD_ENUM__被取消定义,因此keywords.h文件内容此时变为:

[cpp] view plain copy
  1. KEYWORD(capability,  OPTION,  0, 0)  
  2. KEYWORD(chdir,       COMMAND, 1, do_chdir)  
  3. KEYWORD(chroot,      COMMAND, 1, do_chroot)  
  4. KEYWORD(class,       OPTION,  0, 0)  
  5. KEYWORD(class_start, COMMAND, 1, do_class_start)  
  6. KEYWORD(class_stop,  COMMAND, 1, do_class_stop)  
  7. KEYWORD(class_reset, COMMAND, 1, do_class_reset)  
  8. KEYWORD(console,     OPTION,  0, 0)  
  9. KEYWORD(critical,    OPTION,  0, 0)  
  10. KEYWORD(disabled,    OPTION,  0, 0)  
  11. KEYWORD(domainname,  COMMAND, 1, do_domainname)  
  12. KEYWORD(exec,        COMMAND, 1, do_exec)  
  13. KEYWORD(export,      COMMAND, 2, do_export)  
  14. KEYWORD(group,       OPTION,  0, 0)  
  15. KEYWORD(hostname,    COMMAND, 1, do_hostname)  
  16. KEYWORD(ifup,        COMMAND, 1, do_ifup)  
  17. KEYWORD(insmod,      COMMAND, 1, do_insmod)  
  18. KEYWORD(import,      SECTION, 1, 0)  
  19. KEYWORD(keycodes,    OPTION,  0, 0)  
  20. KEYWORD(mkdir,       COMMAND, 1, do_mkdir)  
  21. KEYWORD(mount_all,   COMMAND, 1, do_mount_all)  
  22. KEYWORD(mount,       COMMAND, 3, do_mount)  
  23. KEYWORD(on,          SECTION, 0, 0)  
  24. KEYWORD(oneshot,     OPTION,  0, 0)  
  25. KEYWORD(onrestart,   OPTION,  0, 0)  
  26. KEYWORD(restart,     COMMAND, 1, do_restart)  
  27. KEYWORD(restorecon,  COMMAND, 1, do_restorecon)  
  28. KEYWORD(rm,          COMMAND, 1, do_rm)  
  29. KEYWORD(rmdir,       COMMAND, 1, do_rmdir)  
  30. KEYWORD(seclabel,    OPTION,  0, 0)  
  31. KEYWORD(service,     SECTION, 0, 0)  
  32. KEYWORD(setcon,      COMMAND, 1, do_setcon)  
  33. KEYWORD(setenforce,  COMMAND, 1, do_setenforce)  
  34. KEYWORD(setenv,      OPTION,  2, 0)  
  35. KEYWORD(setkey,      COMMAND, 0, do_setkey)  
  36. KEYWORD(setprop,     COMMAND, 2, do_setprop)  
  37. KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)  
  38. KEYWORD(setsebool,   COMMAND, 1, do_setsebool)  
  39. KEYWORD(socket,      OPTION,  0, 0)  
  40. KEYWORD(start,       COMMAND, 1, do_start)  
  41. KEYWORD(stop,        COMMAND, 1, do_stop)  
  42. KEYWORD(trigger,     COMMAND, 1, do_trigger)  
  43. KEYWORD(symlink,     COMMAND, 1, do_symlink)  
  44. KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)  
  45. KEYWORD(user,        OPTION,  0, 0)  
  46. KEYWORD(wait,        COMMAND, 1, do_wait)  
  47. KEYWORD(write,       COMMAND, 2, do_write)  
  48. KEYWORD(copy,        COMMAND, 2, do_copy)  
  49. KEYWORD(chown,       COMMAND, 2, do_chown)  
  50. KEYWORD(chmod,       COMMAND, 2, do_chmod)  
  51. KEYWORD(loglevel,    COMMAND, 1, do_loglevel)  
  52. KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)  
  53. KEYWORD(pipe,        COMMAND, 2, do_pipe)  
  54. KEYWORD(ioprio,      OPTION,  0, 0)  

使用上述KEYWORD宏展开得到keyword_info数组内容如下:

[cpp] view plain copy
  1. [ K_capability      ] = { capability,   0,              1,  OPTION, },  
  2. [ K_class           ] = { class,        0,              1,  OPTION, },  
  3. [ K_console         ] = { console,      0,              1,  OPTION, },  
  4. [ K_critical        ] = { critical,     0,              1,  OPTION, },  
  5. [ K_group           ] = { group,        0,              1,  OPTION, },  
  6. [ K_disabled        ] = { disabled,     0,              1,  OPTION, },  
  7. [ K_keycodes        ] = { keycodes,     0,              1,  OPTION, },  
  8. [ K_oneshot         ] = { oneshot,      0,              1,  OPTION, },  
  9. [ K_onrestart       ] = { onrestart,    0,              1,  OPTION, },  
  10. [ K_socket          ] = { socket,       0,              1,  OPTION, },  
  11. [ K_setenv          ] = { setenv,       0,              3,  OPTION, },  
  12. [ K_ioprio          ] = { ioprio,       0,              1,  OPTION, },  
  13. [ K_user            ] = { user,         0,              1,  OPTION, },  
  14. [ K_seclabel        ] = { seclabel,     0,              1,  OPTION, },  
  15.   
  16. [ K_service         ] = { service,      0,              1, SECTION, },  
  17. [ K_on              ] = { on,           0,              1, SECTION, },  
  18. [ K_import          ] = { import,       0,              2, SECTION, },  
  19.   
  20. [ K_chdir           ] = { chdir,        do_chdir,       2, COMMAND, },  
  21. [ K_chroot          ] = { chroot,       do_chroot,      2, COMMAND, },  
  22. [ K_class_start     ] = { class_start,  do_class_start, 2, COMMAND, },  
  23. [ K_class_stop      ] = { class_stop,   do_class_stop,  2, COMMAND, },  
  24. [ K_class_reset     ] = { class_reset,  do_class_reset, 2, COMMAND, },  
  25. [ K_domainname      ] = { domainname,   do_domainname,  2, COMMAND, },  
  26. [ K_exec            ] = { exec,         do_exec,        2, COMMAND, },  
  27. [ K_export          ] = { export,       do_export,      3, COMMAND, },  
  28. [ K_hostname        ] = { hostname,     do_hostname,    2, COMMAND, },  
  29. [ K_ifup            ] = { ifup,         do_ifup,        2, COMMAND, },  
  30. [ K_insmod          ] = { insmod,       do_insmod,      3, COMMAND, },  
  31. [ K_mkdir           ] = { mkdir,        do_mkdir,       2, COMMAND, },  
  32. [ K_mount_all       ] = { mount_all,    do_mount_all,   2, COMMAND, },  
  33. [ K_mount           ] = { mount,        do_mount,       4, COMMAND, },  
  34. [ K_restart         ] = { restart,      do_restart,     2, COMMAND, },  
  35. [ K_restorecon      ] = { restorecon,   do_restorecon,  2, COMMAND, },  
  36. [ K_rm              ] = { rm,           do_rm,          2, COMMAND, }  
  37. [ K_rmdir           ] = { rmdir,        do_rmdir,       2, COMMAND, },  
  38. [ K_setcon          ] = { setcon,       do_setcon,      2, COMMAND, },  
  39. [ K_setenforce      ] = { setenforce,   do_setenforce,  2, COMMAND, },  
  40. [ K_setkey          ] = { setkey,       do_setkey,      1, COMMAND, },  
  41. [ K_setprop         ] = { setprop,      do_setprop,     3, COMMAND, },  
  42. [ K_setrlimit       ] = { setrlimit,    do_setrlimit,   4, COMMAND, },  
  43. [ K_setsebool       ] = { setsebool,    do_setsebool,   2, COMMAND, },  
  44. [ K_start           ] = { start,        do_start,       2, COMMAND, },  
  45. [ K_stop            ] = { stop,         do_stop,        2, COMMAND, },  
  46. [ K_trigger         ] = { trigger,      do_trigger,     2, COMMAND, },  
  47. [ K_symlink         ] = { symlink,      do_symlink,     2, COMMAND, },  
  48. [ K_sysclktz        ] = { sysclktz,     do_sysclktz,    2, COMMAND, },  
  49. [ K_wait            ] = { wait,         do_wait,        2, COMMAND, },  
  50. [ K_write           ] = { write,        do_write,       3, COMMAND, },  
  51. [ K_copy            ] = { copy,         do_copy,        3, COMMAND, },  
  52. [ K_chown           ] = { chown,        do_chown,       3, COMMAND, },  
  53. [ K_chmod           ] = { chmod,        do_chmod,       3, COMMAND, },  
  54. [ K_loglevel        ] = { loglevel,     do_loglevel,    2, COMMAND, },  
  55. [ K_load_persist_props] = { load_persist_props, do_load_persist_props,1, COMMAND, },  
  56. [ K_pipe            ] = { pipe,         do_pipe,        3, COMMAND, },  

了解了这些内容之后,我们开始分析init.rc文件的真正解析过程:

[cpp] view plain copy
  1. static void parse_config(const char *fn, char *s)  
  2. {  
  3.     struct parse_state state;  
  4.     struct listnode import_list;  
  5.     struct listnode *node;  
  6.     char *args[INIT_PARSER_MAXARGS];  
  7.     int nargs;  
  8.   
  9.     nargs = 0;  
  10.     state.filename = fn; //文件名称  
  11.     state.line = 0; //统计文件行数  
  12.     state.ptr = s; //文件内容  
  13.     state.nexttoken = 0;  
  14.     state.parse_line = parse_line_no_op; //解析函数指针  
  15.     //初始化import_list链表,该链表用于保存通过import关键字引入的其他.rc文件  
  16.     list_init(&import_list);  
  17.     state.priv = &import_list;  
  18.       
  19.     for (;;) {  
  20.     //next_token函数用于扫描init.rc中的token  
  21.         switch (next_token(&state)) {  
  22.     //文件结束EOF  
  23.         case T_EOF:  
  24.             state.parse_line(&state, 0, 0);  
  25.             goto parser_done;  
  26.     //新行NEWLINE  
  27.         case T_NEWLINE:  
  28.             state.line++;   
  29.             if (nargs) {  
  30.         //根据行头查找关键字类型  
  31.                 int kw = lookup_keyword(args[0]);  
  32.         //如果是SECTION类型,SECTION包括以关键字service,on,import开头的语句  
  33.                 if (kw_is(kw, SECTION)) {  
  34.             //解析该行,此时parse_line指向的回调函数为parse_line_no_op,该函数什么也不做  
  35.                     state.parse_line(&state, 0, 0);  
  36.             //解析该SECTION  
  37.                     parse_new_section(&state, kw, nargs, args);  
  38.         //如果不是SECTION类型,则调用parse_line指向的回调函数  
  39.                 } else {  
  40.                     state.parse_line(&state, nargs, args);  
  41.                 }  
  42.                 nargs = 0;  
  43.             }  
  44.             break;  
  45.         //文本TEXT  
  46.         case T_TEXT:  
  47.             if (nargs < INIT_PARSER_MAXARGS) {  
  48.                 args[nargs++] = state.text;  
  49.             }  
  50.             break;  
  51.         }  
  52.     }  
  53.   
  54. parser_done:  
  55.      //init.rc 文件解析结束后,解析通过import关键字导入的.rc文件  
  56.     list_for_each(node, &import_list) {  
  57.       //从import_list链表中循环取出导入的.rc文件路径  
  58.          struct import *import = node_to_item(node, struct import, list);  
  59.          int ret;  
  60.   
  61.          INFO("importing '%s'", import->filename);  
  62.          //读取并解析导入的.rc文件  
  63.          ret = init_parse_config_file(import->filename);  
  64.          if (ret)  
  65.              ERROR("could not import file '%s' from '%s'\n",import->filename, fn);  
  66.     }  
  67. }  
函数parse_config通过调用next_token函数来查找3个定义的token,当查找到T_NEWLINE  token时,使用lookup_keyword函数来判断关键字类型,如果属于SECTION类型,则调用parse_new_section函数进行解析,如果是其他类型,则调用parse_line指向的回调函数来解析。

在前面介绍了通过定义枚举来为每个命令分配类型,lookup_keyword函数通过比较命令名称来返回对应命令的类型,如下所示:

[cpp] view plain copy
  1. int lookup_keyword(const char *s)  
  2. {  
  3.     switch (*s++) {  
  4.     case 'c':  
  5.     if (!strcmp(s, "opy")) return K_copy;  
  6.         if (!strcmp(s, "apability")) return K_capability;  
  7.         if (!strcmp(s, "hdir")) return K_chdir;  
  8.         if (!strcmp(s, "hroot")) return K_chroot;  
  9.         if (!strcmp(s, "lass")) return K_class;  
  10.         if (!strcmp(s, "lass_start")) return K_class_start;  
  11.         if (!strcmp(s, "lass_stop")) return K_class_stop;  
  12.         if (!strcmp(s, "lass_reset")) return K_class_reset;  
  13.         if (!strcmp(s, "onsole")) return K_console;  
  14.         if (!strcmp(s, "hown")) return K_chown;  
  15.         if (!strcmp(s, "hmod")) return K_chmod;  
  16.         if (!strcmp(s, "ritical")) return K_critical;  
  17.         break;  
  18.     case 'd':  
  19.         if (!strcmp(s, "isabled")) return K_disabled;  
  20.         if (!strcmp(s, "omainname")) return K_domainname;  
  21.         break;  
  22.     case 'e':  
  23.         if (!strcmp(s, "xec")) return K_exec;  
  24.         if (!strcmp(s, "xport")) return K_export;  
  25.         break;  
  26.     case 'g':  
  27.         if (!strcmp(s, "roup")) return K_group;  
  28.         break;  
  29.     case 'h':  
  30.         if (!strcmp(s, "ostname")) return K_hostname;  
  31.         break;  
  32.     case 'i':  
  33.         if (!strcmp(s, "oprio")) return K_ioprio;  
  34.         if (!strcmp(s, "fup")) return K_ifup;  
  35.         if (!strcmp(s, "nsmod")) return K_insmod;  
  36.         if (!strcmp(s, "mport")) return K_import;  
  37.         break;  
  38.     case 'k':  
  39.         if (!strcmp(s, "eycodes")) return K_keycodes;  
  40.         break;  
  41.     case 'l':  
  42.         if (!strcmp(s, "oglevel")) return K_loglevel;  
  43.         if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;  
  44.         break;  
  45.     case 'm':  
  46.         if (!strcmp(s, "kdir")) return K_mkdir;  
  47.         if (!strcmp(s, "ount_all")) return K_mount_all;  
  48.         if (!strcmp(s, "ount")) return K_mount;  
  49.         break;  
  50.     case 'o':  
  51.         if (!strcmp(s, "n")) return K_on;  
  52.         if (!strcmp(s, "neshot")) return K_oneshot;  
  53.         if (!strcmp(s, "nrestart")) return K_onrestart;  
  54.         break;  
  55.     case 'r':  
  56.         if (!strcmp(s, "estart")) return K_restart;  
  57.         if (!strcmp(s, "estorecon")) return K_restorecon;  
  58.         if (!strcmp(s, "mdir")) return K_rmdir;  
  59.         if (!strcmp(s, "m")) return K_rm;  
  60.         break;  
  61.     case 's':  
  62.         if (!strcmp(s, "eclabel")) return K_seclabel;  
  63.         if (!strcmp(s, "ervice")) return K_service;  
  64.         if (!strcmp(s, "etcon")) return K_setcon;  
  65.         if (!strcmp(s, "etenforce")) return K_setenforce;  
  66.         if (!strcmp(s, "etenv")) return K_setenv;  
  67.         if (!strcmp(s, "etkey")) return K_setkey;  
  68.         if (!strcmp(s, "etprop")) return K_setprop;  
  69.         if (!strcmp(s, "etrlimit")) return K_setrlimit;  
  70.         if (!strcmp(s, "etsebool")) return K_setsebool;  
  71.         if (!strcmp(s, "ocket")) return K_socket;  
  72.         if (!strcmp(s, "tart")) return K_start;  
  73.         if (!strcmp(s, "top")) return K_stop;  
  74.         if (!strcmp(s, "ymlink")) return K_symlink;  
  75.         if (!strcmp(s, "ysclktz")) return K_sysclktz;  
  76.         break;  
  77.     case 't':  
  78.         if (!strcmp(s, "rigger")) return K_trigger;  
  79.         break;  
  80.     case 'u':  
  81.         if (!strcmp(s, "ser")) return K_user;  
  82.         break;  
  83.     case 'w':  
  84.         if (!strcmp(s, "rite")) return K_write;  
  85.         if (!strcmp(s, "ait")) return K_wait;  
  86.         break;  
  87.     case 'p':  
  88.         if (!strcmp(s, "ipe")) return K_pipe;  
  89.     }  
  90.     return K_UNKNOWN;  
  91. }  
对于SECTION类型,又包括import、on、service三种,因此需要分别处理:

[cpp] view plain copy
  1. void parse_new_section(struct parse_state *state, int kw,  
  2.                        int nargs, char **args)  
  3. {  
  4.     printf("[ %s %s ]\n", args[0],nargs > 1 ? args[1] : "");  
  5.     switch(kw) {  
  6.     //如果关键字是service,表示这是一条描述服务的语句,调用parse_service函数来解析该行,并将解析得到的service保存在state->context中,同时设置解析函数parse_line为parse_line_service()  
  7.     case K_service:  
  8.         state->context = parse_service(state, nargs, args);  
  9.         if (state->context) {  
  10.             state->parse_line = parse_line_service;  
  11.             return;  
  12.         }  
  13.         break;  
  14.     //如果关键字是on,表示这是一条Action语句,调用parse_action函数来解析该行,并将解析得到的Action保存在state->context中,同时设置解析函数parse_line为parse_line_action()  
  15.     case K_on:  
  16.         state->context = parse_action(state, nargs, args);  
  17.         if (state->context) {  
  18.             state->parse_line = parse_line_action;  
  19.             return;  
  20.         }  
  21.         break;  
  22.     //如果关键字是import,表示这是一条import语句,调用parse_import函数来解析该行,同时设置解析函数parse_line为parse_line_no_op()  
  23.     case K_import:  
  24.         parse_import(state, nargs, args);  
  25.         break;  
  26.     }  
  27.     state->parse_line = parse_line_no_op;  
  28. }  
1.Service解析

[cpp] view plain copy
  1. static void *parse_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct service *svc;  
  4.     //检查参数个数  
  5.     if (nargs < 3) {  
  6.         parse_error(state, "services must have a name and a program\n");  
  7.         return 0;  
  8.     }  
  9.     //检查参数名称的有效性  
  10.     if (!valid_name(args[1])) {  
  11.         parse_error(state, "invalid service name '%s'\n", args[1]);  
  12.         return 0;  
  13.     }  
  14.     //从服务链表中查找该名称的服务以防止出现重复的服务  
  15.     svc = service_find_by_name(args[1]);  
  16.     if (svc) {  
  17.         parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);  
  18.         return 0;  
  19.     }  
  20.     nargs -= 2;  
  21.     //创建一个service  
  22.     svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);  
  23.     if (!svc) {  
  24.         parse_error(state, "out of memory\n");  
  25.         return 0;  
  26.     }  
  27.     svc->name = args[1];  
  28.     svc->classname = "default";  
  29.     memcpy(svc->args, args + 2, sizeof(char*) * nargs);  
  30.     svc->args[nargs] = 0;  
  31.     svc->nargs = nargs;  
  32.     svc->onrestart.name = "onrestart";  
  33.     list_init(&svc->onrestart.commands);  
  34.     //将该服务添加到service_list链表中  
  35.     list_add_tail(&service_list, &svc->slist);  
  36.     return svc;  
  37. }  
2. Service 配置项解析

[cpp] view plain copy
  1. static void parse_line_service(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     //从state->context中取出已经解析生成的service  
  4.     struct service *svc = state->context;  
  5.     struct command *cmd;  
  6.     int i, kw, kw_nargs;  
  7.     if (nargs == 0) {  
  8.         return;  
  9.     }  
  10.     svc->ioprio_class = IoSchedClass_NONE;  
  11.     //查找命令类型  
  12.     kw = lookup_keyword(args[0]);  
  13.     switch (kw) {  
  14.     //capability命令处理  
  15.     case K_capability:  
  16.         break;  
  17.     //class命令处理  
  18.     case K_class:  
  19.         if (nargs != 2) {  
  20.             parse_error(state, "class option requires a classname\n");  
  21.         } else {  
  22.             svc->classname = args[1];  
  23.         }  
  24.         break;  
  25.     //console命令处理  
  26.     case K_console:  
  27.         svc->flags |= SVC_CONSOLE;  
  28.         break;  
  29.     //disabled命令处理  
  30.     case K_disabled:  
  31.         svc->flags |= SVC_DISABLED;  
  32.         svc->flags |= SVC_RC_DISABLED;  
  33.         break;  
  34.     //ioprio命令处理  
  35.     case K_ioprio:  
  36.         if (nargs != 3) {  
  37.             parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");  
  38.         } else {  
  39.             svc->ioprio_pri = strtoul(args[2], 0, 8);  
  40.   
  41.             if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {  
  42.                 parse_error(state, "priority value must be range 0 - 7\n");  
  43.                 break;  
  44.             }  
  45.             if (!strcmp(args[1], "rt")) {  
  46.                 svc->ioprio_class = IoSchedClass_RT;  
  47.             } else if (!strcmp(args[1], "be")) {  
  48.                 svc->ioprio_class = IoSchedClass_BE;  
  49.             } else if (!strcmp(args[1], "idle")) {  
  50.                 svc->ioprio_class = IoSchedClass_IDLE;  
  51.             } else {  
  52.                 parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");  
  53.             }  
  54.         }  
  55.         break;  
  56.     //group命令处理  
  57.     case K_group:  
  58.         if (nargs < 2) {  
  59.             parse_error(state, "group option requires a group id\n");  
  60.         } else if (nargs > NR_SVC_SUPP_GIDS + 2) {  
  61.             parse_error(state, "group option accepts at most %d supp. groups\n",  
  62.                         NR_SVC_SUPP_GIDS);  
  63.         } else {  
  64.             int n;  
  65.             svc->gid = decode_uid(args[1]);  
  66.             for (n = 2; n < nargs; n++) {  
  67.                 svc->supp_gids[n-2] = decode_uid(args[n]);  
  68.             }  
  69.             svc->nr_supp_gids = n - 2;  
  70.         }  
  71.         break;  
  72.     //keycodes命令处理,service命令组合键启动  
  73.     case K_keycodes:  
  74.         if (nargs < 2) {  
  75.             parse_error(state, "keycodes option requires atleast one keycode\n");  
  76.         } else {  
  77.             svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));  
  78.             if (!svc->keycodes) {  
  79.                 parse_error(state, "could not allocate keycodes\n");  
  80.             } else {  
  81.                 svc->nkeycodes = nargs - 1;  
  82.                 for (i = 1; i < nargs; i++) {  
  83.                     svc->keycodes[i - 1] = atoi(args[i]);  
  84.                 }  
  85.             }  
  86.         }  
  87.         break;  
  88.     //oneshot命令处理  
  89.     case K_oneshot:  
  90.         svc->flags |= SVC_ONESHOT;  
  91.         break;  
  92.     //onrestart命令处理  
  93.     case K_onrestart:  
  94.         nargs--;  
  95.         args++;  
  96.         //onrestart restart zygote  
  97.         //查找onrestart后的参数类型  
  98.         kw = lookup_keyword(args[0]);  
  99.         //如果不属于COMMAND类型,跳出不处理  
  100.         if (!kw_is(kw, COMMAND)) {  
  101.             parse_error(state, "invalid command '%s'\n", args[0]);  
  102.             break;  
  103.         }  
  104.         //读取该命令的参数个数  
  105.         kw_nargs = kw_nargs(kw);  
  106.         //验证参数个数  
  107.         if (nargs < kw_nargs) {  
  108.             parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,  
  109.                 kw_nargs > 2 ? "arguments" : "argument");  
  110.             break;  
  111.         }  
  112.         //创建一个command  
  113.         cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  114.         cmd->func = kw_func(kw);  
  115.         cmd->nargs = nargs;  
  116.         memcpy(cmd->args, args, sizeof(char*) * nargs);  
  117.         //添加到svc->onrestart.commands链表中  
  118.         list_add_tail(&svc->onrestart.commands, &cmd->clist);  
  119.         break;  
  120.     //critical命令处理  
  121.     case K_critical:  
  122.         svc->flags |= SVC_CRITICAL;  
  123.         break;  
  124.     //setenv命令处理  
  125.     case K_setenv: { /* name value */  
  126.         struct svcenvinfo *ei;  
  127.         if (nargs < 2) {  
  128.             parse_error(state, "setenv option requires name and value arguments\n");  
  129.             break;  
  130.         }  
  131.         //创建一个环境变量svcenvinfo  
  132.         ei = calloc(1, sizeof(*ei));  
  133.         if (!ei) {  
  134.             parse_error(state, "out of memory\n");  
  135.             break;  
  136.         }  
  137.         ei->name = args[1];  
  138.         ei->value = args[2];  
  139.         //添加到svc->envvars链表中  
  140.         ei->next = svc->envvars;  
  141.         svc->envvars = ei;  
  142.         break;  
  143.     }  
  144.     //socket命令处理  
  145.     case K_socket: {/* name type perm [ uid gid ] */  
  146.         struct socketinfo *si;  
  147.         if (nargs < 4) {  
  148.             parse_error(state, "socket option requires name, type, perm arguments\n");  
  149.             break;  
  150.         }  
  151.         if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")  
  152.                 && strcmp(args[2],"seqpacket")) {  
  153.             parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");  
  154.             break;  
  155.         }  
  156.         //创建一个socket信息结构  
  157.         si = calloc(1, sizeof(*si));  
  158.         if (!si) {  
  159.             parse_error(state, "out of memory\n");  
  160.             break;  
  161.         }  
  162.         si->name = args[1];  
  163.         si->type = args[2];  
  164.         si->perm = strtoul(args[3], 0, 8);  
  165.         if (nargs > 4)  
  166.             si->uid = decode_uid(args[4]);  
  167.         if (nargs > 5)  
  168.             si->gid = decode_uid(args[5]);  
  169.         //添加到svc->sockets链表中  
  170.         si->next = svc->sockets;  
  171.         svc->sockets = si;  
  172.         break;  
  173.     }  
  174.     //user命令处理  
  175.     case K_user:  
  176.         if (nargs != 2) {  
  177.             parse_error(state, "user option requires a user id\n");  
  178.         } else {  
  179.             svc->uid = decode_uid(args[1]);  
  180.         }  
  181.         break;  
  182.     //seclabel命令处理  
  183.     case K_seclabel:  
  184. #ifdef HAVE_SELINUX  
  185.         if (nargs != 2) {  
  186.             parse_error(state, "seclabel option requires a label string\n");  
  187.         } else {  
  188.             svc->seclabel = args[1];  
  189.         }  
  190. #endif  
  191.         break;  
  192.   
  193.     default:  
  194.         parse_error(state, "invalid option '%s'\n", args[0]);  
  195.     }  
  196. }  
3. Action解析
[cpp] view plain copy
  1. static void *parse_action(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct action *act;  
  4.     if (nargs < 2) {  
  5.         parse_error(state, "actions must have a trigger\n");  
  6.         return 0;  
  7.     }  
  8.     if (nargs > 2) {  
  9.         parse_error(state, "actions may not have extra parameters\n");  
  10.         return 0;  
  11.     }  
  12.     //创建一个action  
  13.     act = calloc(1, sizeof(*act));  
  14.     act->name = args[1];  
  15.     list_init(&act->commands);  
  16.     //添加到action_list链表中  
  17.     list_add_tail(&action_list, &act->alist);  
  18.     return act;  
  19. }  
解析到新的on section调用parse_action()时,申请了struct action *act,设置:

     1) act->name为on section的名字(比如boot/fs/);

     2) 初始化list act->commands

     3) 把act->alist加入到action_list的列尾

这样,action创建并加入到了action_list中。

4.Action 命令解析

[cpp] view plain copy
  1. static void parse_line_action(struct parse_state* state, int nargs, char **args)  
  2. {  
  3.     struct command *cmd;  
  4.     //获取解析得到的action  
  5.     struct action *act = state->context;  
  6.     int (*func)(int nargs, char **args);  
  7.     int kw, n;  
  8.     if (nargs == 0) {  
  9.         return;  
  10.     }  
  11.     //查找关键字类型  
  12.     kw = lookup_keyword(args[0]);  
  13.     //如果不是COMMAND类型,跳出不处理  
  14.     if (!kw_is(kw, COMMAND)) {  
  15.         parse_error(state, "invalid command '%s'\n", args[0]);  
  16.         return;  
  17.     }  
  18.     //得到命令参数个数,验证参数个数的合法性  
  19.     n = kw_nargs(kw);  
  20.     if (nargs < n) {  
  21.         parse_error(state, "%s requires %d %s\n", args[0], n - 1,  
  22.             n > 2 ? "arguments" : "argument");  
  23.         return;  
  24.     }  
  25.     //创建命令command  
  26.     cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  27.     cmd->func = kw_func(kw);  
  28.     cmd->nargs = nargs;  
  29.     memcpy(cmd->args, args, sizeof(char*) * nargs);  
  30.     //将command添加到act->commands链表中  
  31.     list_add_tail(&act->commands, &cmd->clist);  
  32. }  
action里的command的解析
    对on section内action里的command,调用parse_line_action()
     1) 查找关键字,核对是否是COMMAND,参数数目是否正确
     2) 申请struct command *cmd
       - cmd->func从keyword表中获取;
       - 设置参数个数给cmd->nargs,拷贝参数给cmd->args;
       - 把cmd->clist加入到act->commands的列尾

这样,command加入到了action中。

5. import 命令解析

[cpp] view plain copy
  1. void parse_import(struct parse_state *state, int nargs, char **args)  
  2. {  
  3.     struct listnode *import_list = state->priv;  
  4.     struct import *import;  
  5.     char conf_file[PATH_MAX];  
  6.     int ret;  
  7.     //参数个数判断  
  8.     if (nargs != 2) {  
  9.         ERROR("single argument needed for import\n");  
  10.         return;  
  11.     }  
  12.   
  13.     ret = expand_props(conf_file, args[1], sizeof(conf_file));  
  14.     if (ret) {  
  15.         ERROR("error while handling import on line '%d' in '%s'\n",  
  16.               state->line, state->filename);  
  17.         return;  
  18.     }  
  19.     //创建一个import  
  20.     import = calloc(1, sizeof(struct import));  
  21.     //设置import文件名称  
  22.     import->filename = strdup(conf_file);  
  23.     //添加到import->list链表中  
  24.     list_add_tail(import_list, &import->list);  
  25.     INFO("found import '%s', adding to import list", import->filename);  
  26. }  

当init.rc文件解析完成后,将从import_list链表中取出通过关键字import导入的其他rc文件,并调用init_parse_config_file函数进行解析:

[cpp] view plain copy
  1. list_for_each(node, &import_list) {  
  2.      struct import *import = node_to_item(node, struct import, list);  
  3.      int ret;  
  4.   
  5.      INFO("importing '%s'", import->filename);  
  6.      ret = init_parse_config_file(import->filename);  
  7.      if (ret)  
  8.          ERROR("could not import file '%s' from '%s'\n",  
  9.                import->filename, fn);  
  10. }  
到此init.rc文件就解析完成,文件内容全部存储在service_list和action_list链表中。

添加Action到待执行队列

 当解析完所有的init.rc内容之后,在执行这些action之前,需要按顺序将其置于一个待执行队列中

[cpp] view plain copy
  1. void action_for_each_trigger(const char *trigger,void (*func)(struct action *act))  
  2. {  
  3.     struct listnode *node;  
  4.     struct action *act;  
  5.     //遍历action_list链表,根据名字查找相关的action  
  6.     list_for_each(node, &action_list) {  
  7.         act = node_to_item(node, struct action, alist);  
  8.         if (!strcmp(act->name, trigger)) {  
  9.             //回调action_add_queue_tail函数  
  10.             func(act);  
  11.         }  
  12.     }  
  13. }  
从action_list链表中查询指定名称的action,并调用函数action_add_queue_tail将其添加到待执行队列action_queue中。

[cpp] view plain copy
  1. void action_add_queue_tail(struct action *act)  
  2. {  
  3.     list_add_tail(&action_queue, &act->qlist);  
  4. }  

action_for_each_trigger()把队列action_list里所匹配的action,追加到action_queue的队尾

构建新的Action

还有一些没有在init.rc中定义的action,相比init.rc,这些action的共同点是没有参数,对于这类action,通过queue_builtin_action()函数来构建

queue_builtin_action()把执行的函数组成command,创建action,挂在action_list上,并追加到action_queue的队尾。

[cpp] view plain copy
  1. void queue_builtin_action(int (*func)(int nargs, char **args), char *name)  
  2. {  
  3.     struct action *act;  
  4.     struct command *cmd;  
  5.     //创建一个Action  
  6.     act = calloc(1, sizeof(*act));  
  7.     act->name = name;  
  8.     list_init(&act->commands);  
  9.     //为该Action创建一个command  
  10.     cmd = calloc(1, sizeof(*cmd));  
  11.     cmd->func = func;  
  12.     cmd->args[0] = name;  
  13.     //将该command添加到Action的commands链表中  
  14.     list_add_tail(&act->commands, &cmd->clist);  
  15.     //将该Action添加到action_list链表中  
  16.     list_add_tail(&action_list, &act->alist);  
  17.     //将该Action添加到待执行队列action_queue中  
  18.     action_add_queue_tail(act);  
  19. }  

添加Action到待执行队列

init 进程通过action_for_each_trigger 和queue_builtin_action 函数向待执行队列action_queue依次添加了以下Action:

[cpp] view plain copy
  1.     action_for_each_trigger("early-init", action_add_queue_tail);  
  2.     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  3.     queue_builtin_action(keychord_init_action, "keychord_init");  
  4.     queue_builtin_action(console_init_action, "console_init");  
  5.     /* execute all the boot actions to get us started */  
  6.     action_for_each_trigger("init", action_add_queue_tail);  
  7.     /* skip mounting filesystems in charger mode */  
  8.     action_for_each_trigger("early-fs", action_add_queue_tail);  
  9.     action_for_each_trigger("fs", action_add_queue_tail);  
  10.     action_for_each_trigger("post-fs", action_add_queue_tail);  
  11.     if (!is_charger) {  
  12.         //action_for_each_trigger("post-fs", action_add_queue_tail);  
  13.         action_for_each_trigger("post-fs-data", action_add_queue_tail);  
  14.     }  
  15.     queue_builtin_action(property_service_init_action, "property_service_init");  
  16.     queue_builtin_action(signal_init_action, "signal_init");  
  17.     queue_builtin_action(check_startup_action, "check_startup");  
  18.     if (!strcmp(bootmode, "alarm")) {  
  19.         action_for_each_trigger("alarm", action_add_queue_tail);  
  20.     }  
  21.     if (is_charger) {  
  22.         action_for_each_trigger("charger", action_add_queue_tail);  
  23.     } else {  
  24.         action_for_each_trigger("early-boot", action_add_queue_tail);  
  25.         action_for_each_trigger("boot", action_add_queue_tail);  
  26.     }  
  27.      /* run all property triggers based on current state of the properties */  
  28.     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  29. #if BOOTCHART  
  30.     queue_builtin_action(bootchart_init_action, "bootchart_init");  
  31. #endif  

early-init
    查看init.rc中的相应字符段为start ueventd
    这个action主要目的是通过early-init启动ueventd服务,这个服务负责uevent(user space event)的处理,uevent是内核向用户空间发出的一个时间通知,使应用程序能够有机会对该event做出反应。

wait_for_coldboot_done
    android 冷过程结束后会生成dev/.coldboot_done文件,wait_for_coldboot_done这个action会等待dev/.coldboot_done文件的生成,等待时长为5s。当然这个action不会阻塞android的冷启动过程,它会每查询一次就会休眠0.1s,直到冷启动结束。

[cpp] view plain copy
  1. static int wait_for_coldboot_done_action(int nargs, char **args)  
  2. {  
  3.     int ret;  
  4.     INFO("wait for %s\n", coldboot_done);  
  5.     //  /dev/.coldboot_done  
  6.     //#define COMMAND_RETRY_TIMEOUT 5  
  7.     ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);  
  8.     if (ret)  
  9.         ERROR("Timed out waiting for %s\n", coldboot_done);  
  10.     return ret;  
  11. }  

keychord_init

keychord是组合按键,Android暂时还不支持keychord机制,keychord机制就是在init.rc文件中为每个服务配置组合键,在服务解析时为指定服务设置相应的键码值。

[cpp] view plain copy
  1. static int keychord_init_action(int nargs, char **args)  
  2. {  
  3.     keychord_init();  
  4.     return 0;  
  5. }  
调用keychord_init函数来初始化组合键机制。

[cpp] view plain copy
  1. void keychord_init()  
  2. {  
  3.     int fd, ret;  
  4.     //遍历service_list链表,为每个service分配keychord_id  
  5.     service_for_each(add_service_keycodes);  
  6.   
  7.     /* nothing to do if no services require keychords */  
  8.     if (!keychords)  
  9.         return;  
  10.     //打开/dev/keychord设备文件  
  11.     fd = open("/dev/keychord", O_RDWR);  
  12.     if (fd < 0) {  
  13.         ERROR("could not open /dev/keychord\n");  
  14.         return;  
  15.     }  
  16.     //设置设备属性  
  17.     fcntl(fd, F_SETFD, FD_CLOEXEC);  
  18.     //将keychords数组内容写入设备文件中  
  19.     ret = write(fd, keychords, keychords_length);  
  20.     if (ret != keychords_length) {  
  21.         ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);  
  22.         close(fd);  
  23.         fd = -1;  
  24.     }  
  25.     free(keychords);  
  26.     keychords = 0;  
  27.     keychord_fd = fd;  
  28. }  
console_init
    1.如果/proc/cmdline指定了控制台终端,那么优先使用这个控制台,如果没有指定,那么将使用默认控制台终端/dev/console。
    2.加载开机图片,参考load_565rle_image函数
    a,通过ioctl函数修改dev/tty0(即终端控制台)为图像显示模式;
    b,尝试打开/initlogo.rle,如果失败,那么将dev/tty0恢复为文本显示模式,则开机时显示"ANDROID"文字;
    c,如果打开/initlogo.rle成功,那么init将会打开Framebuffer;

    d,将initlogo.rle数据写到Framebuffer中。

[cpp] view plain copy
  1. static int console_init_action(int nargs, char **args)  
  2. {  
  3.     int fd;  
  4.     char tmp[PROP_VALUE_MAX];  
  5.   
  6.     if (console[0]) {  
  7.         snprintf(tmp, sizeof(tmp), "/dev/%s", console);  
  8.         console_name = strdup(tmp);  
  9.     }  
  10.   
  11.     fd = open(console_name, O_RDWR);  
  12.     if (fd >= 0)  
  13.         have_console = 1;  
  14.     close(fd);  
  15.     //加载开机图片  
  16.     if( load_565rle_image(INIT_IMAGE_FILE) ) {  
  17.         fd = open("/dev/tty0", O_WRONLY);  
  18.         if (fd >= 0) {  
  19.             const char *msg;  
  20.                 msg = "\n"  
  21.             "\n"  
  22.             "\n"  
  23.             "\n"  
  24.             "\n"  
  25.             "\n"  
  26.             "\n"  // console is 40 cols x 30 lines  
  27.             "\n"  
  28.             "\n"  
  29.             "\n"  
  30.             "\n"  
  31.             "\n"  
  32.             "\n"  
  33.             "\n"  
  34.             "             A N D R O I D ";  
  35.             write(fd, msg, strlen(msg));  
  36.             close(fd);  
  37.         }  
  38.     }  
  39.     return 0;  
  40. }  
load_565rle_image()函数将加载由参数传递过来的图像文件,而后将该文件显示在LCD屏幕上。

property_service_init

       读取属性文件,并设置相关属性。关于Android属性系统,请查看Android 系统属性SystemProperty分析

[cpp] view plain copy
  1. static int property_service_init_action(int nargs, char **args)  
  2. {  
  3.     /* read any property files on system or data and 
  4.      * fire up the property service.  This must happen 
  5.      * after the ro.foo properties are set above so 
  6.      * that /data/local.prop cannot interfere with them. 
  7.      */  
  8.     start_property_service();  
  9.     return 0;  
  10. }  

signal_init
       
    创建套接字对,以便init进程在收到子进程终止的SIGCHLD信号时调用相应的handler

[cpp] view plain copy
  1. static int signal_init_action(int nargs, char **args)  
  2. {  
  3.     signal_init();  
  4.     return 0;  
  5. }  

[cpp] view plain copy
  1. void signal_init(void)  
  2. {  
  3.     int s[2];  
  4.     struct sigaction act;  
  5.     act.sa_handler = sigchld_handler; //设置handler回调函数  
  6.     act.sa_flags = SA_NOCLDSTOP;  
  7.     act.sa_mask = 0;  
  8.     act.sa_restorer = NULL;  
  9.     sigaction(SIGCHLD, &act, 0); //安装信号处理器  
  10.   
  11.     /* create a signalling mechanism for the sigchld handler */  
  12.     if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {   
  13.         signal_fd = s[0];  
  14.         signal_recv_fd = s[1];  
  15.         fcntl(s[0], F_SETFD, FD_CLOEXEC);  
  16.         fcntl(s[0], F_SETFL, O_NONBLOCK);  
  17.         fcntl(s[1], F_SETFD, FD_CLOEXEC);  
  18.         fcntl(s[1], F_SETFL, O_NONBLOCK);  
  19.     }  
  20.     handle_signal();  
  21. }  
init进程定义了handler,用于处理子进程的终止,当子进程死亡时将向父进程发送SIGCHLD信号,为了调用相关handler,init进程会通过socket连接SIGCHLD信号的handler,socketpair()函数会创建一对已经连接的套接字,事件处理handler会监视signal_recv_fd的值,当其值为1时,init进程就会调用子进程停止处理函数handler。

check_startup

    检查属性socket句柄及信号句柄是否安装成功

[cpp] view plain copy
  1. static int check_startup_action(int nargs, char **args)  
  2. {  
  3.     /* */  
  4.     if ((get_property_set_fd() < 0) ||(get_signal_fd() < 0)) {  
  5.         ERROR("init startup failure\n");  
  6.         exit(1);  
  7.     }  
  8.         /* signal that we hit this point */  
  9.     unlink("/dev/.booting");  
  10.     return 0;  
  11. }  
queue_property_triggers
   根据当前属性值来触发该属性对应的动作

[cpp] view plain copy
  1. static int queue_property_triggers_action(int nargs, char **args)  
  2. {  
  3.     queue_all_property_triggers();  
  4.     /* enable property triggers */  
  5.     property_triggers_enabled = 1;  
  6.     return 0;  
  7. }  
调用queue_all_property_triggers()函数来检查init.rc文件中配置的属性触发条件是否满足,如果满足,则将该Action添加到待执行队列中:

[cpp] view plain copy
  1. void queue_all_property_triggers()  
  2. {  
  3.     struct listnode *node;  
  4.     struct action *act;  
  5.     //遍历action_list链表  
  6.     list_for_each(node, &action_list) {  
  7.         //取得每个节点下对应的action  
  8.         act = node_to_item(node, struct action, alist);  
  9.         //如果该action的名字以property开头   
  10.         if (!strncmp(act->name, "property:", strlen("property:"))) {  
  11.             //读取该属性的名称  
  12.             const char* name = act->name + strlen("property:");  
  13.             //读取该属性的值  
  14.             const char* equals = strchr(name, '=');  
  15.             if (equals) {  
  16.                 char prop_name[PROP_NAME_MAX + 1];  
  17.                 const char* value;  
  18.                 int length = equals - name;  
  19.                 if (length > PROP_NAME_MAX) {  
  20.                     ERROR("property name too long in trigger %s", act->name);  
  21.                 } else {  
  22.                     memcpy(prop_name, name, length);  
  23.                     prop_name[length] = 0;  
  24.   
  25.                     /* 从属性系统中读取该属性的值*/  
  26.                     value = property_get(prop_name);  
  27.                     //如果属性系统中的值等于init.rc文件中设置的触发值  
  28.                     if (value && (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*"))) {  
  29.                         //将该Action添加到待执行队列action_queue中  
  30.                         action_add_queue_tail(act);  
  31.                     }  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  
  36. }  

Action - boot

  在boot动作中启动所有的service服务,启动命令如下:

[cpp] view plain copy
  1. class_start core                                                                                        
  2. class_start main  
我们可以在service配置中通过关键字class 将service分为不同的类别,从而可以通过class_start 或class_stop 来启动或停止某一类型的service,如下将adbd服务设置为core类型的服务:

[cpp] view plain copy
  1. service adbd /sbin/adbd  
  2. class core  
  3. disabled  
class_start core 表示启动所有类型为core的服务:

[cpp] view plain copy
  1. int do_class_start(int nargs, char **args)  
  2. {  
  3.         /* Starting a class does not start services 
  4.          * which are explicitly disabled.  They must 
  5.          * be started individually. 
  6.          */  
  7.     service_for_each_class(args[1], service_start_if_not_disabled);  
  8.     return 0;  
  9. }  
函数中args[1]指定该服务所属类型,service_start_if_not_disabled是启动服务的回调函数;

[cpp] view plain copy
  1. void service_for_each_class(const char *classname,  
  2.                             void (*func)(struct service *svc))  
  3. {  
  4.     struct listnode *node;  
  5.     struct service *svc;  
  6.     list_for_each(node, &service_list) {  
  7.         svc = node_to_item(node, struct service, slist);  
  8.         if (!strcmp(svc->classname, classname)) {  
  9.             func(svc);  
  10.         }  
  11.     }  
  12. }  
service_for_each_class函数通过遍历service_list服务链表来查找指定类型名称的服务,并调用函数service_start_if_not_disabled来启动服务。

[cpp] view plain copy
  1. static void service_start_if_not_disabled(struct service *svc)  
  2. {  
  3.     if (!(svc->flags & SVC_DISABLED)) {  
  4.         service_start(svc, NULL);  
  5.     }  
  6. }  
函数service_start_if_not_disabled()首先判断该服务的标志位是否设置成了SVC_DISABLED,SVC_DISABLED标志着服务不能在开始时启动,如果服务没有设置此标志位,则启动该服务,

service_start()函数比较复杂,这里就不详细分析,service_start()函数主要完成以下工作:

1)设置服务标志位

2)调用fork()系统调用创建新的进程;

3)获取属性匿名存储空间句柄,并添加为服务配置的环境变量;

4)创建服务配置的socket,调用publish_socket函数将创建的socket句柄添加到环境变量中;该环境变量为:ANDROID_SOCKET_XXX = fd

5)为新进程打开控制台,并设置新进程的PID,GID等;

6)调用execve()系统调用执行新进程运行的程序;

7)设置服务运行状态属性;该属性为:init.svc.XXX = running

Init进程循环执行

当将以上Action添加到待执行队列中后,init进程将进入无限循环中执行,循环过程中主要完成以下工作:

A. 调用函数execute_one_command来检查action_queue列表是否为空。如果不为空的话,那么init进程就会将保存在列表头中的action移除,并且执行这个被移除的action。由于前面我们将一个名称为“console_init”的action添加到了action_queue列表中,因此,在这个无限循环中,这个action就会被执行,即函数console_init_action会被调用。
B. 调用函数restart_processes来检查系统中是否有进程需要重启。在启动脚本/init.rc中,我们可以指定一个进程在退出之后会自动重新启动。在这种情况下,函数restart_processes就会检查是否存在需要重新启动的进程,如果存在的话,那么就会将它重新启动起来。
C. 处理系统属性变化事件。当我们调用函数property_set来改变一个系统属性值时,系统就会通过一个socket(通过调用函数get_property_set_fd可以获得它的文件描述符)来向init进程发送一个属性值改变事件通知。init进程接收到这个属性值改变事件之后,就会调用函数handle_property_set_fd来进行相应的处理。后面在分析第三个开机画面的显示过程时,我们就会看到,SurfaceFlinger服务就是通过修改“ctl.start”和“ctl.stop”属性值来启动和停止第三个开机画面的。
D. 处理一种称为“chorded keyboard”的键盘输入事件。这种类型为chorded keyboard”的键盘设备通过不同的铵键组合来描述不同的命令或者操作,它对应的设备文件为/dev/keychord。我们可以通过调用函数get_keychord_fd来获得这个设备的文件描述符,以便可以监控它的输入事件,并且调用函数handle_keychord来对这些输入事件进行处理。
E. 回收僵尸进程。我们知道,在Linux内核中,如果父进程不等待子进程结束就退出,那么当子进程结束的时候,就会变成一个僵尸进程,从而占用系统的资源。为了回收这些僵尸进程,init进程会安装一个SIGCHLD信号接收器。当那些父进程已经退出了的子进程退出的时候,内核就会发出一个SIGCHLD信号给init进程。init进程可以通过一个socket(通过调用函数get_signal_fd可以获得它的文件描述符)来将接收到的SIGCHLD信号读取回来,并且调用函数handle_signal来对接收到的SIGCHLD信号进行处理,即回收那些已经变成了僵尸的子进程。

[cpp] view plain copy
  1. for(;;) {  
  2.     int nr, i, timeout = -1;  
  3.     execute_one_command();①  
  4.     restart_processes();②  
  5.     if (!property_set_fd_init && get_property_set_fd() > 0) {③  
  6.         ufds[fd_count].fd = get_property_set_fd();  
  7.         ufds[fd_count].events = POLLIN;  
  8.         ufds[fd_count].revents = 0;  
  9.         fd_count++;  
  10.         property_set_fd_init = 1;  
  11.     }  
  12.     if (!signal_fd_init && get_signal_fd() > 0) {  
  13.         ufds[fd_count].fd = get_signal_fd();  
  14.         ufds[fd_count].events = POLLIN;  
  15.         ufds[fd_count].revents = 0;  
  16.         fd_count++;  
  17.         signal_fd_init = 1;  
  18.     }  
  19.     if (!keychord_fd_init && get_keychord_fd() > 0) {  
  20.         ufds[fd_count].fd = get_keychord_fd();  
  21.         ufds[fd_count].events = POLLIN;  
  22.         ufds[fd_count].revents = 0;  
  23.         fd_count++;  
  24.         keychord_fd_init = 1;  
  25.     }  
  26.   
  27.     if (process_needs_restart) {④  
  28.         timeout = (process_needs_restart - gettime()) * 1000;  
  29.         if (timeout < 0)  
  30.             timeout = 0;  
  31.     }  
  32.   
  33.     if (!action_queue_empty() || cur_action)  
  34.         timeout = 0;  
  35.   
  36. #if BOOTCHART  
  37.     if (bootchart_count > 0) {  
  38.         if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)  
  39.             timeout = BOOTCHART_POLLING_MS;  
  40.         if (bootchart_step() < 0 || --bootchart_count == 0) {  
  41.             bootchart_finish();  
  42.             bootchart_count = 0;  
  43.         }  
  44.     }  
  45. #endif  
  46.   
  47.     nr = poll(ufds, fd_count, timeout);⑤  
  48.     if (nr <= 0)  
  49.         continue;  
  50.   
  51.     for (i = 0; i < fd_count; i++) {  
  52.         if (ufds[i].revents == POLLIN) {  
  53.             if (ufds[i].fd == get_property_set_fd())  
  54.                 handle_property_set_fd();⑥  
  55.             else if (ufds[i].fd == get_keychord_fd())  
  56.                 handle_keychord();⑦  
  57.             else if (ufds[i].fd == get_signal_fd())  
  58.                 handle_signal();⑧  
  59.         }  
  60.     }  
  61. }  
1).execute_one_command(void)

从待执行队列action_queue中取出一个Action来执行,并且将已经执行完的Action从action_queue队列中移除。

[cpp] view plain copy
  1. void execute_one_command(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {  
  6.         cur_action = action_remove_queue_head();  
  7.         cur_command = NULL;  
  8.         if (!cur_action)  
  9.             return;  
  10.         INFO("processing action %p (%s)\n", cur_action, cur_action->name);  
  11.         cur_command = get_first_command(cur_action);  
  12.     } else {  
  13.         cur_command = get_next_command(cur_action, cur_command);  
  14.     }  
  15.   
  16.     if (!cur_command)  
  17.         return;  
  18.   
  19.     ret = cur_command->func(cur_command->nargs, cur_command->args);  
  20.     INFO("command '%s' r=%d\n", cur_command->args[0], ret);  
  21. }  
      1) 从action_queue取下struct action *act赋给cur_action;

      2) 从cur_action获得struct command *赋给cur_command;

      3) 执行cur_command->func(cur_command->nargs, cur_command->args)

2).服务重启

当内存不足时,Android系统会自动杀死一下进程来释放空间,所以当某些重要的服务被杀,同时该服务进程并未设置为oneshot,则必须重新启动该服务进程。

[cpp] view plain copy
  1. static void restart_processes()  
  2. {  
  3.     process_needs_restart = 0;  
  4.     service_for_each_flags(SVC_RESTARTING,restart_service_if_needed);  
  5. }  
调用函数service_for_each_flags来循环遍历服务链表,查找标志位为SVC_RESTARTING的服务,当该服务进程死亡时,init进程监控到进程死亡事件,在处理该事件的时候会为该服务进程设置SVC_RESTARTING标志位,并调用restart_service_if_needed函数重启服务

[cpp] view plain copy
  1. void service_for_each_flags(unsigned matchflags,  
  2.                             void (*func)(struct service *svc))  
  3. {  
  4.     struct listnode *node;  
  5.     struct service *svc;  
  6.     list_for_each(node, &service_list) {  
  7.         svc = node_to_item(node, struct service, slist);  
  8.         if (svc->flags & matchflags) {  
  9.             func(svc);  
  10.         }  
  11.     }  
  12. }  
从服务链表中查找具有相同标志位的服务,并调用回调函数进行处理,对于具有SVC_RESTARTING标志的服务,说明该服务需要重启,

[cpp] view plain copy
  1. static void restart_service_if_needed(struct service *svc)  
  2. {  
  3.     time_t next_start_time = svc->time_started + 5;  
  4.   
  5.     if (next_start_time <= gettime()) {  
  6.         svc->flags &= (~SVC_RESTARTING);  
  7.         service_start(svc, NULL);  
  8.         return;  
  9.     }  
  10.   
  11.     if ((next_start_time < process_needs_restart) ||  
  12.         (process_needs_restart == 0)) {  
  13.         process_needs_restart = next_start_time;  
  14.     }  
  15. }  
当当前时间大于服务启动时间时,清楚服务重启标志并启动该服务,service_start()函数已经在前面简单介绍过了。那服务重启标志位是在哪里设置的呢?在接下来介绍的init进程处理子进程死亡信号SIGCHLD时会进行详细介绍。

3.设置句柄池

[cpp] view plain copy
  1. if (!property_set_fd_init && get_property_set_fd() > 0) {  
  2.     ufds[fd_count].fd = get_property_set_fd();  
  3.     ufds[fd_count].events = POLLIN;  
  4.     ufds[fd_count].revents = 0;  
  5.     fd_count++;  
  6.     property_set_fd_init = 1;  
  7. }  
  8. if (!signal_fd_init && get_signal_fd() > 0) {  
  9.     ufds[fd_count].fd = get_signal_fd();  
  10.     ufds[fd_count].events = POLLIN;  
  11.     ufds[fd_count].revents = 0;  
  12.     fd_count++;  
  13.     signal_fd_init = 1;  
  14. }  
  15. if (!keychord_fd_init && get_keychord_fd() > 0) {  
  16.     ufds[fd_count].fd = get_keychord_fd();  
  17.     ufds[fd_count].events = POLLIN;  
  18.     ufds[fd_count].revents = 0;  
  19.     fd_count++;  
  20.     keychord_fd_init = 1;  
  21. }  
get_property_set_fd()函数用于得到属性socket设备/dev/socket/property_service的句柄property_set_fd,并添加到句柄次ufds中;property_set_fd_init标志位的设置是为了在下一次循环中不在执行这部分代码,从而避免了重复添加句柄的工作。

get_signal_fd()函数用于得到安装信号处理时创建的socket对的接收端句柄signal_recv_fd;signal_fd_init和property_set_fd_init的作用相同;

get_keychord_fd()函数用于得到设备/dev/keychord的句柄keychord_fd,keychord_fd_init和property_set_fd_init的作用相同;

4.计算超时时间

系统调用poll在监控句柄池时,如果超时时间到了或者有事件发生时,才会返回,如果超时时间被设置为-1时,只有事件发生才会返回。

[cpp] view plain copy
  1. if (process_needs_restart) {  
  2.     timeout = (process_needs_restart - gettime()) * 1000;  
  3.     if (timeout < 0)  
  4.         timeout = 0;  
  5. }  
  6.   
  7. if (!action_queue_empty() || cur_action)  
  8.     timeout = 0;  
如果待执行队列不为空,并且当前Action也不为空,这设置timeout为0,这样poll就不会阻塞,init进程就可以循环执行队列action_queue中的Action

5.事件监控

[cpp] view plain copy
  1. nr = poll(ufds, fd_count, timeout);  
  2. if (nr <= 0)  
  3.     continue;  
如果被监控的句柄池中的句柄没有事件发生,但超时时间已到,则返回-1,此时代码不往下执行,而是继续循环执行队列action_queue中的Action,及重启必要的服务。

6.事件处理

当监控的句柄池中的句柄发生了某些事件时,返回事件发生对应的句柄,从而进入该句柄对应的事件处理函数中。

[cpp] view plain copy
  1. for (i = 0; i < fd_count; i++) {  
  2.     if (ufds[i].revents == POLLIN) {  
  3.         if (ufds[i].fd == get_property_set_fd())  
  4.             handle_property_set_fd();  
  5.         else if (ufds[i].fd == get_keychord_fd())  
  6.             handle_keychord();  
  7.         else if (ufds[i].fd == get_signal_fd())  
  8.             handle_signal();  
  9.     }  
  10. }  
这里有三类事件:

1.属性设置事件;

2.键盘组合事件;

3.子进程死亡信号事件;

对于属性设置事件处理handle_property_set_fd(),请查看Android 系统属性SystemProperty分析。由于Android系统暂时未使用keychord机制,因此这里不详细介绍。

[cpp] view plain copy
  1. void handle_keychord()  
  2. {  
  3.     struct service *svc;  
  4.     const char* debuggable;  
  5.     const char* adb_enabled;  
  6.     int ret;  
  7.     __u16 id;  
  8.   
  9.     // only handle keychords if ro.debuggable is set or adb is enabled.  
  10.     // the logic here is that bugreports should be enabled in userdebug or eng builds  
  11.     // and on user builds for users that are developers.  
  12.     debuggable = property_get("ro.debuggable");  
  13.     adb_enabled = property_get("init.svc.adbd");  
  14.     ret = read(keychord_fd, &id, sizeof(id));  
  15.     if (ret != sizeof(id)) {  
  16.         ERROR("could not read keychord id\n");  
  17.         return;  
  18.     }  
  19.     //只有在调试模式下才使用  
  20.     if ((debuggable && !strcmp(debuggable, "1")) ||  
  21.         (adb_enabled && !strcmp(adb_enabled, "running"))) {  
  22.         svc = service_find_by_keychord(id); //根据keychord_id查找指定的服务  
  23.         if (svc) {  
  24.             INFO("starting service %s from keychord\n", svc->name); //通过发送组合键消息来启动某些服务  
  25.             service_start(svc, NULL);  
  26.         } else {  
  27.             ERROR("service for keychord %d not found\n", id);  
  28.         }  
  29.     }  
  30. }  
keychord机制就是为服务配置指定的组合键,可以通过该组合键来启动对应的服务。


当init进程的某个子进程终止时,会对系统的运行产生影响,因此init进程需要重新启动他们。当init的子进程意外终止时,会向父进程init进程传递SIGCHLD信号,init进程接收到该信号时,预先安装的handler将被调用,将SIGCHLD信号的编号写入socket对的一端,在socket另一端通过poll系统调用监控到事件的发生,将调用子进程死亡事件处理函数。



当init子进程终止时,init进程会接收到SIGCHLD信号,前面已经介绍了init进程首先安装了信号处理器,因此当接收到SIGCHLD信号时,init进程会调用与该信号相对应的处理函数sigchld_handler:

[cpp] view plain copy
  1. static void sigchld_handler(int s)  
  2. {  
  3.     write(signal_fd, &s, 1);  
  4. }  
参数s用来接收SIGCHLD信号的编号,该函数仅仅将信号编号写入socket对的一端signal_fd中,由于signal_fd与signal_recv_fd是一对已连接的socket,因此当向signal_fd写入信号编号时,信号编号被传递到接收端signal_recv_fd中,由于signal_recv_fd被添加到了监控句柄池中并被注册到了poll系统调用中,因此信号编号的写入将触发poll函数返回并调用handle_signal()函数来处理信号事件。

[cpp] view plain copy
  1. void handle_signal(void)  
  2. {  
  3.     char tmp[32];  
  4.     //读取socket接收端的数据  
  5.     /* we got a SIGCHLD - reap and restart as needed */  
  6.     read(signal_recv_fd, tmp, sizeof(tmp));  
  7.     while (!wait_for_one_process(0))  
  8.         ;  
  9. }  
该函数首先从signal_recv_fd读取发送过来的信号编号,表示该事件得到处理,避免重复处理该信号事件,然后循环调用wait_for_one_process函数,直到wait_for_one_process函数返回非0,wait_for_one_process函数在产生SIGCHLD信号的进程服务列表中,检查进程的设置选项,若选项没有配置oneshot(SVC_ONE_SHOT)则设置重启选项(SVC_RESTARTING),oneshot选项定义在init.rc文件的service部分中,若进程带有oneshot选项,进程终止时不会被重启。

[cpp] view plain copy
  1. static int wait_for_one_process(int block)  
  2. {   //block = 0 -->false  
  3.     pid_t pid;  
  4.     int status;  
  5.     struct service *svc;  
  6.     struct socketinfo *si;  
  7.     time_t now;  
  8.     struct listnode *node;  
  9.     struct command *cmd;  
  10.     /*当进程被终止时,将发送SIGCHLD信号,waitpid()函数用来回收进程所占用的资源,第一个参 
  11.     数pid是指欲等待的子进程的识别码,设置为-1表示查看所有子进程是否发出SIGCHIL信号,第二 
  12.     个参数status用于返回子进程的结束状态;第三个参数决定waitpid()函数是否应用阻塞处理方式。 
  13.     waitpid()函数返回产生SIGCHID信号的进程pid */  
  14.     while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );  
  15.     //正常情况下返回的死亡进程pid大于0,因此wait_for_one_process的返回值正常情况下为0  
  16.     if (pid <= 0) return -1;  
  17.     INFO("waitpid returned pid %d, status = %08x\n", pid, status);  
  18.     //用于根据pid值在服务链表中查找对应的服务  
  19.     svc = service_find_by_pid(pid);  
  20.     if (!svc) {  
  21.         ERROR("untracked pid %d exited\n", pid);  
  22.         return 0;  
  23.     }  
  24.       
  25.     NOTICE("process '%s', pid %d exited\n", svc->name, pid);  
  26.     /* 检查服务是否设置了oneshot标志,SVC_ONESHOT表示进程仅运行一次,如果没有设置SVC_ONESHOT标志, 
  27.     表示需要重启该服务进程,首先将该服务进程组下的所有子进程杀死 */  
  28.     if (!(svc->flags & SVC_ONESHOT)) {  
  29.         kill(-pid, SIGKILL);  
  30.         NOTICE("process '%s' killing any children in process group\n", svc->name);  
  31.     }  
  32.   
  33.     /* 删除该服务进程下的创建的所有socket  */  
  34.     for (si = svc->sockets; si; si = si->next) {  
  35.         char tmp[128];  
  36.         snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);  
  37.         unlink(tmp);  
  38.     }  
  39.     //设置服务的pid为0 ,并清除SVC_RUNNING标志  
  40.     svc->pid = 0;  
  41.     svc->flags &= (~SVC_RUNNING);  
  42.   
  43.     /* 如果设置了SVC_ONESHOT标志,表示服务只能运行一次,因此设置表示位SVC_DISABLED */  
  44.     if (svc->flags & SVC_ONESHOT) {  
  45.         svc->flags |= SVC_DISABLED;  
  46.     }  
  47.   
  48.     /* 判断服务标志是否设置了SVC_DISABLED 或 SVC_RESET 对于设置了这两种标志的进程是不能重启的 */  
  49.     if (svc->flags & (SVC_DISABLED | SVC_RESET) )  {  
  50.         //设置进程运行状态属性值为stopped  
  51.         notify_service_state(svc->name, "stopped");  
  52.         return 0;  
  53.     }  
  54.   
  55.     now = gettime();  
  56.     //如果死亡的服务进程是系统关键进程,则直接重启手机  
  57.     if (svc->flags & SVC_CRITICAL) {  
  58.         if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {  
  59.             if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {  
  60.                 ERROR("critical process '%s' exited %d times in %d minutes; "  
  61.                       "rebooting into recovery mode\n", svc->name,  
  62.                       CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);  
  63.                 //手机重启  
  64.                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery");  
  65.                 return 0;  
  66.             }  
  67.         } else {  
  68.             svc->time_crashed = now;  
  69.             svc->nr_crashed = 1;  
  70.         }  
  71.     }  
  72.     //设置服务进程标志SVC_RESTARTING,在restart_processes()函数中会重启持有SVC_RESTARTING  
  73.     svc->flags |= SVC_RESTARTING;  
  74.   
  75.     /* 运行该service下所有Execute all onrestart commands for this service. */  
  76.     list_for_each(node, &svc->onrestart.commands) {  
  77.         cmd = node_to_item(node, struct command, clist);  
  78.         cmd->func(cmd->nargs, cmd->args);   
  79.     }  
  80.     //设置进程运行状态属性值为stopped  
  81.     notify_service_state(svc->name, "restarting");  
  82.     return 0;  
  83. }