init进程【1】——init启动过程

来源:互联网 发布:淘宝微淘在哪里 编辑:程序博客网 时间:2024/06/05 05:42

【转载自这里】:http://blog.csdn.net/zhgxhuaa


init启动过程

众所周知,Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。Android是基于Linux的操作系统,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。下面先简单的看一下init进程的启动过程。


@/kernel/goodfish/init/main.c

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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.   
  20.     do_pre_smp_initcalls();  
  21.     lockup_detector_init();  
  22.   
  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.   
  39.     if (!ramdisk_execute_command)  
  40.         ramdisk_execute_command = "/init";  
  41.   
  42.     if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {  
  43.         ramdisk_execute_command = NULL;  
  44.         prepare_namespace();  
  45.     }  
  46.   
  47.     /* 
  48.      * Ok, we have completed the initial bootup, and 
  49.      * we're essentially up and running. Get rid of the 
  50.      * initmem segments and start the user-mode stuff.. 
  51.      */  
  52.   
  53.     init_post();  
  54.     return 0;  
  55. }  
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /* This is a non __init function. Force it to be noinline otherwise gcc 
  2.  * makes it inline to init() and it becomes part of init.text section 
  3.  */  
  4. static noinline int init_post(void)  
  5. {  
  6.     /* need to finish all async __init code before freeing the memory */  
  7.     async_synchronize_full();  
  8.     free_initmem();  
  9.     mark_rodata_ro();  
  10.     system_state = SYSTEM_RUNNING;  
  11.     numa_default_policy();  
  12.   
  13.   
  14.     current->signal->flags |= SIGNAL_UNKILLABLE;  
  15.   
  16.     if (ramdisk_execute_command) {  
  17.         run_init_process(ramdisk_execute_command);  
  18.         printk(KERN_WARNING "Failed to execute %s\n",  
  19.                 ramdisk_execute_command);  
  20.     }  
  21.   
  22.     /* 
  23.      * We try each of these until one succeeds. 
  24.      * 
  25.      * The Bourne shell can be used instead of init if we are 
  26.      * trying to recover a really broken machine. 
  27.      */  
  28.     if (execute_command) {  
  29.         run_init_process(execute_command);  
  30.         printk(KERN_WARNING "Failed to execute %s.  Attempting "  
  31.                     "defaults...\n", execute_command);  
  32.     }  
  33.     run_init_process("/sbin/init");  
  34.     run_init_process("/etc/init");  
  35.     run_init_process("/bin/init");  
  36.     run_init_process("/bin/sh");  
  37.   
  38.     panic("No init found.  Try passing init= option to kernel. "  
  39.           "See Linux Documentation/init.txt for guidance.");  
  40. }  
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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. }  

在init_post()中会判断execute_command是否为空,如果不为空则执行run_init_process调用。execute_command的赋值在init_setup()中,所以这里应该注意在设置内核启动选项时,应设置为“ init=/init”,以便正常启动init进程,因为编译完Android后生成的文件系统中,init位于最顶层目录。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">static const char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };</span>  

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. static int __init init_setup(char *str)  
  2. {  
  3.     unsigned int i;  
  4.   
  5.     execute_command = str;  
  6.     /* 
  7.      * In case LILO is going to boot us with default command line, 
  8.      * it prepends "auto" before the whole cmdline which makes 
  9.      * the shell think it should execute a script with such name. 
  10.      * So we ignore all arguments entered _before_ init=... [MJ] 
  11.      */  
  12.     for (i = 1; i < MAX_INIT_ARGS; i++)  
  13.         argv_init[i] = NULL;  
  14.     return 1;  
  15. }  
  16. __setup("init=", init_setup);  
当根目录中不存在init时,或者未指定启动项“init=”时,内核会到/sbin、/etc、/bin目录下查找init。

了解了init进程的启动过程后,接下来看一下init进程都干了些什么?Android中的init进程与Linux不同,其职责可以归结如下:

  • 作为守护进程
  • 解析和执行init.rc文件
  • 生成设备驱动节点
  • 属性服务

init源码分析

init进程的入口函数是main,它的代码如下:

