Android init 进程源码分析

基于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”字段信息写入;“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文件的内容,并查找格式为: = 的字串,调用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/ 0770 system   
  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..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  after binding the port.  
  314.     group  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   
  322.     group  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主要包括:

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

Service

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

service [ ]*
     

Option

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

Command

comand主要包括:

 exec [ ]*执行一个指定的程序
 export 设置一个全局变量
 ifup 使网络接口连接
 import 引入其他的配置文件
 hostname 设置主机名
 chdir 切换工作目录
 chmod 设置访问权限
 chown 设置用户和组
 chroot 设置根目录
 class_start 启动类中的service
 class_stop 停止类中的service
 domainname 设置域名
 insmod 安装模块
 mkdir [mode] [owner] [group] 创建一个目录,并可以指定权限,用户和组
 mount

[ ]* 加载指定设备到目录下 包括"ro", "rw", "remount", "noatime"
 setprop 设置系统属性
 setrlimit 设置资源访问权限
 start 开启服务
 stop 停止服务
 symlink 创建一个动态链接
 sysclktz 设置系统时钟
 trigger 触发事件
 write [ ]* 向路径的文件写入多个

Properties(属性)

Init更新一些系统属性以提供对正在发生的事件的监控能力:
       init.action 此属性值为正在被执行的action的名字,如果没有则为""。
       init.command  此属性值为正在被执行的command的名字,如果没有则为""。
       init.svc. 名为的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.         

你可能感兴趣的:(Android init 进程源码分析)