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来初始化配置:
- asmlinkage void __init start_kernel(void)
- {
- .............. //执行初始化工作
- rest_init();
- }
- static noinline void __init_refok rest_init(void)
- __releases(kernel_lock)
- {
- int pid;
- rcu_scheduler_starting();
- //创建一个kernel_init进程,该进程实质上是Init进程,用于启动用户空间进程
- kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
- numa_default_policy();
- //创建一个kthreadd内核线程,用于创建新的内核进程
- pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
- rcu_read_lock();
- kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
- rcu_read_unlock();
- complete(&kthreadd_done);
- unlock_kernel();
- /*
- * The boot idle thread must execute schedule()
- * at least once to get things moving:
- */
- init_idle_bootup_task(current);
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- /* Call into cpu_idle with preempt disabled */
- cpu_idle();
- }
kernel_thread函数仅仅调用了fork系统调用来创建新的进程,创建的子进程和父进程都执行在fork函数调用之后的代码,子进程是父进程的一个拷贝。
- static int __init kernel_init(void * unused)
- {
- /*
- * Wait until kthreadd is all set-up.
- */
- wait_for_completion(&kthreadd_done);
- /*
- * init can allocate pages on any node
- */
- set_mems_allowed(node_states[N_HIGH_MEMORY]);
- /*
- * init can run on any cpu.
- */
- set_cpus_allowed_ptr(current, cpu_all_mask);
- cad_pid = task_pid(current);
- smp_prepare_cpus(setup_max_cpus);
- //执行保存在__initcall_start与__early_initcall_end之间的函数
- do_pre_smp_initcalls();
- lockup_detector_init();
- //smp 多核初始化处理
- smp_init();
- sched_init_smp();
- //内核驱动模块初始化
- do_basic_setup();
- /* Open the /dev/console on the rootfs, this should never fail */
- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
- printk(KERN_WARNING "Warning: unable to open an initial console.\n");
- (void) sys_dup(0);
- (void) sys_dup(0);
- /*
- * check if there is an early userspace init. If yes, let it do all
- * the work
- */
- if (!ramdisk_execute_command)
- ramdisk_execute_command = "/init";
- if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
- ramdisk_execute_command = NULL;
- prepare_namespace();
- }
- /*
- * Ok, we have completed the initial bootup, and
- * we're essentially up and running. Get rid of the
- * initmem segments and start the user-mode stuff..
- * 进入用户空间,执行用户空间代码
- */
- init_post();
- return 0;
- }
- static noinline int init_post(void)
- {
- /* need to finish all async __init code before freeing the memory */
- async_synchronize_full();
- free_initmem();
- mark_rodata_ro();
- system_state = SYSTEM_RUNNING;
- numa_default_policy();
- current->signal->flags |= SIGNAL_UNKILLABLE;
- //如果ramdisk_execute_command不为空,ramdisk_execute_command下的Init程序
- if (ramdisk_execute_command) {
- run_init_process(ramdisk_execute_command);
- printk(KERN_WARNING "Failed to execute %s\n",ramdisk_execute_command);
- }
- //如果execute_command不为空,execute_command下的Init程序
- if (execute_command) {
- run_init_process(execute_command);
- printk(KERN_WARNING "Failed to execute %s. Attempting ""defaults...\n", execute_command);
- }
- //如果以上路径下都没有init程序,就从/sbin、/etc、/bin三个路径下寻找init程序,同时启动一个sh进程
- run_init_process("/sbin/init");
- run_init_process("/etc/init");
- run_init_process("/bin/init");
- run_init_process("/bin/sh");
- //如果以上路径都没有找到init程序,调用内核panic
- panic("No init found. Try passing init= option to kernel. "
- "See Linux Documentation/init.txt for guidance.");
- }
- static void run_init_process(const char *init_filename)
- {
- argv_init[0] = init_filename;
- kernel_execve(init_filename, argv_init, envp_init);
- }
Init进程源码分析
Android的init进程主要功能:
1)、分析init.rc启动脚本文件,根据文件内容执行相应的功能;
2)、当一些关键进程死亡时,重启该进程;
3)、提供Android系统的属性服务;
- int main(int argc, char **argv)
- {
- int fd_count = 0;
- struct pollfd ufds[4];
- char *tmpdev;
- char* debuggable;
- char tmp[32];
- int property_set_fd_init = 0;
- int signal_fd_init = 0;
- int keychord_fd_init = 0;
- bool is_charger = false;
- if (!strcmp(basename(argv[0]), "ueventd"))
- return ueventd_main(argc, argv);
- /* clear the umask */
- umask(0);
- //挂载tmpfs,devpts,proc,sysfs 4类文件系统
- mkdir("/dev", 0755);
- mkdir("/proc", 0755);
- mkdir("/sys", 0755);
- mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
- mkdir("/dev/pts", 0755);
- mkdir("/dev/socket", 0755);
- mount("devpts", "/dev/pts", "devpts", 0, NULL);
- mount("proc", "/proc", "proc", 0, NULL);
- mount("sysfs", "/sys", "sysfs", 0, NULL);
- /* indicate that booting is in progress to background fw loaders, etc */
- close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));
- //屏蔽标准的输入输出,即标准的输入输出定向到NULL设备。
- open_devnull_stdio();
- // log 初始化
- klog_init();
- // 属性存储空间初始化
- property_init();
- //读取机器硬件名称
- get_hardware_name(hardware, &revision);
- //设置基本属性
- process_kernel_cmdline();
- #ifdef HAVE_SELINUX
- INFO("loading selinux policy\n");
- selinux_load_policy();
- #endif
- //判断当前启动模式
- is_charger = !strcmp(bootmode, "charger");
- INFO("property init\n");
- if (!is_charger)
- //读取默认的属性文件
- property_load_boot_defaults();
- //解析init.rc文件
- INFO("reading config file\n");
- init_parse_config_file("/init.rc");
- //将early-init动作添加到链表action_queue中
- action_for_each_trigger("early-init", action_add_queue_tail);
- //创建wait_for_coldboot_done 动作并添加到链表action_queue和action_list中
- queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
- //创建keychord_init动作并添加到链表action_queue和action_list中
- queue_builtin_action(keychord_init_action, "keychord_init");
- //创建console_init动作并添加到链表action_queue和action_list中
- queue_builtin_action(console_init_action, "console_init");
- //将init动作添加到链表action_queue中
- action_for_each_trigger("init", action_add_queue_tail);
- //将early-fs动作添加到链表action_queue中
- action_for_each_trigger("early-fs", action_add_queue_tail);
- //将fs动作添加到链表action_queue中
- action_for_each_trigger("fs", action_add_queue_tail);
- //将post-fs动作添加到链表action_queue中
- action_for_each_trigger("post-fs", action_add_queue_tail);
- //非充电模式下,将post-fs-data动作添加到链表action_queue中
- if (!is_charger) {
- action_for_each_trigger("post-fs-data", action_add_queue_tail);
- }
- //创建property_service_init动作并添加到链表action_queue和action_list中
- queue_builtin_action(property_service_init_action, "property_service_init");
- //创建signal_init动作并添加到链表action_queue和action_list中
- queue_builtin_action(signal_init_action, "signal_init");
- //创建check_startup动作并添加到链表action_queue和action_list中
- queue_builtin_action(check_startup_action, "check_startup");
- if (!strcmp(bootmode, "alarm")) {
- action_for_each_trigger("alarm", action_add_queue_tail);
- }
- if (is_charger) {
- //充电模式下,将charger动作添加到链表action_queue中
- action_for_each_trigger("charger", action_add_queue_tail);
- } else {
- //非充电模式下,将early-boot、boot动作添加到链表action_queue中
- action_for_each_trigger("early-boot", action_add_queue_tail);
- action_for_each_trigger("boot", action_add_queue_tail);
- }
- //创建queue_property_triggers动作并添加到链表action_queue和action_list中
- queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
- #if BOOTCHART
- //如果BOOTCHART宏定义了,创建bootchart_init动作并添加到链表action_queue和action_list中
- queue_builtin_action(bootchart_init_action, "bootchart_init");
- #endif
- for(;;) {
- int nr, i, timeout = -1;
- //按序执行action_queue里的action
- execute_one_command();
- //重启一些关键进程
- restart_processes();
- //添加事件句柄到句柄次
- if (!property_set_fd_init && get_property_set_fd() > 0) {
- ufds[fd_count].fd = get_property_set_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- property_set_fd_init = 1;
- }
- if (!signal_fd_init && get_signal_fd() > 0) {
- ufds[fd_count].fd = get_signal_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- signal_fd_init = 1;
- }
- if (!keychord_fd_init && get_keychord_fd() > 0) {
- ufds[fd_count].fd = get_keychord_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- keychord_fd_init = 1;
- }
- //计算超时时间
- if (process_needs_restart) {
- timeout = (process_needs_restart - gettime()) * 1000;
- if (timeout < 0)
- timeout = 0;
- }
- if (!action_queue_empty() || cur_action)
- timeout = 0;
- #if BOOTCHART
- if (bootchart_count > 0) {
- if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
- timeout = BOOTCHART_POLLING_MS;
- if (bootchart_step() < 0 || --bootchart_count == 0) {
- bootchart_finish();
- bootchart_count = 0;
- }
- }
- #endif
- //监控句柄池中的事件
- nr = poll(ufds, fd_count, timeout);
- if (nr <= 0)
- continue;
- //事件处理
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents == POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
- }
- return 0;
- }
文件系统简介
tmpfs文件系统
tmpfs是一种虚拟内存文件系统,因此它会将所有的文件存储在虚拟内存中,并且tmpfs下的所有内容均为临时性的内容,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。tmpfs是一个独立的文件系统,不是块设备,只要挂接,立即就可以使用。
devpts文件系统
devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。
proc文件系统
proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
sysfs文件系统
与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。
屏蔽标准的输入输出
- void open_devnull_stdio(void)
- {
- int fd;
- //创建一个字符专用文件/dev/__null__
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- //获取/dev/__null__的文件描述符,并输出该文件
- fd = open(name, O_RDWR);
- unlink(name);
- if (fd >= 0) {
- //将与进程相关的标准输入(0),标准输出(1),标准错误输出(2),均定向到NULL设备
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) {
- close(fd);
- }
- return;
- }
- }
- exit(1);
- }
初始化内核log系统
- void klog_init(void)
- {
- static const char *name = "/dev/__kmsg__";
- //创建/dev/__kmsg__设备节点
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
- klog_fd = open(name, O_WRONLY);
- //当进程在进行exec系统调用时,要确保log_fd是关闭的
- fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
- unlink(name);
- }
- }
属性存储空间初始化
- void property_init(void)
- {
- init_property_area();
- }
读取机器硬件名称
从/proc/cpuinfo中获取“Hardware”字段信息写入<hw>;“Reversion” 字段信息写入<reversion>
- void get_hardware_name(char *hardware, unsigned int *revision)
- {
- char data[1024];
- int fd, n;
- char *x, *hw, *rev;
- /* Hardware string was provided on kernel command line */
- if (hardware[0])
- return;
- //打开/proc/cpuinfo文件
- fd = open("/proc/cpuinfo", O_RDONLY);
- if (fd < 0) return;
- //读取/proc/cpuinfo文件内容
- n = read(fd, data, 1023);
- close(fd);
- if (n < 0) return;
- data[n] = 0;
- hw = strstr(data, "\nHardware");
- rev = strstr(data, "\nRevision");
- if (hw) {
- x = strstr(hw, ": ");
- if (x) {
- x += 2;
- n = 0;
- while (*x && *x != '\n') {
- if (!isspace(*x))
- hardware[n++] = tolower(*x);
- x++;
- if (n == 31) break;
- }
- hardware[n] = 0;
- }
- }
- if (rev) {
- x = strstr(rev, ": ");
- if (x) {
- *revision = strtoul(x + 2, 0, 16);
- }
- }
- }
- Processor : ARMv7 Processor rev 1 (v7l)
- BogoMIPS : 1024.00
- Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3
- CPU implementer : 0x41
- CPU architecture: 7
- CPU variant : 0x0
- CPU part : 0xc05
- CPU revision : 1
- Hardware : sc7710g
- Revision : 0000
- Serial : 0000000000000000
设置命令行参数属性
- static void process_kernel_cmdline(void)
- {
- /* don't expose the raw commandline to nonpriv processes */
- chmod("/proc/cmdline", 0440);
- /* first pass does the common stuff, and finds if we are in qemu.
- * second pass is only necessary for qemu to export all kernel params
- * as props.
- */
- import_kernel_cmdline(0, import_kernel_nv);
- if (qemu[0])
- import_kernel_cmdline(1, import_kernel_nv);
- /* now propogate the info given on command line to internal variables
- * used by init as well as the current required properties
- */
- export_kernel_boot_props();
- }
- void import_kernel_cmdline(int in_qemu,void (*import_kernel_nv)(char *name, int in_qemu))
- {
- char cmdline[1024];
- char *ptr;
- int fd;
- //打开并读取/proc/cmdline文件
- fd = open("/proc/cmdline", O_RDONLY);
- if (fd >= 0) {
- int n = read(fd, cmdline, 1023);
- if (n < 0) n = 0;
- /* get rid of trailing newline, it happens */
- if (n > 0 && cmdline[n-1] == '\n') n--;
- cmdline[n] = 0;
- close(fd);
- } else {
- cmdline[0] = 0;
- }
- ptr = cmdline;
- while (ptr && *ptr) {
- char *x = strchr(ptr, ' ');
- if (x != 0) *x++ = 0;
- //回调import_kernel_nv函数,in_qemu =0
- import_kernel_nv(ptr, in_qemu);
- ptr = x;
- }
- }
- 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
- static void import_kernel_nv(char *name, int for_emulator)
- {
- char *value = strchr(name, '=');
- int name_len = strlen(name);
- if (value == 0) return;
- *value++ = 0;
- if (name_len == 0) return;
- #ifdef HAVE_SELINUX
- if (!strcmp(name,"enforcing")) {
- selinux_enforcing = atoi(value);
- } else if (!strcmp(name,"selinux")) {
- selinux_enabled = atoi(value);
- }
- #endif
- //判断是否为模拟器
- if (for_emulator) {
- /* in the emulator, export any kernel option with the
- * ro.kernel. prefix */
- char buff[PROP_NAME_MAX];
- int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
- if (len < (int)sizeof(buff))
- property_set( buff, value );
- return;
- }
- //如果/proc/cmdline文件中有qemu关键字
- if (!strcmp(name,"qemu")) {
- strlcpy(qemu, value, sizeof(qemu));
- //如果/proc/cmdline文件中有以androidboot.开头的关键字
- } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
- const char *boot_prop_name = name + 12;
- char prop[PROP_NAME_MAX];
- int cnt;
- //格式化为ro.boot.xx 属性
- cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
- if (cnt < PROP_NAME_MAX)
- property_set(prop, value);
- }
- }
- static void export_kernel_boot_props(void)
- {
- char tmp[PROP_VALUE_MAX];
- const char *pval;
- unsigned i;
- //属性表
- struct {
- const char *src_prop;
- const char *dest_prop;
- const char *def_val;
- } prop_map[] = {
- { "ro.boot.serialno", "ro.serialno", "", },
- { "ro.boot.mode", "ro.bootmode", "unknown", },
- { "ro.boot.baseband", "ro.baseband", "unknown", },
- { "ro.boot.bootloader", "ro.bootloader", "unknown", },
- };
- //循环读取ro.boot.xxx属性值,并设置ro.xxx属性
- for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
- pval = property_get(prop_map[i].src_prop);
- property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);
- }
- //读取ro.boot.console属性值
- pval = property_get("ro.boot.console");
- if (pval)
- strlcpy(console, pval, sizeof(console));
- //读取ro.bootmode属性值
- strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));
- //读取ro.boot.hardware属性值
- pval = property_get("ro.boot.hardware");
- if (pval)
- strlcpy(hardware, pval, sizeof(hardware));
- //设置ro.hardware属性
- property_set("ro.hardware", hardware);
- //设置ro.revision属性
- snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
- property_set("ro.revision", tmp);
- //设置ro.factorytest属性
- if (!strcmp(bootmode,"factory"))
- property_set("ro.factorytest", "1");
- else if (!strcmp(bootmode,"factory2"))
- property_set("ro.factorytest", "2");
- else
- property_set("ro.factorytest", "0");
- }
init.rc 文件解析
- init_parse_config_file(const char *fn)
- {
- char *data;
- //读取/init.rc文件内容
- data = read_file(fn, 0);
- if (!data) return -1;
- //解析读取到的文件内容
- parse_config(fn, data);
- DUMP();
- return 0;
- }
- void *read_file(const char *fn, unsigned *_sz)
- {
- char *data;
- int sz;
- int fd;
- struct stat sb;
- data = 0;
- //打开/init.rc文件
- fd = open(fn, O_RDONLY);
- if(fd < 0) return 0;
- // for security reasons, disallow world-writable
- // or group-writable files
- if (fstat(fd, &sb) < 0) {
- ERROR("fstat failed for '%s'\n", fn);
- goto oops;
- }
- if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- ERROR("skipping insecure file '%s'\n", fn);
- goto oops;
- }
- //将文件指针移到文件尾部,得到文件内容长度
- sz = lseek(fd, 0, SEEK_END);
- if(sz < 0) goto oops;
- if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
- //分配buffer
- data = (char*) malloc(sz + 2);
- if(data == 0) goto oops;
- //读取文件
- if(read(fd, data, sz) != sz) goto oops;
- close(fd);
- data[sz] = '\n';
- data[sz+1] = 0;
- if(_sz) *_sz = sz;
- return data;
- oops:
- close(fd);
- if(data != 0) free(data);
- return 0;
- }
init.rc文件语法介绍
在Android根文件系统下存在多个.rc文件,该文件为Android启动配置脚本文件,文件内容如下:
- # Copyright (C) 2012 The Android Open Source Project
- #
- # IMPORTANT: Do not create world writable files or directories.
- # This is a common source of Android security bugs.
- #
- import /init.${ro.hardware}.rc
- import /init.usb.rc
- import /init.trace.rc
- on early-init
- # Set init and its forked children's oom_adj.
- write /proc/1/oom_adj -16
- start ueventd
- mkdir /mnt 0775 root system
- on init
- sysclktz 0
- loglevel 3
- # setup the global environment
- export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
- export LD_LIBRARY_PATH /vendor/lib:/system/lib
- export ANDROID_BOOTLOGO 1
- export ANDROID_ROOT /system
- export ANDROID_ASSETS /system/app
- export ANDROID_DATA /data
- export ASEC_MOUNTPOINT /mnt/asec
- export LOOP_MOUNTPOINT /mnt/obb
- 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
- # Backward compatibility
- symlink /system/etc /etc
- symlink /sys/kernel/debug /d
- # Right now vendor lives on the same filesystem as system,
- # but someday that may change.
- symlink /system/vendor /vendor
- # Create cgroup mount point for cpu accounting
- mkdir /acct
- mount cgroup none /acct cpuacct
- mkdir /acct/uid
- mkdir /system
- mkdir /data 0771 system system
- mkdir /cache 0770 system cache
- mkdir /runtimenv 0774 system system
- mkdir /backupfixnv 0774 system system
- mkdir /productinfo 0774 system system
- mkdir /fixnv 0774 system system
- mkdir /config 0500 root root
- # Create cgroup mount points for process groups
- mkdir /dev/cpuctl
- mount cgroup none /dev/cpuctl cpu
- chown system system /dev/cpuctl
- chown system system /dev/cpuctl/tasks
- chmod 0660 /dev/cpuctl/tasks
- write /dev/cpuctl/cpu.shares 1024
- write /dev/cpuctl/cpu.rt_runtime_us 950000
- write /dev/cpuctl/cpu.rt_period_us 1000000
- mkdir /dev/cpuctl/apps
- chown system system /dev/cpuctl/apps/tasks
- chmod 0666 /dev/cpuctl/apps/tasks
- write /dev/cpuctl/apps/cpu.shares 1024
- write /dev/cpuctl/apps/cpu.rt_runtime_us 800000
- write /dev/cpuctl/apps/cpu.rt_period_us 1000000
- on fs
- # mount mtd partitions
- # Mount /system rw first to give the filesystem a chance to save a checkpoint
- chmod 0744 /modem_control
- start modem_control
- mount yaffs2 mtd@system /system
- mount yaffs2 mtd@system /system ro remount
- mount yaffs2 mtd@userdata /data nosuid nodev
- mount yaffs2 mtd@cache /cache nosuid nodev
- on post-fs
- # once everything is setup, no need to modify /
- mount rootfs rootfs / ro remount
- mount yaffs2 mtd@fixnv /fixnv nosuid nodev no-checkpoint
- chown system system /fixnv
- chmod 0774 /fixnv
- mount yaffs2 mtd@runtimenv /runtimenv nosuid nodev no-checkpoint
- chown system system /runtimenv
- chmod 0774 /runtimenv
- # We chown/chmod /cache again so because mount is run as root + defaults
- chown system cache /cache
- chmod 0770 /cache
- mount yaffs2 mtd@backupfixnv /backupfixnv nosuid nodev no-checkpoint
- chown system system /backupfixnv
- chmod 0774 /backupfixnv
- mount yaffs2 mtd@productinfo /productinfo nosuid nodev no-checkpoint
- chown system system /productinfo
- chmod 0774 /productinfo
- chmod 0660 /fixnv/fixnv.bin
- chmod 0660 /backupfixnv/fixnv.bin
- chmod 0660 /productinfo/productinfo.bin
- chmod 0660 /productinfo/productinfobkup.bin
- chown system system /fixnv/fixnv.bin
- chown system system /backupfixnv/fixnv.bin
- chown system system /productinfo/productinfo.bin
- chown system system /productinfo/productinfobkup.bin
- # This may have been created by the recovery system with odd permissions
- chown system cache /cache/recovery
- chmod 0770 /cache/recovery
- #change permissions on vmallocinfo so we can grab it from bugreports
- chown root log /proc/vmallocinfo
- chmod 0440 /proc/vmallocinfo
- #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks
- chown root system /proc/kmsg
- chmod 0440 /proc/kmsg
- chown root system /proc/sysrq-trigger
- chmod 0220 /proc/sysrq-trigger
- # create the lost+found directories, so as to enforce our permissions
- mkdir /cache/lost+found 0770 root root
- on post-fs-data
- # create basic filesystem structure
- mkdir /data/misc 01771 system misc
- mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth
- mkdir /data/misc/bluetooth 0770 system system
- mkdir /data/misc/keystore 0700 keystore keystore
- mkdir /data/misc/keychain 0771 system system
- mkdir /data/misc/vpn 0770 system vpn
- mkdir /data/misc/systemkeys 0700 system system
- on boot
- # basic network init
- ifup lo
- hostname localhost
- domainname localdomain
- # set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit 13 40 40
- # Memory management. Basic kernel parameters, and allow the high
- # level system server to be able to adjust the kernel OOM driver
- # parameters to match how it is managing things.
- write /proc/sys/vm/overcommit_memory 1
- write /proc/sys/vm/min_free_order_shift 4
- chown root system /sys/module/lowmemorykiller/parameters/adj
- # Tweak background writeout
- write /proc/sys/vm/dirty_expire_centisecs 200
- write /proc/sys/vm/dirty_background_ratio 5
- class_start core
- class_start main
- on nonencrypted
- class_start late_start
- on charger
- class_start core
- class_start charger
- on alarm
- insmod /system/lib/modules/ft5306_ts.ko
- class_start core
- start media
- exec /bin/poweroff_alarm
- on property:vold.decrypt=trigger_reset_main
- class_reset main
- on property:vold.decrypt=trigger_load_persist_props
- load_persist_props
- on property:vold.decrypt=trigger_post_fs_data
- trigger post-fs-data
- on property:vold.decrypt=trigger_restart_min_framework
- class_start main
- on property:vold.decrypt=trigger_restart_framework
- class_start main
- class_start late_start
- on property:vold.decrypt=trigger_shutdown_framework
- class_reset late_start
- class_reset main
- ## Daemon processes to be run by init.
- ##
- service ueventd /sbin/ueventd
- class core
- critical
- service console /system/bin/sh
- class core
- console
- disabled
- user shell
- group log
- on property:ro.debuggable=1
- start console
- # adbd is controlled via property triggers in init.<platform>.usb.rc
- service adbd /sbin/adbd
- class core
- disabled
- # adbd on at boot in emulator
- on property:ro.kernel.qemu=1
- start adbd
- # This property trigger has added to imitiate the previous behavior of "adb root".
- # The adb gadget driver used to reset the USB bus when the adbd daemon exited,
- # and the host side adb relied on this behavior to force it to reconnect with the
- # new adbd instance after init relaunches it. So now we force the USB bus to reset
- # here when adbd sets the service.adb.root property to 1. We also restart adbd here
- # rather than waiting for init to notice its death and restarting it so the timing
- # of USB resetting and adb restarting more closely matches the previous behavior.
- on property:service.adb.root=1
- write /sys/class/android_usb/android0/enable 0
- restart adbd
- write /sys/class/android_usb/android0/enable 1
- service servicemanager /system/bin/servicemanager
- class core
- user system
- group system
- critical
- onrestart restart zygote
- onrestart restart media
- onrestart restart surfaceflinger
- onrestart restart drm
- service vold /system/bin/vold
- class core
- socket vold stream 0660 root mount
- ioprio be 2
- service netd /system/bin/netd
- class main
- socket netd stream 0660 root system
- socket dnsproxyd stream 0660 root inet
- socket mdns stream 0660 root system
- service debuggerd /system/bin/debuggerd
- class main
- #service ril-daemon /system/bin/rild
- # class main
- # socket rild stream 660 root radio
- # socket rild-debug stream 660 radio system
- # user root
- # group radio cache inet misc audio sdcard_r sdcard_rw log
- service surfaceflinger /system/bin/surfaceflinger
- class main
- user system
- group graphics
- onrestart restart zygote
- service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
- class main
- socket zygote stream 660 root system
- onrestart write /sys/android_power/request_state wake
- onrestart write /sys/power/state on
- onrestart restart media
- onrestart restart netd
- service bootanim /system/bin/bootanimation
- class main
- user graphics
- group graphics
- disabled
- oneshot
- service dbus /system/bin/dbus-daemon --system --nofork
- class main
- socket dbus stream 660 bluetooth bluetooth
- user bluetooth
- group bluetooth net_bt_admin
- service bluetoothd /system/bin/bluetoothd -n
- class main
- socket bluetooth stream 660 bluetooth bluetooth
- socket dbus_bluetooth stream 660 bluetooth bluetooth
- # init.rc does not yet support applying capabilities, so run as root and
- # let bluetoothd drop uid to bluetooth with the right linux capabilities
- group bluetooth net_bt_admin misc
- disabled
- service installd /system/bin/installd
- class main
- socket installd stream 600 system system
- service flash_recovery /system/etc/install-recovery.sh
- class main
- oneshot
- service racoon /system/bin/racoon
- class main
- socket racoon stream 600 system system
- # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
- group vpn net_admin inet
- disabled
- oneshot
- service mtpd /system/bin/mtpd
- class main
- socket mtpd stream 600 system system
- user vpn
- group vpn net_admin inet net_raw
- disabled
- oneshot
- service keystore /system/bin/keystore /data/misc/keystore
- class main
- user keystore
- group keystore drmrpc
- socket keystore stream 666
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的定义
- #define T_EOF 0
- #define T_TEXT 1
- #define T_NEWLINE 2
- struct listnode
- {
- struct listnode *next;
- struct listnode *prev;
- };
- struct action {
- /* node in list of all actions */
- struct listnode alist;
- /* node in the queue of pending actions */
- struct listnode qlist;
- /* node in list of actions for a trigger */
- struct listnode tlist;
- unsigned hash;
- const char *name;
- struct listnode commands;
- struct command *current;
- };
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #define node_to_item(node, container, member) \
- (container *) (((char*) (node)) - offsetof(container, member))
Service数据结构定义:
- struct service {
- /* list of all services */
- struct listnode slist;
- const char *name;
- const char *classname;
- unsigned flags;
- pid_t pid;
- time_t time_started; /* time of last start */
- time_t time_crashed; /* first crash within inspection window */
- int nr_crashed; /* number of times crashed within window */
- uid_t uid;
- gid_t gid;
- gid_t supp_gids[NR_SVC_SUPP_GIDS];
- size_t nr_supp_gids;
- #ifdef HAVE_SELINUX
- char *seclabel;
- #endif
- struct socketinfo *sockets;
- struct svcenvinfo *envvars;
- struct action onrestart; /* Actions to execute on restart. */
- /* keycodes for triggering this service via /dev/keychord */
- int *keycodes;
- int nkeycodes;
- int keychord_id;
- int ioprio_class;
- int ioprio_pri;
- int nargs;
- /* "MUST BE AT THE END OF THE STRUCT" */
- char *args[1];
- };
- service wril-daemon /system/bin/rild_sp -l /system/lib/libreference-ril_sp.so -m w -n 0
- class core
- socket rild stream 660 root radio
- socket rild-debug stream 660 radio system
- disabled
- user root
- group radio cache inet misc audio sdcard_rw log
- struct socketinfo {
- struct socketinfo *next;
- const char *name;
- const char *type;
- uid_t uid;
- gid_t gid;
- int perm;
- };
某些Service在运行时需要设置环境变量,这些环境变量被保存在Service的成员变量envvars链表中,svcenvinfo 数据结构定义如下:
- struct svcenvinfo {
- struct svcenvinfo *next;
- const char *name;
- const char *value;
- };
- struct command
- {
- /* list of commands in an action */
- struct listnode clist;
- int (*func)(int nargs, char **args);
- int nargs;
- char *args[1];
- };
在Init进程中分别使用了3个链表来存储init.rc文件中的Action和Service:
- static list_declare(service_list);
- static list_declare(action_list);
- static list_declare(action_queue);
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文件中定义了解析关键字,其内容如下:
- #ifndef KEYWORD
- int do_chroot(int nargs, char **args);
- int do_chdir(int nargs, char **args);
- int do_class_start(int nargs, char **args);
- int do_class_stop(int nargs, char **args);
- int do_class_reset(int nargs, char **args);
- int do_domainname(int nargs, char **args);
- int do_exec(int nargs, char **args);
- int do_export(int nargs, char **args);
- int do_hostname(int nargs, char **args);
- int do_ifup(int nargs, char **args);
- int do_insmod(int nargs, char **args);
- int do_mkdir(int nargs, char **args);
- int do_mount_all(int nargs, char **args);
- int do_mount(int nargs, char **args);
- int do_restart(int nargs, char **args);
- int do_restorecon(int nargs, char **args);
- int do_rm(int nargs, char **args);
- int do_rmdir(int nargs, char **args);
- int do_setcon(int nargs, char **args);
- int do_setenforce(int nargs, char **args);
- int do_setkey(int nargs, char **args);
- int do_setprop(int nargs, char **args);
- int do_setrlimit(int nargs, char **args);
- int do_setsebool(int nargs, char **args);
- int do_start(int nargs, char **args);
- int do_stop(int nargs, char **args);
- int do_trigger(int nargs, char **args);
- int do_symlink(int nargs, char **args);
- int do_sysclktz(int nargs, char **args);
- int do_write(int nargs, char **args);
- int do_copy(int nargs, char **args);
- int do_chown(int nargs, char **args);
- int do_chmod(int nargs, char **args);
- int do_loglevel(int nargs, char **args);
- int do_load_persist_props(int nargs, char **args);
- int do_pipe(int nargs, char **args);
- int do_wait(int nargs, char **args);
- #define __MAKE_KEYWORD_ENUM__
- #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
- enum {
- K_UNKNOWN,
- #endif
- KEYWORD(capability, OPTION, 0, 0)
- KEYWORD(chdir, COMMAND, 1, do_chdir)
- KEYWORD(chroot, COMMAND, 1, do_chroot)
- KEYWORD(class, OPTION, 0, 0)
- KEYWORD(class_start, COMMAND, 1, do_class_start)
- KEYWORD(class_stop, COMMAND, 1, do_class_stop)
- KEYWORD(class_reset, COMMAND, 1, do_class_reset)
- KEYWORD(console, OPTION, 0, 0)
- KEYWORD(critical, OPTION, 0, 0)
- KEYWORD(disabled, OPTION, 0, 0)
- KEYWORD(domainname, COMMAND, 1, do_domainname)
- KEYWORD(exec, COMMAND, 1, do_exec)
- KEYWORD(export, COMMAND, 2, do_export)
- KEYWORD(group, OPTION, 0, 0)
- KEYWORD(hostname, COMMAND, 1, do_hostname)
- KEYWORD(ifup, COMMAND, 1, do_ifup)
- KEYWORD(insmod, COMMAND, 1, do_insmod)
- KEYWORD(import, SECTION, 1, 0)
- KEYWORD(keycodes, OPTION, 0, 0)
- KEYWORD(mkdir, COMMAND, 1, do_mkdir)
- KEYWORD(mount_all, COMMAND, 1, do_mount_all)
- KEYWORD(mount, COMMAND, 3, do_mount)
- KEYWORD(on, SECTION, 0, 0)
- KEYWORD(oneshot, OPTION, 0, 0)
- KEYWORD(onrestart, OPTION, 0, 0)
- KEYWORD(restart, COMMAND, 1, do_restart)
- KEYWORD(restorecon, COMMAND, 1, do_restorecon)
- KEYWORD(rm, COMMAND, 1, do_rm)
- KEYWORD(rmdir, COMMAND, 1, do_rmdir)
- KEYWORD(seclabel, OPTION, 0, 0)
- KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
- KEYWORD(setenforce, COMMAND, 1, do_setenforce)
- KEYWORD(setenv, OPTION, 2, 0)
- KEYWORD(setkey, COMMAND, 0, do_setkey)
- KEYWORD(setprop, COMMAND, 2, do_setprop)
- KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
- KEYWORD(setsebool, COMMAND, 1, do_setsebool)
- KEYWORD(socket, OPTION, 0, 0)
- KEYWORD(start, COMMAND, 1, do_start)
- KEYWORD(stop, COMMAND, 1, do_stop)
- KEYWORD(trigger, COMMAND, 1, do_trigger)
- KEYWORD(symlink, COMMAND, 1, do_symlink)
- KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
- KEYWORD(user, OPTION, 0, 0)
- KEYWORD(wait, COMMAND, 1, do_wait)
- KEYWORD(write, COMMAND, 2, do_write)
- KEYWORD(copy, COMMAND, 2, do_copy)
- KEYWORD(chown, COMMAND, 2, do_chown)
- KEYWORD(chmod, COMMAND, 2, do_chmod)
- KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
- KEYWORD(pipe, COMMAND, 2, do_pipe)
- KEYWORD(ioprio, OPTION, 0, 0)
- #ifdef __MAKE_KEYWORD_ENUM__
- KEYWORD_COUNT,
- };
- #undef __MAKE_KEYWORD_ENUM__
- #undef KEYWORD
- #endif
- #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
- enum {
- K_UNKNOWN,
- KEYWORD(capability, OPTION, 0, 0)
- KEYWORD(chdir, COMMAND, 1, do_chdir)
- KEYWORD(chroot, COMMAND, 1, do_chroot)
- KEYWORD(class, OPTION, 0, 0)
- KEYWORD(class_start, COMMAND, 1, do_class_start)
- KEYWORD(class_stop, COMMAND, 1, do_class_stop)
- KEYWORD(class_reset, COMMAND, 1, do_class_reset)
- KEYWORD(console, OPTION, 0, 0)
- KEYWORD(critical, OPTION, 0, 0)
- KEYWORD(disabled, OPTION, 0, 0)
- KEYWORD(domainname, COMMAND, 1, do_domainname)
- KEYWORD(exec, COMMAND, 1, do_exec)
- KEYWORD(export, COMMAND, 2, do_export)
- KEYWORD(group, OPTION, 0, 0)
- KEYWORD(hostname, COMMAND, 1, do_hostname)
- KEYWORD(ifup, COMMAND, 1, do_ifup)
- KEYWORD(insmod, COMMAND, 1, do_insmod)
- KEYWORD(import, SECTION, 1, 0)
- KEYWORD(keycodes, OPTION, 0, 0)
- KEYWORD(mkdir, COMMAND, 1, do_mkdir)
- KEYWORD(mount_all, COMMAND, 1, do_mount_all)
- KEYWORD(mount, COMMAND, 3, do_mount)
- KEYWORD(on, SECTION, 0, 0)
- KEYWORD(oneshot, OPTION, 0, 0)
- KEYWORD(onrestart, OPTION, 0, 0)
- KEYWORD(restart, COMMAND, 1, do_restart)
- KEYWORD(restorecon, COMMAND, 1, do_restorecon)
- KEYWORD(rm, COMMAND, 1, do_rm)
- KEYWORD(rmdir, COMMAND, 1, do_rmdir)
- KEYWORD(seclabel, OPTION, 0, 0)
- KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
- KEYWORD(setenforce, COMMAND, 1, do_setenforce)
- KEYWORD(setenv, OPTION, 2, 0)
- KEYWORD(setkey, COMMAND, 0, do_setkey)
- KEYWORD(setprop, COMMAND, 2, do_setprop)
- KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
- KEYWORD(setsebool, COMMAND, 1, do_setsebool)
- KEYWORD(socket, OPTION, 0, 0)
- KEYWORD(start, COMMAND, 1, do_start)
- KEYWORD(stop, COMMAND, 1, do_stop)
- KEYWORD(trigger, COMMAND, 1, do_trigger)
- KEYWORD(symlink, COMMAND, 1, do_symlink)
- KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
- KEYWORD(user, OPTION, 0, 0)
- KEYWORD(wait, COMMAND, 1, do_wait)
- KEYWORD(write, COMMAND, 2, do_write)
- KEYWORD(copy, COMMAND, 2, do_copy)
- KEYWORD(chown, COMMAND, 2, do_chown)
- KEYWORD(chmod, COMMAND, 2, do_chmod)
- KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
- KEYWORD(pipe, COMMAND, 2, do_pipe)
- KEYWORD(ioprio, OPTION, 0, 0)
- KEYWORD_COUNT,
- };
- enum {
- K_UNKNOWN,
- K_capability,
- K_chdir,
- K_chroot,
- K_class,
- K_class_start,
- K_class_stop,
- K_class_reset,
- K_console,
- K_critical,
- K_disabled,
- K_domainname,
- K_exec,
- K_export,
- K_group,
- K_hostname,
- K_ifup,
- K_insmod,
- K_import,
- K_keycodes,
- K_mkdir,
- K_mount_all,
- K_mount,
- K_on,
- K_oneshot,
- K_onrestart,
- K_restart,
- K_restorecon,
- K_rm,
- K_rmdir
- K_seclabel
- K_service
- K_setcon
- K_setenforce
- K_setenv
- K_setkey
- K_setprop
- K_setrlimit
- K_setsebool
- K_socket
- K_start
- K_stop
- K_trigger
- K_symlink
- K_sysclktz
- K_user
- K_wait
- K_write
- K_copy
- K_chown
- K_chmod
- K_loglevel
- K_load_persist_props
- K_pipe
- K_ioprio
- KEYWORD_COUNT,
- };
- #define KEYWORD(symbol, flags, nargs, func) \
- [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
- struct {
- const char *name;
- int (*func)(int nargs, char **args);
- unsigned char nargs;
- unsigned char flags;
- } keyword_info[KEYWORD_COUNT] = {
- [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
- #include "keywords.h"
- };
- KEYWORD(capability, OPTION, 0, 0)
- KEYWORD(chdir, COMMAND, 1, do_chdir)
- KEYWORD(chroot, COMMAND, 1, do_chroot)
- KEYWORD(class, OPTION, 0, 0)
- KEYWORD(class_start, COMMAND, 1, do_class_start)
- KEYWORD(class_stop, COMMAND, 1, do_class_stop)
- KEYWORD(class_reset, COMMAND, 1, do_class_reset)
- KEYWORD(console, OPTION, 0, 0)
- KEYWORD(critical, OPTION, 0, 0)
- KEYWORD(disabled, OPTION, 0, 0)
- KEYWORD(domainname, COMMAND, 1, do_domainname)
- KEYWORD(exec, COMMAND, 1, do_exec)
- KEYWORD(export, COMMAND, 2, do_export)
- KEYWORD(group, OPTION, 0, 0)
- KEYWORD(hostname, COMMAND, 1, do_hostname)
- KEYWORD(ifup, COMMAND, 1, do_ifup)
- KEYWORD(insmod, COMMAND, 1, do_insmod)
- KEYWORD(import, SECTION, 1, 0)
- KEYWORD(keycodes, OPTION, 0, 0)
- KEYWORD(mkdir, COMMAND, 1, do_mkdir)
- KEYWORD(mount_all, COMMAND, 1, do_mount_all)
- KEYWORD(mount, COMMAND, 3, do_mount)
- KEYWORD(on, SECTION, 0, 0)
- KEYWORD(oneshot, OPTION, 0, 0)
- KEYWORD(onrestart, OPTION, 0, 0)
- KEYWORD(restart, COMMAND, 1, do_restart)
- KEYWORD(restorecon, COMMAND, 1, do_restorecon)
- KEYWORD(rm, COMMAND, 1, do_rm)
- KEYWORD(rmdir, COMMAND, 1, do_rmdir)
- KEYWORD(seclabel, OPTION, 0, 0)
- KEYWORD(service, SECTION, 0, 0)
- KEYWORD(setcon, COMMAND, 1, do_setcon)
- KEYWORD(setenforce, COMMAND, 1, do_setenforce)
- KEYWORD(setenv, OPTION, 2, 0)
- KEYWORD(setkey, COMMAND, 0, do_setkey)
- KEYWORD(setprop, COMMAND, 2, do_setprop)
- KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
- KEYWORD(setsebool, COMMAND, 1, do_setsebool)
- KEYWORD(socket, OPTION, 0, 0)
- KEYWORD(start, COMMAND, 1, do_start)
- KEYWORD(stop, COMMAND, 1, do_stop)
- KEYWORD(trigger, COMMAND, 1, do_trigger)
- KEYWORD(symlink, COMMAND, 1, do_symlink)
- KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
- KEYWORD(user, OPTION, 0, 0)
- KEYWORD(wait, COMMAND, 1, do_wait)
- KEYWORD(write, COMMAND, 2, do_write)
- KEYWORD(copy, COMMAND, 2, do_copy)
- KEYWORD(chown, COMMAND, 2, do_chown)
- KEYWORD(chmod, COMMAND, 2, do_chmod)
- KEYWORD(loglevel, COMMAND, 1, do_loglevel)
- KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
- KEYWORD(pipe, COMMAND, 2, do_pipe)
- KEYWORD(ioprio, OPTION, 0, 0)
使用上述KEYWORD宏展开得到keyword_info数组内容如下:
- [ K_capability ] = { capability, 0, 1, OPTION, },
- [ K_class ] = { class, 0, 1, OPTION, },
- [ K_console ] = { console, 0, 1, OPTION, },
- [ K_critical ] = { critical, 0, 1, OPTION, },
- [ K_group ] = { group, 0, 1, OPTION, },
- [ K_disabled ] = { disabled, 0, 1, OPTION, },
- [ K_keycodes ] = { keycodes, 0, 1, OPTION, },
- [ K_oneshot ] = { oneshot, 0, 1, OPTION, },
- [ K_onrestart ] = { onrestart, 0, 1, OPTION, },
- [ K_socket ] = { socket, 0, 1, OPTION, },
- [ K_setenv ] = { setenv, 0, 3, OPTION, },
- [ K_ioprio ] = { ioprio, 0, 1, OPTION, },
- [ K_user ] = { user, 0, 1, OPTION, },
- [ K_seclabel ] = { seclabel, 0, 1, OPTION, },
- [ K_service ] = { service, 0, 1, SECTION, },
- [ K_on ] = { on, 0, 1, SECTION, },
- [ K_import ] = { import, 0, 2, SECTION, },
- [ K_chdir ] = { chdir, do_chdir, 2, COMMAND, },
- [ K_chroot ] = { chroot, do_chroot, 2, COMMAND, },
- [ K_class_start ] = { class_start, do_class_start, 2, COMMAND, },
- [ K_class_stop ] = { class_stop, do_class_stop, 2, COMMAND, },
- [ K_class_reset ] = { class_reset, do_class_reset, 2, COMMAND, },
- [ K_domainname ] = { domainname, do_domainname, 2, COMMAND, },
- [ K_exec ] = { exec, do_exec, 2, COMMAND, },
- [ K_export ] = { export, do_export, 3, COMMAND, },
- [ K_hostname ] = { hostname, do_hostname, 2, COMMAND, },
- [ K_ifup ] = { ifup, do_ifup, 2, COMMAND, },
- [ K_insmod ] = { insmod, do_insmod, 3, COMMAND, },
- [ K_mkdir ] = { mkdir, do_mkdir, 2, COMMAND, },
- [ K_mount_all ] = { mount_all, do_mount_all, 2, COMMAND, },
- [ K_mount ] = { mount, do_mount, 4, COMMAND, },
- [ K_restart ] = { restart, do_restart, 2, COMMAND, },
- [ K_restorecon ] = { restorecon, do_restorecon, 2, COMMAND, },
- [ K_rm ] = { rm, do_rm, 2, COMMAND, }
- [ K_rmdir ] = { rmdir, do_rmdir, 2, COMMAND, },
- [ K_setcon ] = { setcon, do_setcon, 2, COMMAND, },
- [ K_setenforce ] = { setenforce, do_setenforce, 2, COMMAND, },
- [ K_setkey ] = { setkey, do_setkey, 1, COMMAND, },
- [ K_setprop ] = { setprop, do_setprop, 3, COMMAND, },
- [ K_setrlimit ] = { setrlimit, do_setrlimit, 4, COMMAND, },
- [ K_setsebool ] = { setsebool, do_setsebool, 2, COMMAND, },
- [ K_start ] = { start, do_start, 2, COMMAND, },
- [ K_stop ] = { stop, do_stop, 2, COMMAND, },
- [ K_trigger ] = { trigger, do_trigger, 2, COMMAND, },
- [ K_symlink ] = { symlink, do_symlink, 2, COMMAND, },
- [ K_sysclktz ] = { sysclktz, do_sysclktz, 2, COMMAND, },
- [ K_wait ] = { wait, do_wait, 2, COMMAND, },
- [ K_write ] = { write, do_write, 3, COMMAND, },
- [ K_copy ] = { copy, do_copy, 3, COMMAND, },
- [ K_chown ] = { chown, do_chown, 3, COMMAND, },
- [ K_chmod ] = { chmod, do_chmod, 3, COMMAND, },
- [ K_loglevel ] = { loglevel, do_loglevel, 2, COMMAND, },
- [ K_load_persist_props] = { load_persist_props, do_load_persist_props,1, COMMAND, },
- [ K_pipe ] = { pipe, do_pipe, 3, COMMAND, },
- static void parse_config(const char *fn, char *s)
- {
- struct parse_state state;
- struct listnode import_list;
- struct listnode *node;
- char *args[INIT_PARSER_MAXARGS];
- int nargs;
- nargs = 0;
- state.filename = fn; //文件名称
- state.line = 0; //统计文件行数
- state.ptr = s; //文件内容
- state.nexttoken = 0;
- state.parse_line = parse_line_no_op; //解析函数指针
- //初始化import_list链表,该链表用于保存通过import关键字引入的其他.rc文件
- list_init(&import_list);
- state.priv = &import_list;
- for (;;) {
- //next_token函数用于扫描init.rc中的token
- switch (next_token(&state)) {
- //文件结束EOF
- case T_EOF:
- state.parse_line(&state, 0, 0);
- goto parser_done;
- //新行NEWLINE
- case T_NEWLINE:
- state.line++;
- if (nargs) {
- //根据行头查找关键字类型
- int kw = lookup_keyword(args[0]);
- //如果是SECTION类型,SECTION包括以关键字service,on,import开头的语句
- if (kw_is(kw, SECTION)) {
- //解析该行,此时parse_line指向的回调函数为parse_line_no_op,该函数什么也不做
- state.parse_line(&state, 0, 0);
- //解析该SECTION
- parse_new_section(&state, kw, nargs, args);
- //如果不是SECTION类型,则调用parse_line指向的回调函数
- } else {
- state.parse_line(&state, nargs, args);
- }
- nargs = 0;
- }
- break;
- //文本TEXT
- case T_TEXT:
- if (nargs < INIT_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
- }
- parser_done:
- //init.rc 文件解析结束后,解析通过import关键字导入的.rc文件
- list_for_each(node, &import_list) {
- //从import_list链表中循环取出导入的.rc文件路径
- struct import *import = node_to_item(node, struct import, list);
- int ret;
- INFO("importing '%s'", import->filename);
- //读取并解析导入的.rc文件
- ret = init_parse_config_file(import->filename);
- if (ret)
- ERROR("could not import file '%s' from '%s'\n",import->filename, fn);
- }
- }
在前面介绍了通过定义枚举来为每个命令分配类型,lookup_keyword函数通过比较命令名称来返回对应命令的类型,如下所示:
- int lookup_keyword(const char *s)
- {
- switch (*s++) {
- case 'c':
- if (!strcmp(s, "opy")) return K_copy;
- if (!strcmp(s, "apability")) return K_capability;
- if (!strcmp(s, "hdir")) return K_chdir;
- if (!strcmp(s, "hroot")) return K_chroot;
- if (!strcmp(s, "lass")) return K_class;
- if (!strcmp(s, "lass_start")) return K_class_start;
- if (!strcmp(s, "lass_stop")) return K_class_stop;
- if (!strcmp(s, "lass_reset")) return K_class_reset;
- if (!strcmp(s, "onsole")) return K_console;
- if (!strcmp(s, "hown")) return K_chown;
- if (!strcmp(s, "hmod")) return K_chmod;
- if (!strcmp(s, "ritical")) return K_critical;
- break;
- case 'd':
- if (!strcmp(s, "isabled")) return K_disabled;
- if (!strcmp(s, "omainname")) return K_domainname;
- break;
- case 'e':
- if (!strcmp(s, "xec")) return K_exec;
- if (!strcmp(s, "xport")) return K_export;
- break;
- case 'g':
- if (!strcmp(s, "roup")) return K_group;
- break;
- case 'h':
- if (!strcmp(s, "ostname")) return K_hostname;
- break;
- case 'i':
- if (!strcmp(s, "oprio")) return K_ioprio;
- if (!strcmp(s, "fup")) return K_ifup;
- if (!strcmp(s, "nsmod")) return K_insmod;
- if (!strcmp(s, "mport")) return K_import;
- break;
- case 'k':
- if (!strcmp(s, "eycodes")) return K_keycodes;
- break;
- case 'l':
- if (!strcmp(s, "oglevel")) return K_loglevel;
- if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
- break;
- case 'm':
- if (!strcmp(s, "kdir")) return K_mkdir;
- if (!strcmp(s, "ount_all")) return K_mount_all;
- if (!strcmp(s, "ount")) return K_mount;
- break;
- case 'o':
- if (!strcmp(s, "n")) return K_on;
- if (!strcmp(s, "neshot")) return K_oneshot;
- if (!strcmp(s, "nrestart")) return K_onrestart;
- break;
- case 'r':
- if (!strcmp(s, "estart")) return K_restart;
- if (!strcmp(s, "estorecon")) return K_restorecon;
- if (!strcmp(s, "mdir")) return K_rmdir;
- if (!strcmp(s, "m")) return K_rm;
- break;
- case 's':
- if (!strcmp(s, "eclabel")) return K_seclabel;
- if (!strcmp(s, "ervice")) return K_service;
- if (!strcmp(s, "etcon")) return K_setcon;
- if (!strcmp(s, "etenforce")) return K_setenforce;
- if (!strcmp(s, "etenv")) return K_setenv;
- if (!strcmp(s, "etkey")) return K_setkey;
- if (!strcmp(s, "etprop")) return K_setprop;
- if (!strcmp(s, "etrlimit")) return K_setrlimit;
- if (!strcmp(s, "etsebool")) return K_setsebool;
- if (!strcmp(s, "ocket")) return K_socket;
- if (!strcmp(s, "tart")) return K_start;
- if (!strcmp(s, "top")) return K_stop;
- if (!strcmp(s, "ymlink")) return K_symlink;
- if (!strcmp(s, "ysclktz")) return K_sysclktz;
- break;
- case 't':
- if (!strcmp(s, "rigger")) return K_trigger;
- break;
- case 'u':
- if (!strcmp(s, "ser")) return K_user;
- break;
- case 'w':
- if (!strcmp(s, "rite")) return K_write;
- if (!strcmp(s, "ait")) return K_wait;
- break;
- case 'p':
- if (!strcmp(s, "ipe")) return K_pipe;
- }
- return K_UNKNOWN;
- }
- void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
- {
- printf("[ %s %s ]\n", args[0],nargs > 1 ? args[1] : "");
- switch(kw) {
- //如果关键字是service,表示这是一条描述服务的语句,调用parse_service函数来解析该行,并将解析得到的service保存在state->context中,同时设置解析函数parse_line为parse_line_service()
- case K_service:
- state->context = parse_service(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_service;
- return;
- }
- break;
- //如果关键字是on,表示这是一条Action语句,调用parse_action函数来解析该行,并将解析得到的Action保存在state->context中,同时设置解析函数parse_line为parse_line_action()
- case K_on:
- state->context = parse_action(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_action;
- return;
- }
- break;
- //如果关键字是import,表示这是一条import语句,调用parse_import函数来解析该行,同时设置解析函数parse_line为parse_line_no_op()
- case K_import:
- parse_import(state, nargs, args);
- break;
- }
- state->parse_line = parse_line_no_op;
- }
- static void *parse_service(struct parse_state *state, int nargs, char **args)
- {
- struct service *svc;
- //检查参数个数
- if (nargs < 3) {
- parse_error(state, "services must have a name and a program\n");
- return 0;
- }
- //检查参数名称的有效性
- if (!valid_name(args[1])) {
- parse_error(state, "invalid service name '%s'\n", args[1]);
- return 0;
- }
- //从服务链表中查找该名称的服务以防止出现重复的服务
- svc = service_find_by_name(args[1]);
- if (svc) {
- parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
- return 0;
- }
- nargs -= 2;
- //创建一个service
- svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
- if (!svc) {
- parse_error(state, "out of memory\n");
- return 0;
- }
- svc->name = args[1];
- svc->classname = "default";
- memcpy(svc->args, args + 2, sizeof(char*) * nargs);
- svc->args[nargs] = 0;
- svc->nargs = nargs;
- svc->onrestart.name = "onrestart";
- list_init(&svc->onrestart.commands);
- //将该服务添加到service_list链表中
- list_add_tail(&service_list, &svc->slist);
- return svc;
- }
- static void parse_line_service(struct parse_state *state, int nargs, char **args)
- {
- //从state->context中取出已经解析生成的service
- struct service *svc = state->context;
- struct command *cmd;
- int i, kw, kw_nargs;
- if (nargs == 0) {
- return;
- }
- svc->ioprio_class = IoSchedClass_NONE;
- //查找命令类型
- kw = lookup_keyword(args[0]);
- switch (kw) {
- //capability命令处理
- case K_capability:
- break;
- //class命令处理
- case K_class:
- if (nargs != 2) {
- parse_error(state, "class option requires a classname\n");
- } else {
- svc->classname = args[1];
- }
- break;
- //console命令处理
- case K_console:
- svc->flags |= SVC_CONSOLE;
- break;
- //disabled命令处理
- case K_disabled:
- svc->flags |= SVC_DISABLED;
- svc->flags |= SVC_RC_DISABLED;
- break;
- //ioprio命令处理
- case K_ioprio:
- if (nargs != 3) {
- parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
- } else {
- svc->ioprio_pri = strtoul(args[2], 0, 8);
- if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
- parse_error(state, "priority value must be range 0 - 7\n");
- break;
- }
- if (!strcmp(args[1], "rt")) {
- svc->ioprio_class = IoSchedClass_RT;
- } else if (!strcmp(args[1], "be")) {
- svc->ioprio_class = IoSchedClass_BE;
- } else if (!strcmp(args[1], "idle")) {
- svc->ioprio_class = IoSchedClass_IDLE;
- } else {
- parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
- }
- }
- break;
- //group命令处理
- case K_group:
- if (nargs < 2) {
- parse_error(state, "group option requires a group id\n");
- } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
- parse_error(state, "group option accepts at most %d supp. groups\n",
- NR_SVC_SUPP_GIDS);
- } else {
- int n;
- svc->gid = decode_uid(args[1]);
- for (n = 2; n < nargs; n++) {
- svc->supp_gids[n-2] = decode_uid(args[n]);
- }
- svc->nr_supp_gids = n - 2;
- }
- break;
- //keycodes命令处理,service命令组合键启动
- case K_keycodes:
- if (nargs < 2) {
- parse_error(state, "keycodes option requires atleast one keycode\n");
- } else {
- svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
- if (!svc->keycodes) {
- parse_error(state, "could not allocate keycodes\n");
- } else {
- svc->nkeycodes = nargs - 1;
- for (i = 1; i < nargs; i++) {
- svc->keycodes[i - 1] = atoi(args[i]);
- }
- }
- }
- break;
- //oneshot命令处理
- case K_oneshot:
- svc->flags |= SVC_ONESHOT;
- break;
- //onrestart命令处理
- case K_onrestart:
- nargs--;
- args++;
- //onrestart restart zygote
- //查找onrestart后的参数类型
- kw = lookup_keyword(args[0]);
- //如果不属于COMMAND类型,跳出不处理
- if (!kw_is(kw, COMMAND)) {
- parse_error(state, "invalid command '%s'\n", args[0]);
- break;
- }
- //读取该命令的参数个数
- kw_nargs = kw_nargs(kw);
- //验证参数个数
- if (nargs < kw_nargs) {
- parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
- kw_nargs > 2 ? "arguments" : "argument");
- break;
- }
- //创建一个command
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->nargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- //添加到svc->onrestart.commands链表中
- list_add_tail(&svc->onrestart.commands, &cmd->clist);
- break;
- //critical命令处理
- case K_critical:
- svc->flags |= SVC_CRITICAL;
- break;
- //setenv命令处理
- case K_setenv: { /* name value */
- struct svcenvinfo *ei;
- if (nargs < 2) {
- parse_error(state, "setenv option requires name and value arguments\n");
- break;
- }
- //创建一个环境变量svcenvinfo
- ei = calloc(1, sizeof(*ei));
- if (!ei) {
- parse_error(state, "out of memory\n");
- break;
- }
- ei->name = args[1];
- ei->value = args[2];
- //添加到svc->envvars链表中
- ei->next = svc->envvars;
- svc->envvars = ei;
- break;
- }
- //socket命令处理
- case K_socket: {/* name type perm [ uid gid ] */
- struct socketinfo *si;
- if (nargs < 4) {
- parse_error(state, "socket option requires name, type, perm arguments\n");
- break;
- }
- if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
- && strcmp(args[2],"seqpacket")) {
- parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
- break;
- }
- //创建一个socket信息结构
- si = calloc(1, sizeof(*si));
- if (!si) {
- parse_error(state, "out of memory\n");
- break;
- }
- si->name = args[1];
- si->type = args[2];
- si->perm = strtoul(args[3], 0, 8);
- if (nargs > 4)
- si->uid = decode_uid(args[4]);
- if (nargs > 5)
- si->gid = decode_uid(args[5]);
- //添加到svc->sockets链表中
- si->next = svc->sockets;
- svc->sockets = si;
- break;
- }
- //user命令处理
- case K_user:
- if (nargs != 2) {
- parse_error(state, "user option requires a user id\n");
- } else {
- svc->uid = decode_uid(args[1]);
- }
- break;
- //seclabel命令处理
- case K_seclabel:
- #ifdef HAVE_SELINUX
- if (nargs != 2) {
- parse_error(state, "seclabel option requires a label string\n");
- } else {
- svc->seclabel = args[1];
- }
- #endif
- break;
- default:
- parse_error(state, "invalid option '%s'\n", args[0]);
- }
- }
- static void *parse_action(struct parse_state *state, int nargs, char **args)
- {
- struct action *act;
- if (nargs < 2) {
- parse_error(state, "actions must have a trigger\n");
- return 0;
- }
- if (nargs > 2) {
- parse_error(state, "actions may not have extra parameters\n");
- return 0;
- }
- //创建一个action
- act = calloc(1, sizeof(*act));
- act->name = args[1];
- list_init(&act->commands);
- //添加到action_list链表中
- list_add_tail(&action_list, &act->alist);
- return act;
- }
1) act->name为on section的名字(比如boot/fs/);
2) 初始化list act->commands;
3) 把act->alist加入到action_list的列尾
这样,action创建并加入到了action_list中。
4.Action 命令解析- static void parse_line_action(struct parse_state* state, int nargs, char **args)
- {
- struct command *cmd;
- //获取解析得到的action
- struct action *act = state->context;
- int (*func)(int nargs, char **args);
- int kw, n;
- if (nargs == 0) {
- return;
- }
- //查找关键字类型
- kw = lookup_keyword(args[0]);
- //如果不是COMMAND类型,跳出不处理
- if (!kw_is(kw, COMMAND)) {
- parse_error(state, "invalid command '%s'\n", args[0]);
- return;
- }
- //得到命令参数个数,验证参数个数的合法性
- n = kw_nargs(kw);
- if (nargs < n) {
- parse_error(state, "%s requires %d %s\n", args[0], n - 1,
- n > 2 ? "arguments" : "argument");
- return;
- }
- //创建命令command
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->nargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- //将command添加到act->commands链表中
- list_add_tail(&act->commands, &cmd->clist);
- }
对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 命令解析
- void parse_import(struct parse_state *state, int nargs, char **args)
- {
- struct listnode *import_list = state->priv;
- struct import *import;
- char conf_file[PATH_MAX];
- int ret;
- //参数个数判断
- if (nargs != 2) {
- ERROR("single argument needed for import\n");
- return;
- }
- ret = expand_props(conf_file, args[1], sizeof(conf_file));
- if (ret) {
- ERROR("error while handling import on line '%d' in '%s'\n",
- state->line, state->filename);
- return;
- }
- //创建一个import
- import = calloc(1, sizeof(struct import));
- //设置import文件名称
- import->filename = strdup(conf_file);
- //添加到import->list链表中
- list_add_tail(import_list, &import->list);
- INFO("found import '%s', adding to import list", import->filename);
- }
当init.rc文件解析完成后,将从import_list链表中取出通过关键字import导入的其他rc文件,并调用init_parse_config_file函数进行解析:
- list_for_each(node, &import_list) {
- struct import *import = node_to_item(node, struct import, list);
- int ret;
- INFO("importing '%s'", import->filename);
- ret = init_parse_config_file(import->filename);
- if (ret)
- ERROR("could not import file '%s' from '%s'\n",
- import->filename, fn);
- }
添加Action到待执行队列
当解析完所有的init.rc内容之后,在执行这些action之前,需要按顺序将其置于一个待执行队列中
- void action_for_each_trigger(const char *trigger,void (*func)(struct action *act))
- {
- struct listnode *node;
- struct action *act;
- //遍历action_list链表,根据名字查找相关的action
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- if (!strcmp(act->name, trigger)) {
- //回调action_add_queue_tail函数
- func(act);
- }
- }
- }
- void action_add_queue_tail(struct action *act)
- {
- list_add_tail(&action_queue, &act->qlist);
- }
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的队尾。
- void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
- {
- struct action *act;
- struct command *cmd;
- //创建一个Action
- act = calloc(1, sizeof(*act));
- act->name = name;
- list_init(&act->commands);
- //为该Action创建一个command
- cmd = calloc(1, sizeof(*cmd));
- cmd->func = func;
- cmd->args[0] = name;
- //将该command添加到Action的commands链表中
- list_add_tail(&act->commands, &cmd->clist);
- //将该Action添加到action_list链表中
- list_add_tail(&action_list, &act->alist);
- //将该Action添加到待执行队列action_queue中
- action_add_queue_tail(act);
- }
添加Action到待执行队列
init 进程通过action_for_each_trigger 和queue_builtin_action 函数向待执行队列action_queue依次添加了以下Action:
- action_for_each_trigger("early-init", action_add_queue_tail);
- queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
- queue_builtin_action(keychord_init_action, "keychord_init");
- queue_builtin_action(console_init_action, "console_init");
- /* execute all the boot actions to get us started */
- action_for_each_trigger("init", action_add_queue_tail);
- /* skip mounting filesystems in charger mode */
- action_for_each_trigger("early-fs", action_add_queue_tail);
- action_for_each_trigger("fs", action_add_queue_tail);
- action_for_each_trigger("post-fs", action_add_queue_tail);
- if (!is_charger) {
- //action_for_each_trigger("post-fs", action_add_queue_tail);
- action_for_each_trigger("post-fs-data", action_add_queue_tail);
- }
- queue_builtin_action(property_service_init_action, "property_service_init");
- queue_builtin_action(signal_init_action, "signal_init");
- queue_builtin_action(check_startup_action, "check_startup");
- if (!strcmp(bootmode, "alarm")) {
- action_for_each_trigger("alarm", action_add_queue_tail);
- }
- if (is_charger) {
- action_for_each_trigger("charger", action_add_queue_tail);
- } else {
- action_for_each_trigger("early-boot", action_add_queue_tail);
- action_for_each_trigger("boot", action_add_queue_tail);
- }
- /* run all property triggers based on current state of the properties */
- queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
- #if BOOTCHART
- queue_builtin_action(bootchart_init_action, "bootchart_init");
- #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,直到冷启动结束。
- static int wait_for_coldboot_done_action(int nargs, char **args)
- {
- int ret;
- INFO("wait for %s\n", coldboot_done);
- // /dev/.coldboot_done
- //#define COMMAND_RETRY_TIMEOUT 5
- ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT);
- if (ret)
- ERROR("Timed out waiting for %s\n", coldboot_done);
- return ret;
- }
keychord_init
keychord是组合按键,Android暂时还不支持keychord机制,keychord机制就是在init.rc文件中为每个服务配置组合键,在服务解析时为指定服务设置相应的键码值。
- static int keychord_init_action(int nargs, char **args)
- {
- keychord_init();
- return 0;
- }
- void keychord_init()
- {
- int fd, ret;
- //遍历service_list链表,为每个service分配keychord_id
- service_for_each(add_service_keycodes);
- /* nothing to do if no services require keychords */
- if (!keychords)
- return;
- //打开/dev/keychord设备文件
- fd = open("/dev/keychord", O_RDWR);
- if (fd < 0) {
- ERROR("could not open /dev/keychord\n");
- return;
- }
- //设置设备属性
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- //将keychords数组内容写入设备文件中
- ret = write(fd, keychords, keychords_length);
- if (ret != keychords_length) {
- ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
- close(fd);
- fd = -1;
- }
- free(keychords);
- keychords = 0;
- keychord_fd = fd;
- }
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中。
- static int console_init_action(int nargs, char **args)
- {
- int fd;
- char tmp[PROP_VALUE_MAX];
- if (console[0]) {
- snprintf(tmp, sizeof(tmp), "/dev/%s", console);
- console_name = strdup(tmp);
- }
- fd = open(console_name, O_RDWR);
- if (fd >= 0)
- have_console = 1;
- close(fd);
- //加载开机图片
- if( load_565rle_image(INIT_IMAGE_FILE) ) {
- fd = open("/dev/tty0", O_WRONLY);
- if (fd >= 0) {
- const char *msg;
- msg = "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n" // console is 40 cols x 30 lines
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- " A N D R O I D ";
- write(fd, msg, strlen(msg));
- close(fd);
- }
- }
- return 0;
- }
property_service_init
读取属性文件,并设置相关属性。关于Android属性系统,请查看Android 系统属性SystemProperty分析
- static int property_service_init_action(int nargs, char **args)
- {
- /* read any property files on system or data and
- * fire up the property service. This must happen
- * after the ro.foo properties are set above so
- * that /data/local.prop cannot interfere with them.
- */
- start_property_service();
- return 0;
- }
signal_init
创建套接字对,以便init进程在收到子进程终止的SIGCHLD信号时调用相应的handler
- static int signal_init_action(int nargs, char **args)
- {
- signal_init();
- return 0;
- }
- void signal_init(void)
- {
- int s[2];
- struct sigaction act;
- act.sa_handler = sigchld_handler; //设置handler回调函数
- act.sa_flags = SA_NOCLDSTOP;
- act.sa_mask = 0;
- act.sa_restorer = NULL;
- sigaction(SIGCHLD, &act, 0); //安装信号处理器
- /* create a signalling mechanism for the sigchld handler */
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
- signal_fd = s[0];
- signal_recv_fd = s[1];
- fcntl(s[0], F_SETFD, FD_CLOEXEC);
- fcntl(s[0], F_SETFL, O_NONBLOCK);
- fcntl(s[1], F_SETFD, FD_CLOEXEC);
- fcntl(s[1], F_SETFL, O_NONBLOCK);
- }
- handle_signal();
- }
check_startup
检查属性socket句柄及信号句柄是否安装成功
- static int check_startup_action(int nargs, char **args)
- {
- /* */
- if ((get_property_set_fd() < 0) ||(get_signal_fd() < 0)) {
- ERROR("init startup failure\n");
- exit(1);
- }
- /* signal that we hit this point */
- unlink("/dev/.booting");
- return 0;
- }
根据当前属性值来触发该属性对应的动作
- static int queue_property_triggers_action(int nargs, char **args)
- {
- queue_all_property_triggers();
- /* enable property triggers */
- property_triggers_enabled = 1;
- return 0;
- }
- void queue_all_property_triggers()
- {
- struct listnode *node;
- struct action *act;
- //遍历action_list链表
- list_for_each(node, &action_list) {
- //取得每个节点下对应的action
- act = node_to_item(node, struct action, alist);
- //如果该action的名字以property开头
- if (!strncmp(act->name, "property:", strlen("property:"))) {
- //读取该属性的名称
- const char* name = act->name + strlen("property:");
- //读取该属性的值
- const char* equals = strchr(name, '=');
- if (equals) {
- char prop_name[PROP_NAME_MAX + 1];
- const char* value;
- int length = equals - name;
- if (length > PROP_NAME_MAX) {
- ERROR("property name too long in trigger %s", act->name);
- } else {
- memcpy(prop_name, name, length);
- prop_name[length] = 0;
- /* 从属性系统中读取该属性的值*/
- value = property_get(prop_name);
- //如果属性系统中的值等于init.rc文件中设置的触发值
- if (value && (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*"))) {
- //将该Action添加到待执行队列action_queue中
- action_add_queue_tail(act);
- }
- }
- }
- }
- }
- }
Action - boot
在boot动作中启动所有的service服务,启动命令如下:
- class_start core
- class_start main
- service adbd /sbin/adbd
- class core
- disabled
- int do_class_start(int nargs, char **args)
- {
- /* Starting a class does not start services
- * which are explicitly disabled. They must
- * be started individually.
- */
- service_for_each_class(args[1], service_start_if_not_disabled);
- return 0;
- }
- void service_for_each_class(const char *classname,
- void (*func)(struct service *svc))
- {
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (!strcmp(svc->classname, classname)) {
- func(svc);
- }
- }
- }
- static void service_start_if_not_disabled(struct service *svc)
- {
- if (!(svc->flags & SVC_DISABLED)) {
- service_start(svc, NULL);
- }
- }
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信号进行处理,即回收那些已经变成了僵尸的子进程。
- for(;;) {
- int nr, i, timeout = -1;
- execute_one_command();①
- restart_processes();②
- if (!property_set_fd_init && get_property_set_fd() > 0) {③
- ufds[fd_count].fd = get_property_set_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- property_set_fd_init = 1;
- }
- if (!signal_fd_init && get_signal_fd() > 0) {
- ufds[fd_count].fd = get_signal_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- signal_fd_init = 1;
- }
- if (!keychord_fd_init && get_keychord_fd() > 0) {
- ufds[fd_count].fd = get_keychord_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- keychord_fd_init = 1;
- }
- if (process_needs_restart) {④
- timeout = (process_needs_restart - gettime()) * 1000;
- if (timeout < 0)
- timeout = 0;
- }
- if (!action_queue_empty() || cur_action)
- timeout = 0;
- #if BOOTCHART
- if (bootchart_count > 0) {
- if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
- timeout = BOOTCHART_POLLING_MS;
- if (bootchart_step() < 0 || --bootchart_count == 0) {
- bootchart_finish();
- bootchart_count = 0;
- }
- }
- #endif
- nr = poll(ufds, fd_count, timeout);⑤
- if (nr <= 0)
- continue;
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents == POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();⑥
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();⑦
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();⑧
- }
- }
- }
从待执行队列action_queue中取出一个Action来执行,并且将已经执行完的Action从action_queue队列中移除。
- void execute_one_command(void)
- {
- int ret;
- if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
- cur_action = action_remove_queue_head();
- cur_command = NULL;
- if (!cur_action)
- return;
- INFO("processing action %p (%s)\n", cur_action, cur_action->name);
- cur_command = get_first_command(cur_action);
- } else {
- cur_command = get_next_command(cur_action, cur_command);
- }
- if (!cur_command)
- return;
- ret = cur_command->func(cur_command->nargs, cur_command->args);
- INFO("command '%s' r=%d\n", cur_command->args[0], ret);
- }
2) 从cur_action获得struct command *赋给cur_command;
3) 执行cur_command->func(cur_command->nargs, cur_command->args)
2).服务重启
当内存不足时,Android系统会自动杀死一下进程来释放空间,所以当某些重要的服务被杀,同时该服务进程并未设置为oneshot,则必须重新启动该服务进程。
- static void restart_processes()
- {
- process_needs_restart = 0;
- service_for_each_flags(SVC_RESTARTING,restart_service_if_needed);
- }
- void service_for_each_flags(unsigned matchflags,
- void (*func)(struct service *svc))
- {
- struct listnode *node;
- struct service *svc;
- list_for_each(node, &service_list) {
- svc = node_to_item(node, struct service, slist);
- if (svc->flags & matchflags) {
- func(svc);
- }
- }
- }
- static void restart_service_if_needed(struct service *svc)
- {
- time_t next_start_time = svc->time_started + 5;
- if (next_start_time <= gettime()) {
- svc->flags &= (~SVC_RESTARTING);
- service_start(svc, NULL);
- return;
- }
- if ((next_start_time < process_needs_restart) ||
- (process_needs_restart == 0)) {
- process_needs_restart = next_start_time;
- }
- }
3.设置句柄池
- if (!property_set_fd_init && get_property_set_fd() > 0) {
- ufds[fd_count].fd = get_property_set_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- property_set_fd_init = 1;
- }
- if (!signal_fd_init && get_signal_fd() > 0) {
- ufds[fd_count].fd = get_signal_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- signal_fd_init = 1;
- }
- if (!keychord_fd_init && get_keychord_fd() > 0) {
- ufds[fd_count].fd = get_keychord_fd();
- ufds[fd_count].events = POLLIN;
- ufds[fd_count].revents = 0;
- fd_count++;
- keychord_fd_init = 1;
- }
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时,只有事件发生才会返回。
- if (process_needs_restart) {
- timeout = (process_needs_restart - gettime()) * 1000;
- if (timeout < 0)
- timeout = 0;
- }
- if (!action_queue_empty() || cur_action)
- timeout = 0;
5.事件监控
- nr = poll(ufds, fd_count, timeout);
- if (nr <= 0)
- continue;
6.事件处理
当监控的句柄池中的句柄发生了某些事件时,返回事件发生对应的句柄,从而进入该句柄对应的事件处理函数中。
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents == POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
1.属性设置事件;
2.键盘组合事件;
3.子进程死亡信号事件;
对于属性设置事件处理handle_property_set_fd(),请查看Android 系统属性SystemProperty分析。由于Android系统暂时未使用keychord机制,因此这里不详细介绍。
- void handle_keychord()
- {
- struct service *svc;
- const char* debuggable;
- const char* adb_enabled;
- int ret;
- __u16 id;
- // only handle keychords if ro.debuggable is set or adb is enabled.
- // the logic here is that bugreports should be enabled in userdebug or eng builds
- // and on user builds for users that are developers.
- debuggable = property_get("ro.debuggable");
- adb_enabled = property_get("init.svc.adbd");
- ret = read(keychord_fd, &id, sizeof(id));
- if (ret != sizeof(id)) {
- ERROR("could not read keychord id\n");
- return;
- }
- //只有在调试模式下才使用
- if ((debuggable && !strcmp(debuggable, "1")) ||
- (adb_enabled && !strcmp(adb_enabled, "running"))) {
- svc = service_find_by_keychord(id); //根据keychord_id查找指定的服务
- if (svc) {
- INFO("starting service %s from keychord\n", svc->name); //通过发送组合键消息来启动某些服务
- service_start(svc, NULL);
- } else {
- ERROR("service for keychord %d not found\n", id);
- }
- }
- }
当init进程的某个子进程终止时,会对系统的运行产生影响,因此init进程需要重新启动他们。当init的子进程意外终止时,会向父进程init进程传递SIGCHLD信号,init进程接收到该信号时,预先安装的handler将被调用,将SIGCHLD信号的编号写入socket对的一端,在socket另一端通过poll系统调用监控到事件的发生,将调用子进程死亡事件处理函数。
当init子进程终止时,init进程会接收到SIGCHLD信号,前面已经介绍了init进程首先安装了信号处理器,因此当接收到SIGCHLD信号时,init进程会调用与该信号相对应的处理函数sigchld_handler:
- static void sigchld_handler(int s)
- {
- write(signal_fd, &s, 1);
- }
- void handle_signal(void)
- {
- char tmp[32];
- //读取socket接收端的数据
- /* we got a SIGCHLD - reap and restart as needed */
- read(signal_recv_fd, tmp, sizeof(tmp));
- while (!wait_for_one_process(0))
- ;
- }
- static int wait_for_one_process(int block)
- { //block = 0 -->false
- pid_t pid;
- int status;
- struct service *svc;
- struct socketinfo *si;
- time_t now;
- struct listnode *node;
- struct command *cmd;
- /*当进程被终止时,将发送SIGCHLD信号,waitpid()函数用来回收进程所占用的资源,第一个参
- 数pid是指欲等待的子进程的识别码,设置为-1表示查看所有子进程是否发出SIGCHIL信号,第二
- 个参数status用于返回子进程的结束状态;第三个参数决定waitpid()函数是否应用阻塞处理方式。
- waitpid()函数返回产生SIGCHID信号的进程pid */
- while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
- //正常情况下返回的死亡进程pid大于0,因此wait_for_one_process的返回值正常情况下为0
- if (pid <= 0) return -1;
- INFO("waitpid returned pid %d, status = %08x\n", pid, status);
- //用于根据pid值在服务链表中查找对应的服务
- svc = service_find_by_pid(pid);
- if (!svc) {
- ERROR("untracked pid %d exited\n", pid);
- return 0;
- }
- NOTICE("process '%s', pid %d exited\n", svc->name, pid);
- /* 检查服务是否设置了oneshot标志,SVC_ONESHOT表示进程仅运行一次,如果没有设置SVC_ONESHOT标志,
- 表示需要重启该服务进程,首先将该服务进程组下的所有子进程杀死 */
- if (!(svc->flags & SVC_ONESHOT)) {
- kill(-pid, SIGKILL);
- NOTICE("process '%s' killing any children in process group\n", svc->name);
- }
- /* 删除该服务进程下的创建的所有socket */
- for (si = svc->sockets; si; si = si->next) {
- char tmp[128];
- snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
- unlink(tmp);
- }
- //设置服务的pid为0 ,并清除SVC_RUNNING标志
- svc->pid = 0;
- svc->flags &= (~SVC_RUNNING);
- /* 如果设置了SVC_ONESHOT标志,表示服务只能运行一次,因此设置表示位SVC_DISABLED */
- if (svc->flags & SVC_ONESHOT) {
- svc->flags |= SVC_DISABLED;
- }
- /* 判断服务标志是否设置了SVC_DISABLED 或 SVC_RESET 对于设置了这两种标志的进程是不能重启的 */
- if (svc->flags & (SVC_DISABLED | SVC_RESET) ) {
- //设置进程运行状态属性值为stopped
- notify_service_state(svc->name, "stopped");
- return 0;
- }
- now = gettime();
- //如果死亡的服务进程是系统关键进程,则直接重启手机
- if (svc->flags & SVC_CRITICAL) {
- if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
- if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
- ERROR("critical process '%s' exited %d times in %d minutes; "
- "rebooting into recovery mode\n", svc->name,
- CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
- //手机重启
- android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
- return 0;
- }
- } else {
- svc->time_crashed = now;
- svc->nr_crashed = 1;
- }
- }
- //设置服务进程标志SVC_RESTARTING,在restart_processes()函数中会重启持有SVC_RESTARTING
- svc->flags |= SVC_RESTARTING;
- /* 运行该service下所有Execute all onrestart commands for this service. */
- list_for_each(node, &svc->onrestart.commands) {
- cmd = node_to_item(node, struct command, clist);
- cmd->func(cmd->nargs, cmd->args);
- }
- //设置进程运行状态属性值为stopped
- notify_service_state(svc->name, "restarting");
- return 0;
- }
- 转载: http://blog.youkuaiyun.com/yangwen123/article/details/9029959