@/system/core/init/init.c

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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.   
  14.     //启动ueventd  
  15.     if (!strcmp(basename(argv[0]), "ueventd"))  
  16.         return ueventd_main(argc, argv);  
  17.   
  18.   
  19.     //启动watchdogd  
  20.     if (!strcmp(basename(argv[0]), "watchdogd"))  
  21.         return watchdogd_main(argc, argv);  
  22.   
  23.   
  24.     /* clear the umask */  
  25.     umask(0);  
  26.   
  27.   
  28.         /* Get the basic filesystem setup we need put 
  29.          * together in the initramdisk on / and then we'll 
  30.          * let the rc file figure out the rest. 
  31.          */  
  32.     //创建并挂在启动所需的文件目录  
  33.     mkdir("/dev", 0755);  
  34.     mkdir("/proc", 0755);  
  35.     mkdir("/sys", 0755);  
  36.   
  37.   
  38.     mount("tmpfs""/dev""tmpfs", MS_NOSUID, "mode=0755");  
  39.     mkdir("/dev/pts", 0755);  
  40.     mkdir("/dev/socket", 0755);  
  41.     mount("devpts""/dev/pts""devpts", 0, NULL);  
  42.     mount("proc""/proc""proc", 0, NULL);  
  43.     mount("sysfs""/sys""sysfs", 0, NULL);  
  44.   
  45.   
  46.         /* indicate that booting is in progress to background fw loaders, etc */  
  47.     close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));//检测/dev/.booting文件是否可读写和创建  
  48.   
  49.   
  50.         /* We must have some place other than / to create the 
  51.          * device nodes for kmsg and null, otherwise we won't 
  52.          * be able to remount / read-only later on. 
  53.          * Now that tmpfs is mounted on /dev, we can actually 
  54.          * talk to the outside world. 
  55.          */  
  56.     open_devnull_stdio();//重定向标准输入/输出/错误输出到/dev/_null_  
  57.     klog_init();//log初始化  
  58.     property_init();//属性服务初始化  
  59.   
  60.   
  61.     //从/proc/cpuinfo中读取Hardware名,在后面的mix_hwrng_into_linux_rng_action函数中会将hardware的值设置给属性ro.hardware  
  62.     get_hardware_name(hardware, &revision);  
  63.   
  64.   
  65.     //导入并设置内核变量  
  66.     process_kernel_cmdline();  
  67.   
  68.   
  69.     //selinux相关,暂不分析  
  70.     union selinux_callback cb;  
  71.     cb.func_log = klog_write;  
  72.     selinux_set_callback(SELINUX_CB_LOG, cb);  
  73.   
  74.   
  75.     cb.func_audit = audit_callback;  
  76.     selinux_set_callback(SELINUX_CB_AUDIT, cb);  
  77.   
  78.   
  79.     selinux_initialize();  
  80.     /* These directories were necessarily created before initial policy load 
  81.      * and therefore need their security context restored to the proper value. 
  82.      * This must happen before /dev is populated by ueventd. 
  83.      */  
  84.     restorecon("/dev");  
  85.     restorecon("/dev/socket");  
  86.     restorecon("/dev/__properties__");  
  87.     restorecon_recursive("/sys");  
  88.   
  89.   
  90.     is_charger = !strcmp(bootmode, "charger");//关机充电相关,暂不做分析  
  91.   
  92.   
  93.     INFO("property init\n");  
  94.     if (!is_charger)  
  95.         property_load_boot_defaults();  
  96.   
  97.   
  98.     INFO("reading config file\n");  
  99.     init_parse_config_file("/init.rc");//解析init.rc配置文件  
  100.   
  101.   
  102.     /* 
  103.      * 解析完init.rc后会得到一系列的action等,下面的代码将执行处于early-init阶段的action。 
  104.      * init将action按照执行时间段的不同分为early-init、init、early-boot、boot。 
  105.      * 进行这样的划分是由于有些动作之间具有依赖关系,某些动作只有在其他动作完成后才能执行,所以就有了先后的区别。 
  106.      * 具体哪些动作属于哪个阶段是在init.rc中的配置决定的 
  107.      */  
  108.     action_for_each_trigger("early-init", action_add_queue_tail);  
  109.   
  110.   
  111.     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  112.     queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  113.     queue_builtin_action(keychord_init_action, "keychord_init");  
  114.     queue_builtin_action(console_init_action, "console_init");  
  115.   
  116.   
  117.     /* execute all the boot actions to get us started */  
  118.     action_for_each_trigger("init", action_add_queue_tail);  
  119.   
  120.   
  121.     /* skip mounting filesystems in charger mode */  
  122.     if (!is_charger) {  
  123.         action_for_each_trigger("early-fs", action_add_queue_tail);  
  124.         action_for_each_trigger("fs", action_add_queue_tail);  
  125.         action_for_each_trigger("post-fs", action_add_queue_tail);  
  126.         action_for_each_trigger("post-fs-data", action_add_queue_tail);  
  127.     }  
  128.   
  129.   
  130.     /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random 
  131.      * wasn't ready immediately after wait_for_coldboot_done 
  132.      */  
  133.     queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  134.   
  135.   
  136.     queue_builtin_action(property_service_init_action, "property_service_init");  
  137.     queue_builtin_action(signal_init_action, "signal_init");  
  138.     queue_builtin_action(check_startup_action, "check_startup");  
  139.   
  140.   
  141.     if (is_charger) {  
  142.         action_for_each_trigger("charger", action_add_queue_tail);  
  143.     } else {  
  144.         action_for_each_trigger("early-boot", action_add_queue_tail);  
  145.         action_for_each_trigger("boot", action_add_queue_tail);  
  146.     }  
  147.   
  148.   
  149.         /* run all property triggers based on current state of the properties */  
  150.     queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  151.   
  152.   
  153.   
  154.   
  155. #if BOOTCHART  
  156.     queue_builtin_action(bootchart_init_action, "bootchart_init");  
  157. #endif  
  158.   
  159.   
  160.     for(;;) {//init进入无限循环  
  161.         int nr, i, timeout = -1;  
  162.         //检查action_queue列表是否为空。如果不为空则移除并执行列表头中的action  
  163.         execute_one_command();  
  164.         restart_processes();//重启已经死去的进程  
  165.   
  166.   
  167.         if (!property_set_fd_init && get_property_set_fd() > 0) {  
  168.             ufds[fd_count].fd = get_property_set_fd();  
  169.             ufds[fd_count].events = POLLIN;  
  170.             ufds[fd_count].revents = 0;  
  171.             fd_count++;  
  172.             property_set_fd_init = 1;  
  173.         }  
  174.         if (!signal_fd_init && get_signal_fd() > 0) {  
  175.             ufds[fd_count].fd = get_signal_fd();  
  176.             ufds[fd_count].events = POLLIN;  
  177.             ufds[fd_count].revents = 0;  
  178.             fd_count++;  
  179.             signal_fd_init = 1;  
  180.         }  
  181.         if (!keychord_fd_init && get_keychord_fd() > 0) {  
  182.             ufds[fd_count].fd = get_keychord_fd();  
  183.             ufds[fd_count].events = POLLIN;  
  184.             ufds[fd_count].revents = 0;  
  185.             fd_count++;  
  186.             keychord_fd_init = 1;  
  187.         }  
  188.   
  189.   
  190.         if (process_needs_restart) {  
  191.             timeout = (process_needs_restart - gettime()) * 1000;  
  192.             if (timeout < 0)  
  193.                 timeout = 0;  
  194.         }  
  195.   
  196.   
  197.         if (!action_queue_empty() || cur_action)  
  198.             timeout = 0;  
  199.   
  200.   
  201. #if BOOTCHART  
  202.         if (bootchart_count > 0) {  
  203.             if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)  
  204.                 timeout = BOOTCHART_POLLING_MS;  
  205.             if (bootchart_step() < 0 || --bootchart_count == 0) {  
  206.                 bootchart_finish();  
  207.                 bootchart_count = 0;  
  208.             }  
  209.         }  
  210. #endif  
  211.         //等待事件发生  
  212.         nr = poll(ufds, fd_count, timeout);  
  213.         if (nr <= 0)  
  214.             continue;  
  215.   
  216.   
  217.         for (i = 0; i < fd_count; i++) {  
  218.             if (ufds[i].revents == POLLIN) {  
  219.                 if (ufds[i].fd == get_property_set_fd())//处理属性服务事件  
  220.                     handle_property_set_fd();  
  221.                 else if (ufds[i].fd == get_keychord_fd())//处理keychord事件  
  222.                     handle_keychord();  
  223.                 else if (ufds[i].fd == get_signal_fd())//处理  
  224.                     handle_signal();//处理SIGCHLD信号  
  225.             }  
  226.         }  
  227.     }  
  228.   
  229.   
  230.     return 0;  
  231. }  

main函数分析:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1.   if (!strcmp(basename(argv[0]), "ueventd"))  
  2.         return ueventd_main(argc, argv);  

main函数一开始就会判断参数argv[0]的值是否等于“ueventd”,如果是就调用ueventd进程的入口函数ueventd_main()启动ueventd进程。这是怎么回事呢?当前正在启动的进程不是init吗?它的名称怎么可能会等于“ueventd”?所以这里有必要看一下ueventd的启动过程,ueventd是在init.rc中被启动的。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. on boot  
  2. service ueventd /sbin/ueventd  
  3.     class core  
  4.     critical  
  5.     seclabel u:r:ueventd:s0  
可以看出ueventd可执行文件位于/sbin/ueventd,在观察了/sbin/ueventd后我们发现,它只不过是是可执行文件/init的一个符号链接文件,即应用程序ueventd和init运行的是同一个可执行文件。

所以,整个过程是这样的:内核启动完成之后,可执行文件/init首先会被执行,即init进程会首先被启动。init进程在启动的过程中,会对启动脚本/init.rc进行解析。在启动脚本/init.rc中,配置了一个ueventd进程,它对应的可执行文件为/sbin/ueventd,即ueventd进程加载的可执行文件也为/init(此时init中main函数的参数argv[0] = “/sbin/ueventd”)。因此,通过判断参数argv[0]的值,就可以知道当前正在启动的是init进程还是ueventd进程。      

PS:ueventd是一个守护进程,主要作用是接收uevent来创建或删除/dev/xxx(设备节点),其实现位于@system/core/init/ueventd.c中。ueventd进程会通过一个socket接口来和内核通信,以便可以监控系统设备事件。


在开始所有的工作之前,main进程首先做的是创建并挂载启动所需的(其他的会在解析init.rc时创建)文件目录,如下所示:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1.     /* Get the basic filesystem setup we need put 
  2.      * together in the initramdisk on / and then we'll 
  3.      * let the rc file figure out the rest. 
  4.      */  
  5. //创建并挂在启动所需的文件目录  
  6. mkdir("/dev", 0755);  
  7. mkdir("/proc", 0755);  
  8. mkdir("/sys", 0755);  
  9.   
  10. mount("tmpfs""/dev""tmpfs", MS_NOSUID, "mode=0755");  
  11. mkdir("/dev/pts", 0755);  
  12. mkdir("/dev/socket", 0755);  
  13. mount("devpts""/dev/pts""devpts", 0, NULL);  
  14. mount("proc""/proc""proc", 0, NULL);  
  15. mount("sysfs""/sys""sysfs", 0, NULL);  
说明:

tmpfs是一种虚拟内存的文件系统,典型的tmpfs文件系统完全驻留在RAM中,读写速度远快于内存或硬盘文件系统。

/dev目录保存着硬件设备访问所需要的设备驱动程序。在Android中,将相关目录作用于tmpfs,可以大幅度提高设备访问的速度。

devpts是一种虚拟终端文件系统。

proc是一种虚拟文件系统,只存在于内存中,不占用外存空间。借助此文件系统,应用程序可以与内核内部数据结构进行交互。

sysfs是一种特殊的文件系统,在Linux 2.6中引入,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息,将proc、devpts、devfs三种文件系统统一起来。
编译Android系统源码时,在生成的根文件系统中,并不存在/dev、/proc、/sys这类目录,它们是系统运行时的目录,有init进程在运行中生成,当系统终止时,它们就会消失。上面的代码所形成的的文件层次结构为:



[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">        /* We must have some place other than / to create the 
  2.          * device nodes for kmsg and null, otherwise we won't be able to remount / read-only later on. 
  3.          * Now that tmpfs is mounted on /dev, we can actually talk to the outside world. 
  4.          */  
  5.     open_devnull_stdio();//重定向标准输入/输出/错误输出到/dev/_null_</span>  
open_devnull_stdio()函数的作用是重定向标准输入/输出/错误输出到/dev/_null_,至于为什么要重定向的原因在注释中已经写明。open_devnull_stdio()的实现如下:

@system/core/init/util.c

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void open_devnull_stdio(void)  
  2. {  
  3.     int fd;  
  4.     static const char *name = "/dev/__null__";  
  5.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {  
  6.         fd = open(name, O_RDWR);  
  7.         unlink(name);  
  8.         if (fd >= 0) {  
  9.             dup2(fd, 0);  
  10.             dup2(fd, 1);  
  11.             dup2(fd, 2);  
  12.             if (fd > 2) {  
  13.                 close(fd);  
  14.             }  
  15.             return;  
  16.         }  
  17.     }  
  18.   
  19.     exit(1);  
  20. }  


[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">klog_init();//log初始化</span>  
klog_init()用于初始化log,通过其实现可以看出log被打印到/dev/__kmsg__文件中。主要在代码中最后通过fcntl和unlink使得/dev/__kmsg__不可被访问,这就保证了只有log程序才可以访问。
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void klog_init(void)  
  2. {  
  3.     static const char *name = "/dev/__kmsg__";  
  4.   
  5.     if (klog_fd >= 0) return/* Already initialized */  
  6.   
  7.     if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {  
  8.         klog_fd = open(name, O_WRONLY);  
  9.         if (klog_fd < 0)  
  10.                 return;  
  11.         fcntl(klog_fd, F_SETFD, FD_CLOEXEC);  
  12.         unlink(name);  
  13.     }  
  14. }  


[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. property_init  
属性服务初始化,这里先不深究,接下来会单独分析。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">    </span>//从/proc/cpuinfo中读取Hardware名,在后面的mix_hwrng_into_linux_rng_action函数中会将hardware的值设置给属性ro.hardware  
  2.     get_hardware_name(hardware, &revision);  
get_hardware_name()函数的作用是从/proc/cpuinfo中获取Hardware和Revision的值,并保持到全局变量hardware和revision中。

下面的截图是在我的手机上的CPU info信息:


这里获取hardware信息有什么用呢?在main()函数后面的代码中,我们可以看见这样一句:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1.     //导入并设置内核变量  
  2.     process_kernel_cmdline();  
下面看一下process_kernel_cmdline的实现:

@system/core/init/init.c

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  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. }  
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. static void export_kernel_boot_props(void)  
  2. {  
  3.     char tmp[PROP_VALUE_MAX];  
  4.       
  5.     ......  
  6.   
  7.     /* if this was given on kernel command line, override what we read 
  8.      * before (e.g. from /proc/cpuinfo), if anything */  
  9.     ret = property_get("ro.boot.hardware", tmp);  
  10.     if (ret)  
  11.         strlcpy(hardware, tmp, sizeof(hardware));  
  12.     property_set("ro.hardware", hardware);  
  13.   
  14.     snprintf(tmp, PROP_VALUE_MAX, "%d", revision);  
  15.     property_set("ro.revision", tmp);  
  16.   
  17.     ......  
  18. }  
process_kernel_cmdline()函数用于导入和设置一些内核变量,在export_kernel_boot_props()中我们看见将hardware的值赋值给了属性"ro.hardware"。那这个赋值又是干什么的呢?我们再看一下main()函数,在解析init.rc配置文件的时候,有没有发现少了点什么?

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. INFO("reading config file\n");  
  2. init_parse_config_file("/init.rc");//解析init.rc配置文件  
是的,在以前比较老的代码中(例如2.3和4.0)这里除了init.rc以外还会有一个与硬件相关的rc脚本,如下:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);   
  2. init_parse_config_file(tmp);   
那现在这段代码跑去哪里了呢?我们在init.rc中找到了它:

所以,之前设置的ro.hardware的值是在这里用的,在init.rc中用来导入init.${ro.hardware}.rc脚本,然后一起进行解析。与之前相比,这里只是方式变了,本质上还是一样的。


[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. INFO("reading config file\n");  
  2. init_parse_config_file("/init.rc");//解析init.rc配置文件  
  3.   
  4. action_for_each_trigger("early-init", action_add_queue_tail);  
  5.   
  6. queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  7. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  8. queue_builtin_action(keychord_init_action, "keychord_init");  
  9. queue_builtin_action(console_init_action, "console_init");  
  10.   
  11. /* execute all the boot actions to get us started */  
  12. action_for_each_trigger("init", action_add_queue_tail);  
  13.   
  14. /* skip mounting filesystems in charger mode */  
  15. if (!is_charger) {  
  16.     action_for_each_trigger("early-fs", action_add_queue_tail);  
  17.     action_for_each_trigger("fs", action_add_queue_tail);  
  18.     action_for_each_trigger("post-fs", action_add_queue_tail);  
  19.     action_for_each_trigger("post-fs-data", action_add_queue_tail);  
  20. }  
  21.   
  22. /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random 
  23.  * wasn't ready immediately after wait_for_coldboot_done 
  24.  */  
  25. queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  26.   
  27. queue_builtin_action(property_service_init_action, "property_service_init");  
  28. queue_builtin_action(signal_init_action, "signal_init");  
  29. queue_builtin_action(check_startup_action, "check_startup");  
  30.   
  31. if (is_charger) {  
  32.     action_for_each_trigger("charger", action_add_queue_tail);  
  33. else {  
  34.     action_for_each_trigger("early-boot", action_add_queue_tail);  
  35.     action_for_each_trigger("boot", action_add_queue_tail);  
  36. }  
  37.   
  38.     /* run all property triggers based on current state of the properties */  
  39. queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");  
  40.   
  41.   
  42. BOOTCHART  
  43. queue_builtin_action(bootchart_init_action, "bootchart_init");  
  44. if  
这部分代码用于解析init.rc脚本,并触发执行解析生成的action。这部分后面单独进行分析。

在main()函数的最后,init进入了一个无限循环,并等待一些事情的发生。即:在执行完前面的初始化工作以后,init变为一个守护进程。init所关心的事件有三类:属性服务事件、keychord事件和SIGNAL,当有这三类事件发生时,init进程会调用相应的handle函数进行处理。
0 0
原创粉丝点击