《深入理解Android(卷1)》笔记 2.第三章 深入理解init
2012-11-30 13:13 ...平..淡... 阅读(1419) 评论(3) 编辑 收藏 举报重拾书本...
作者是基于2.2源码分析的,由于我参照的是2.3,有些方法略有不同,我按照2.3的来总结,不过会在不同的地方标记下。
第三章 深入理解init
知识点1:init概述
init是一个进程,而且是Linux系统中用户空间的第一个进程。主要工作概述为:
(1)init负责创建系统中的几个关键进程,包括zygote。
(2)init提供property service(属性服务)来管理Android系统的属性。
这一章是从 init如何创建zygote 和 init的属性服务如何工作 两个方面分析init。
知识点2:init工作流程(精简)
(1)解析两个配置文件 (init.rc 和 init.hardware名.rc)。(init.rc 是系统配置文件,init.hardware名.rc 是与硬件平台相关的配置文件)
(2)执行各个阶段的动作。(总共4个阶段(early-init,init,early-boot,boot),创建zygote的工作就在某个阶段)
(3)调用property_init初始化属性相关的资源,并且通过property_start_service启动属性服务。
(4)init进入一个无限循环,并且等待一些事情的发生。(重点关注init如何处理来自socket和来自属性服务器的相关事情)
贴出init.c中的main方法源码
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 12 if (!strcmp(basename(argv[0]), "ueventd")) 13 return ueventd_main(argc, argv); 14 15 /* clear the umask */ 16 umask(0); 17 18 /* Get the basic filesystem setup we need put 19 * together in the initramdisk on / and then we'll 20 * let the rc file figure out the rest. 21 */ 22 mkdir("/dev", 0755); 23 mkdir("/proc", 0755); 24 mkdir("/sys", 0755); 25 26 mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755"); 27 mkdir("/dev/pts", 0755); 28 mkdir("/dev/socket", 0755); 29 mount("devpts", "/dev/pts", "devpts", 0, NULL); 30 mount("proc", "/proc", "proc", 0, NULL); 31 mount("sysfs", "/sys", "sysfs", 0, NULL); 32 33 /* We must have some place other than / to create the 34 * device nodes for kmsg and null, otherwise we won't 35 * be able to remount / read-only later on. 36 * Now that tmpfs is mounted on /dev, we can actually 37 * talk to the outside world. 38 */ 39 open_devnull_stdio(); 40 log_init(); 41 42 INFO("reading config file\n"); 43 init_parse_config_file("/init.rc"); 44 45 /* pull the kernel commandline and ramdisk properties file in */ 46 import_kernel_cmdline(0); 47 48 get_hardware_name(hardware, &revision); 49 snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); 50 init_parse_config_file(tmp); 51 52 action_for_each_trigger("early-init", action_add_queue_tail); 53 54 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); 55 queue_builtin_action(property_init_action, "property_init"); 56 queue_builtin_action(keychord_init_action, "keychord_init"); 57 queue_builtin_action(console_init_action, "console_init"); 58 queue_builtin_action(set_init_properties_action, "set_init_properties"); 59 60 /* execute all the boot actions to get us started */ 61 action_for_each_trigger("init", action_add_queue_tail); 62 action_for_each_trigger("early-fs", action_add_queue_tail); 63 action_for_each_trigger("fs", action_add_queue_tail); 64 action_for_each_trigger("post-fs", action_add_queue_tail); 65 66 queue_builtin_action(property_service_init_action, "property_service_init"); 67 queue_builtin_action(signal_init_action, "signal_init"); 68 queue_builtin_action(check_startup_action, "check_startup"); 69 70 /* execute all the boot actions to get us started */ 71 action_for_each_trigger("early-boot", action_add_queue_tail); 72 action_for_each_trigger("boot", action_add_queue_tail); 73 74 /* run all property triggers based on current state of the properties */ 75 queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers"); 76 77 78 #if BOOTCHART 79 queue_builtin_action(bootchart_init_action, "bootchart_init"); 80 #endif 81 82 for(;;) { 83 int nr, i, timeout = -1; 84 85 execute_one_command(); 86 restart_processes(); 87 88 if (!property_set_fd_init && get_property_set_fd() > 0) { 89 ufds[fd_count].fd = get_property_set_fd(); 90 ufds[fd_count].events = POLLIN; 91 ufds[fd_count].revents = 0; 92 fd_count++; 93 property_set_fd_init = 1; 94 } 95 if (!signal_fd_init && get_signal_fd() > 0) { 96 ufds[fd_count].fd = get_signal_fd(); 97 ufds[fd_count].events = POLLIN; 98 ufds[fd_count].revents = 0; 99 fd_count++; 100 signal_fd_init = 1; 101 } 102 if (!keychord_fd_init && get_keychord_fd() > 0) { 103 ufds[fd_count].fd = get_keychord_fd(); 104 ufds[fd_count].events = POLLIN; 105 ufds[fd_count].revents = 0; 106 fd_count++; 107 keychord_fd_init = 1; 108 } 109 110 if (process_needs_restart) { 111 timeout = (process_needs_restart - gettime()) * 1000; 112 if (timeout < 0) 113 timeout = 0; 114 } 115 116 if (!action_queue_empty() || cur_action) 117 timeout = 0; 118 119 #if BOOTCHART 120 if (bootchart_count > 0) { 121 if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) 122 timeout = BOOTCHART_POLLING_MS; 123 if (bootchart_step() < 0 || --bootchart_count == 0) { 124 bootchart_finish(); 125 bootchart_count = 0; 126 } 127 } 128 #endif 129 130 nr = poll(ufds, fd_count, timeout); 131 if (nr <= 0) 132 continue; 133 134 for (i = 0; i < fd_count; i++) { 135 if (ufds[i].revents == POLLIN) { 136 if (ufds[i].fd == get_property_set_fd()) 137 handle_property_set_fd(); 138 else if (ufds[i].fd == get_keychord_fd()) 139 handle_keychord(); 140 else if (ufds[i].fd == get_signal_fd()) 141 handle_signal(); 142 } 143 } 144 } 145 146 return 0; 147 }
知识点3:解析配置文件
1.init首先调用init_parse_config_file()方法(2.2上是parse_config_file())来进行解析。[-->init_parser.c] (2.2中是在parser.c)
int init_parse_config_file(const char *fn) { char *data; data = read_file(fn, 0); //读取配置文件的内容 if (!data) return -1; parse_config(fn, data); //调用parse_config做真正的解析 DUMP(); return 0; }
2.该方法中会调用首先调用read_file读取配置文件内容,然后调用parse_config进行解析。[-->init_parser.c]
static void parse_config(const char *fn, char *s) { struct parse_state state; 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; //设置解析函数,不同的内容用不同的解析函数 for (;;) { switch (next_token(&state)) { case T_EOF: state.parse_line(&state, 0, 0); return; case T_NEWLINE: //新的一行 state.line++; if (nargs) { int kw = lookup_keyword(args[0]); //得到关键字的类型,比如service,则返回K_service if (kw_is(kw, SECTION)) { //判断关键字类型是不是SECTION,只有service和on满足 state.parse_line(&state, 0, 0); parse_new_section(&state, kw, nargs, args); //解析这个SECTION } else { state.parse_line(&state, nargs, args); } nargs = 0; } break; case T_TEXT: if (nargs < INIT_PARSER_MAXARGS) { args[nargs++] = state.text; } break; } } }
分析:parse_config方法中的无限for循环,主要对init.rc的内容进行解析,以一行一行进行读取,每读取完一行内容换行到下一行时,使用lookup_keyword分析已经读取的一行的第一个参数。在case T_NEWLINE中首先找到配置文件的一个section(参考a),然后调用parse_new_section方法,它会针对不同的section 使用不同的解析函数来解析。section和init.rc文件的组织结构有关。
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) { case K_service: //如果symbol是service,调用parse_service和parse_line_service方法 state->context = parse_service(state, nargs, args); if (state->context) { state->parse_line = parse_line_service; return; } break; case K_on: //如果symbol是on,调用parse_action和parse_line_action方法 state->context = parse_action(state, nargs, args); if (state->context) { state->parse_line = parse_line_action; return; } break; } state->parse_line = parse_line_no_op; }
(a)根据keywords.h的定义,当symbol为on或service时,关键字才被认为是section。(关键字中最重要的是symbol和flags,请阅读(3) )
keywords.h中定义如下:
KEYWORD(on, SECTION, 0, 0) KEYWORD(service, SECTION, 0, 0)
下面来分析关键字。
3.关键字定义[--->keywords.h] keywords.h中定义了init中使用的关键字。
#ifndef KEYWORD //声明一个函数,这些函数就是前面所说的Action的执行函数。 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(int nargs, char **args); int do_restart(int nargs, char **args); int do_rm(int nargs, char **args); int do_rmdir(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_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_wait(int nargs, char **args); #define __MAKE_KEYWORD_ENUM__ //定义一个宏 /* 定义KEYWORD宏,虽然有四个参数,但这里只用第一个,其中K——##symbol中的##表示连接的意思,即最后得到的值为K——symbol。symbol其实就是init.rc中的关键字。 */ #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, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(rm, COMMAND, 1, do_rm) KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(service, SECTION, 0, 0) KEYWORD(setenv, OPTION, 2, 0) KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props) KEYWORD(ioprio, OPTION, 0, 0) #ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT, }; #undef __MAKE_KEYWORD_ENUM__ #undef KEYWORD //取消KEYWORD宏的定义 #endif
keywords.h的使用,主要是在 [--->init_parser.c]中总共2次包含了keywords.h头文件。
//第一次包含keywords.h,根据keywords.h的代码,首先会得到一个枚举定义。 #include "keywords.h" /* 重新定义KEYWORD宏,这回四个参数全用上了,看起来好像是一个结构体。其中#symbol表示一个字符串,其值为“symbol” */ #define KEYWORD(symbol, flags, nargs, func) \ [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, /* 定义一个结构体keyword_info数组,它用来描述关键字的一些属性,请注意里面的注释内容。 */ struct { const char *name; //关键字的名称 int (*func)(int nargs, char **args); //对应关键字的处理函数 unsigned char nargs; //参数个数,每个关键字的参数个数是固定的 unsigned char flags; //关键字的属性:COMMAND、OPTION、SECTION } keyword_info[KEYWORD_COUNT] = { [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, /* 第二次包含keywords.h,由于已经重新定义了KEYWORD宏,所以以前那些作为枚举值的关键字现在变成了keyword_info数组的索引了。 */ #include "keywords.h" }; #undef KEYWORD #define kw_is(kw, type) (keyword_info[kw].flags & (type)) #define kw_name(kw) (keyword_info[kw].name) #define kw_func(kw) (keyword_info[kw].func) #define kw_nargs(kw) (keyword_info[kw].nargs)
主要做了两件事:
(1)第一次包含keywords.h时,它声明了一些诸如do_class_start的函数,另外还定义了一个枚举,枚举值为K_class,K_mkdir等关键字。
(2)第二次包含keywords.h后,得到了一个keyword_info结构体数组,这个keyword_info结构体数组以前面定义的枚举值为索引,存储对应的关键字信息,这些信息包括关键字名称、处理函数、处理函数的参数个数,以及属性。
4.解析init.rc
on early-init start ueventd 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 EXTERNAL_STORAGE /mnt/sdcard export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb export BOOTCLASSPATH /system/framework/core.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar export TMPDIR /data/local/tmp # 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 mountpoints mkdir /mnt 0775 root system mkdir /mnt/sdcard 0000 system system # Create cgroup mount point for cpu accounting mkdir /acct mount cgroup none /acct cpuacct mkdir /acct/uid # Backwards Compat - XXX: Going away in G* symlink /mnt/sdcard /sdcard mkdir /system mkdir /data 0771 system system mkdir /cache 0770 system cache mkdir /config 0500 root root # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root # Directory for staging bindmounts mkdir /mnt/secure/staging 0700 root root # Directory-target for where the secure container # imagefile directory will be bind-mounted mkdir /mnt/secure/asec 0700 root root # Secure container public mount points. mkdir /mnt/asec 0700 root system mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000 # Filesystem image public mount points. mkdir /mnt/obb 0700 root system mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 write /proc/sys/kernel/sched_latency_ns 10000000 write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000 write /proc/sys/kernel/sched_compat_yield 1 write /proc/sys/kernel/sched_child_runs_first 0 # XXX for development only write /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 600000 write /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 600000 # 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 0777 /dev/cpuctl/tasks write /dev/cpuctl/cpu.shares 1024 mkdir /dev/cpuctl/fg_boost chown system system /dev/cpuctl/fg_boost/tasks chmod 0777 /dev/cpuctl/fg_boost/tasks write /dev/cpuctl/fg_boost/cpu.shares 1024 mkdir /dev/cpuctl/bg_non_interactive chown system system /dev/cpuctl/bg_non_interactive/tasks chmod 0777 /dev/cpuctl/bg_non_interactive/tasks # 5.0 % write /dev/cpuctl/bg_non_interactive/cpu.shares 52 on fs # mount mtd partitions # Mount /system rw first to give the filesystem a chance to save a checkpoint mount yaffs2 mtd@system /system mount yaffs2 mtd@system /system ro remount mount yaffs2 mtd@userdata /data nosuid nodev mount yaffs2 mtd@persist /persist nosuid nodev mount yaffs2 mtd@cache /cache nosuid nodev mount yaffs2 mtd@persist /persist nosuid nodev #if we have a fat.img, bind it to loop7 exec /system/xbin/fatimgcheck losetup /dev/block/loop7 /data/fat.img on emmc-fs # mount mmc partitions wait /dev/block/mmcblk0p12 mount ext4 /dev/block/mmcblk0p12 /system ro barrier=1 wait /dev/block/mmcblk0p13 exec /system/bin/e2fsck -p /dev/block/mmcblk0p13 mount ext4 /dev/block/mmcblk0p13 /data nosuid nodev barrier=1 noauto_da_alloc,noinit_itable,nodelalloc mount ext4 /dev/block/mmcblk0p14 /persist nosuid nodev barrier=1 mount ext4 /dev/block/mmcblk0p15 /cache nosuid nodev barrier=1 on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount # We chown/chmod /data again so because mount is run as root + defaults chown system system /data chmod 0771 /data # Mounting of persist is moved to 'on emmc-fs' and 'on fs' sections # We chown/chmod /persist again so because mount is run as root + defaults chown system system /persist chmod 0771 /persist # Create dump dir and collect dumps. # Do this before we mount cache so eventually we can use cache for # storing dumps on platforms which do not have a dedicated dump partition. mkdir /data/dontpanic chown root log /data/dontpanic chmod 0750 /data/dontpanic # Collect apanic data, free resources and re-arm trigger copy /proc/apanic_console /data/dontpanic/apanic_console chown root log /data/dontpanic/apanic_console chmod 0640 /data/dontpanic/apanic_console copy /proc/apanic_threads /data/dontpanic/apanic_threads chown root log /data/dontpanic/apanic_threads chmod 0640 /data/dontpanic/apanic_threads write /proc/apanic_console 1 # Same reason as /data above chown system cache /cache chmod 0770 /cache # 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 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/vpn 0770 system system mkdir /data/misc/systemkeys 0700 system system mkdir /data/misc/vpn/profiles 0770 system system # give system access to wpa_supplicant.conf for backup and restore mkdir /data/misc/wifi 0770 wifi wifi chmod 0770 /data/misc/wifi chmod 0660 /data/misc/wifi/wpa_supplicant.conf chown wifi root /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0771 shell shell mkdir /data/local/tmp 0771 shell shell mkdir /data/data 0771 system system mkdir /data/app-private 0771 system system mkdir /data/app 0771 system system mkdir /data/property 0700 root root # create dalvik-cache and double-check the perms mkdir /data/dalvik-cache 0771 system system chown system system /data/dalvik-cache chmod 0771 /data/dalvik-cache # create the lost+found directories, so as to enforce our permissions mkdir /data/lost+found 0770 mkdir /cache/lost+found 0770 # double check the perms, in case lost+found already exists, and set owner chown root root /data/lost+found chmod 0770 /data/lost+found chown root root /cache/lost+found chmod 0770 /cache/lost+found 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 # Define the oom_adj values for the classes of processes that can be # killed by the kernel. These are used in ActivityManagerService. setprop ro.FOREGROUND_APP_ADJ 0 setprop ro.VISIBLE_APP_ADJ 1 setprop ro.PERCEPTIBLE_APP_ADJ 2 setprop ro.HEAVY_WEIGHT_APP_ADJ 3 setprop ro.SECONDARY_SERVER_ADJ 4 setprop ro.BACKUP_APP_ADJ 5 setprop ro.HOME_APP_ADJ 6 setprop ro.HIDDEN_APP_MIN_ADJ 7 setprop ro.EMPTY_APP_ADJ 15 # Define the memory thresholds at which the above process classes will # be killed. These numbers are in pages (4k). setprop ro.FOREGROUND_APP_MEM 2048 setprop ro.VISIBLE_APP_MEM 3072 setprop ro.PERCEPTIBLE_APP_MEM 4096 setprop ro.HEAVY_WEIGHT_APP_MEM 4096 setprop ro.SECONDARY_SERVER_MEM 6144 setprop ro.BACKUP_APP_MEM 6144 setprop ro.HOME_APP_MEM 6144 setprop ro.HIDDEN_APP_MEM 7168 setprop ro.EMPTY_APP_MEM 8192 # Write value must be consistent with the above properties. # Note that the driver only supports 6 slots, so we have combined some of # the classes into the same memory level; the associated processes of higher # classes will still be killed first. write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15 write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,6144,7168,8192 # Set init its forked children's oom_adj. write /proc/1/oom_adj -16 # Tweak background writeout write /proc/sys/vm/dirty_expire_centisecs 200 write /proc/sys/vm/dirty_background_ratio 5 # Permissions for System Server and daemons. chown radio system /sys/android_power/state chown radio system /sys/android_power/request_state chown radio system /sys/android_power/acquire_full_wake_lock chown radio system /sys/android_power/acquire_partial_wake_lock chown radio system /sys/android_power/release_wake_lock chown radio system /sys/power/state chown radio system /sys/power/wake_lock chown radio system /sys/power/wake_unlock chmod 0660 /sys/power/state chmod 0660 /sys/power/wake_lock chmod 0660 /sys/power/wake_unlock chown system system /sys/class/timed_output/vibrator/enable chown system system /sys/class/leds/keyboard-backlight/brightness chown system system /sys/class/leds/lcd-backlight/brightness chown system system /sys/class/leds/button-backlight/brightness chown system system /sys/class/leds/jogball-backlight/brightness chown system system /sys/class/leds/red/brightness chown system system /sys/class/leds/green/brightness chown system system /sys/class/leds/blue/brightness chown system system /sys/class/leds/red/device/grpfreq chown system system /sys/class/leds/red/device/grppwm chown system system /sys/class/leds/red/device/blink chown system system /sys/class/leds/red/brightness chown system system /sys/class/leds/green/brightness chown system system /sys/class/leds/blue/brightness chown system system /sys/class/leds/red/device/grpfreq chown system system /sys/class/leds/red/device/grppwm chown system system /sys/class/leds/red/device/blink chown system system /sys/class/timed_output/vibrator/enable chown system system /sys/module/sco/parameters/disable_esco chown system system /sys/kernel/ipv4/tcp_wmem_min chown system system /sys/kernel/ipv4/tcp_wmem_def chown system system /sys/kernel/ipv4/tcp_wmem_max chown system system /sys/kernel/ipv4/tcp_rmem_min chown system system /sys/kernel/ipv4/tcp_rmem_def chown system system /sys/kernel/ipv4/tcp_rmem_max chown root radio /proc/cmdline # QMUD / QMI keeps its sockets in here mkdir /data/radio chmod 0770 /data/radio chown radio /data/radio # Define TCP buffer sizes for various networks # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.wifi 4095,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 class_start default ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd critical service console /system/bin/sh console disabled user shell group log on property:ro.secure=0 start console # adbd is controlled by the persist.service.adb.enable system property service adbd /sbin/adbd disabled # adbd on at boot in emulator on property:ro.kernel.qemu=1 start adbd on property:persist.service.adb.enable=1 start adbd on property:persist.service.adb.enable=0 stop adbd service servicemanager /system/bin/servicemanager user system critical # onrestart restart zygote onrestart restart media service vold /system/bin/vold socket vold stream 0660 root mount ioprio be 2 service netd /system/bin/netd socket netd stream 0660 root system service debuggerd /system/bin/debuggerd #service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server # socket zygote stream 666 # onrestart write /sys/android_power/request_state wake # onrestart write /sys/power/state on # onrestart restart media # onrestart restart netd # .appcache=text/cache-manifest,.mp4=video/mp4 service httpd /system/bin/busybox httpd -f -p 127.0.0.1:7777 -h / user root group inet cache misc net_admin net_raw log service ril-daemon-b2g /system/bin/rild # FIXME: this shouldn't be 0666, but rilproxy checks for that explicitly socket rild stream 666 root radio socket rild-debug stream 660 radio system user root group radio cache inet misc audio sdcard_rw qcom_oncrpc diag service b2g /system/b2g/b2g onrestart restart media service rilproxy /system/bin/rilproxy socket rilproxy stream 660 root system socket rilproxyd stream 660 root system user root group radio cache inet misc audio sdcard_rw net_admin net_raw log service media /system/bin/mediaserver user media group system audio camera graphics inet net_bt net_bt_admin net_raw qcom_oncrpc ioprio rt 4 service bootanim /system/bin/bootanimation user graphics group graphics disabled oneshot service dbus /system/bin/dbus-daemon --system --nofork socket dbus stream 660 bluetooth bluetooth user bluetooth group bluetooth net_bt_admin service bluetoothd /system/bin/bluetoothd -n 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 hfag /system/bin/sdptool add --channel=10 HFAG user bluetooth group bluetooth net_bt_admin disabled oneshot service hsag /system/bin/sdptool add --channel=11 HSAG user bluetooth group bluetooth net_bt_admin disabled oneshot service opush /system/bin/sdptool add --channel=12 OPUSH user bluetooth group bluetooth net_bt_admin disabled oneshot service pbap /system/bin/sdptool add --channel=19 PBAP user bluetooth group bluetooth net_bt_admin disabled oneshot service installd /system/bin/installd socket installd stream 600 system system service flash_recovery /system/etc/install-recovery.sh oneshot service racoon /system/bin/racoon socket racoon stream 600 system system # racoon will setuid to vpn after getting necessary resources. group net_admin disabled oneshot service mtpd /system/bin/mtpd socket mtpd stream 600 system system user vpn group vpn net_admin net_raw disabled oneshot service keystore /system/bin/keystore /data/misc/keystore user keystore group keystore socket keystore stream 666 service dumpstate /system/bin/dumpstate -s socket dumpstate stream 0660 shell log disabled oneshot
了解以下内容前,看下/system/core/init/readme.txt对init.rc的解释 蛮有必要的!!!~~
注意:
(1) 一个section的内容从这个标识section的关键字(即symbol为on或者service)开始,到下一个标识section的地方结束。
(2) boot阶段执行的动作都是由boot这个section定义的。(同理init等阶段)
知识点4:解析 service (即服务)
1. 首先了解下三个结构体:service、action、command。
init中使用service结构体来保存于service section相关的信息。
三个结构体代码如下:
struct command { /* list of commands in an action */ struct listnode clist; int (*func)(int nargs, char **args); int nargs; char *args[1]; }; //----------------------------------------------------------- struct action { /* node in list of all actions */ struct listnode alist; /* node in the queue of pending actions */ struct listnode qlist; /* node in list of actions for a trigger */ struct listnode tlist; unsigned hash; const char *name; struct listnode commands; struct command *current; }; //------------------------------------------------------------ struct service { /* list of all services */ struct listnode slist; const char *name; //service的名字,比如"zygote" const char *classname; //service所属class的名字,默认是"default" unsigned flags; //service的属性 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; char *seclabel; struct socketinfo *sockets; struct svcenvinfo *envvars; // 用来存储command信息 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]; //用于存储参数 }; /* ^-------'args' MUST be at the end of this struct! */
2.Android的三个双向链表
现在继续知识点3的第2个小点来分析 parse_config方法,回到 parse_new_section方法中。解析函数在解析每一个section的时候:
(1) 如果是on,就表明是一个action,则把这个action加入action_list (c)中,比如init、boot都是action。而这个action下面会跟着很多的Commands,解析函数会把这个action下面的所有command链成一个双向链表,保存在action结构体的commands成员(listnode类型)中。
(2) 如果是service,表明是一个服务,则把这个service加入service_list (c)中。而这个service下面有很多options,解析函数会根据这些不同的option执行不同的动作,对服务的信息进行设置,其中包括设置环境信息,创建套接字,设置重启标记等等。
(c)Android维护了三个双向链表,分别是action_list、service_list、action_queue。
action_list存放的是所有要执行的action, service_list存放的是所有要启动的service, action_queue存放的是即将执行的action。
三个链表的创建是在init_parser.c中定义的:
static list_declare(service_list); static list_declare(action_list); static list_declare(action_queue);
而list_declare是在list.h中定义的一个宏:
#define list_declare(name) \ struct listnode name = { \ .next = &name, \ .prev = &name, \ }
*.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。
*.rc 脚本中所有 on 关键字开头的项将会被会添加到 action_list 列表中。
每个action列表项都有一个列表,此列表用来保存该段落下的 Commands。
3.分析parse_new_section方法。
在这里,通过对zygote的解析进行分析:
service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server socket zygote stream 666 onrestart write /sys/android_power/request_state wake 服务名称为: zygote 启动该服务执行的命令: /system/bin/app_process 命令的参数: -Xzygote /system/bin –zygote –start-system-server socket zygote stream 666: 创建一个名为:/dev/socket/zygote 的 socket ,类型为:stream
当解析到这段代码时,开始执行parse_service和parse_line_service两个方法。
parse_service方法:对 service 关键字开头的内容进行解析。操作包括判断service_list 链表中是否已有该service;设置类名(classname)为"default";把service加到全局链表service_list中。
源码如下:
1 static void *parse_service(struct parse_state *state, int nargs, char **args) 2 { 3 struct service *svc; 4 if (nargs < 3) { 5 parse_error(state, "services must have a name and a program\n"); 6 return 0; 7 } 8 if (!valid_name(args[1])) { 9 parse_error(state, "invalid service name '%s'\n", args[1]); 10 return 0; 11 } 12 13 svc = service_find_by_name(args[1]); //查找service_list中是否已有该服务 14 if (svc) { //若有,则直接返回 15 parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); 16 return 0; 17 } 18 19 nargs -= 2; 20 svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); 21 if (!svc) { 22 parse_error(state, "out of memory\n"); 23 return 0; 24 } 25 svc->name = args[1]; 26 svc->classname = "default"; //设置类名为default 27 memcpy(svc->args, args + 2, sizeof(char*) * nargs); 28 svc->args[nargs] = 0; 29 svc->nargs = nargs; 30 svc->onrestart.name = "onrestart"; 31 list_init(&svc->onrestart.commands); 32 list_add_tail(&service_list, &svc->slist); //添加到service_list中 33 return svc; 34 }
在调用此方法后,所有服务的svc->classname都为default,这样将导致service_list中的所有服务都将被执行。
在这里agrs[1]就是zygote,系统会先查找是否已经存在该服务,然后构建一个service svc,进行相关的填充,包括服务名,服务所属的类别名字,以及服务启动带入的参数个数(要减去service和服务名zygote),最后将这个svc加入到service_list全局链表中。随后所做的是对Service的下面几行Option进行解析,比如class,socket,onrestart等等。使用的是parse_line_service函数。
该方法中支持的option如下:
capability # 暂时未实现 class <name> # 设置名称为name的类别,感觉有点像开机启动service的优先级,默认的class名称为default console # 需要在android屏幕上打开控制台 disabled # 设置后,不能自动的通过class名称启动,必须显式的通过service名称启动 ioprio <rt|be|idle> <0-7> # 设置io优先级 group <groupname> [ <groupname> ]* # 设置服务进程的effective group ID(第一个参数)和supplementary group IDs(第二个到最后) keycodes <keycodes> [ <keycodes> ]* # keycodes相关 oneshot # 服务退出时,不再启动,但可以通过名称启动 onrestart # 服务重启时,执行的命令,可能是服务的重启的时候,需要作一些额外的工作 critical # 是device-critical service,在4分钟内退出超过4次,那么设备会重启到recover模式下 setenv <name> <value> # 设置服务的环境变量 socket <name> <type> <perm> [ <user> [ <group> ] ] # 为服务创建socket,可以创建多个 user <effectuserid> # 设置服务进程的effective user ID
来看下parse_line_service函数的源码:
1 static void parse_line_service(struct parse_state *state, int nargs, char **args) 2 { 3 struct service *svc = state->context; 4 struct command *cmd; 5 int kw, kw_nargs; 6 7 if (nargs == 0) { 8 return; 9 } 10 11 kw = lookup_keyword(args[0]); 12 switch (kw) { 13 case K_capability: 14 break; 15 case K_class: 16 if (nargs != 2) { 17 parse_error(state, "class option requires a classname\n"); 18 } else { 19 svc->classname = args[1]; 20 } 21 break; 22 case K_console: 23 svc->flags |= SVC_CONSOLE; 24 break; 25 case K_disabled: 26 svc->flags |= SVC_DISABLED; 27 break; 28 case K_group: 29 if (nargs < 2) { 30 parse_error(state, "group option requires a group id\n"); 31 } else if (nargs > NR_SVC_SUPP_GIDS + 2) { 32 parse_error(state, "group option accepts at most %d supp. groups\n", 33 NR_SVC_SUPP_GIDS); 34 } else { 35 int n; 36 svc->gid = decode_uid(args[1]); 37 for (n = 2; n < nargs; n++) { 38 svc->supp_gids[n-2] = decode_uid(args[n]); 39 } 40 svc->nr_supp_gids = n - 2; 41 } 42 break; 43 case K_oneshot: 44 svc->flags |= SVC_ONESHOT; 45 break; 46 case K_onrestart: 47 nargs--; 48 args++; 49 kw = lookup_keyword(args[0]); 50 if (!kw_is(kw, COMMAND)) { 51 parse_error(state, "invalid command '%s'\n", args[0]); 52 break; 53 } 54 kw_nargs = kw_nargs(kw); 55 if (nargs < kw_nargs) { 56 parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, 57 kw_nargs > 2 ? "arguments" : "argument"); 58 break; 59 } 60 61 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); 62 cmd->func = kw_func(kw); 63 cmd->nargs = nargs; 64 memcpy(cmd->args, args, sizeof(char*) * nargs); 65 list_add_tail(&svc->onrestart.commands, &cmd->clist); 66 break; 67 case K_critical: 68 svc->flags |= SVC_CRITICAL; 69 break; 70 case K_setenv: { /* name value */ 71 struct svcenvinfo *ei; 72 if (nargs < 2) { 73 parse_error(state, "setenv option requires name and value arguments\n"); 74 break; 75 } 76 ei = calloc(1, sizeof(*ei)); 77 if (!ei) { 78 parse_error(state, "out of memory\n"); 79 break; 80 } 81 ei->name = args[1]; 82 ei->value = args[2]; 83 ei->next = svc->envvars; 84 svc->envvars = ei; 85 break; 86 } 87 case K_socket: {/* name type perm [ uid gid ] */ 88 struct socketinfo *si; 89 if (nargs < 4) { 90 parse_error(state, "socket option requires name, type, perm arguments\n"); 91 break; 92 } 93 if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) { 94 parse_error(state, "socket type must be 'dgram' or 'stream'\n"); 95 break; 96 } 97 si = calloc(1, sizeof(*si)); 98 if (!si) { 99 parse_error(state, "out of memory\n"); 100 break; 101 } 102 si->name = args[1]; 103 si->type = args[2]; 104 si->perm = strtoul(args[3], 0, 8); 105 if (nargs > 4) 106 si->uid = decode_uid(args[4]); 107 if (nargs > 5) 108 si->gid = decode_uid(args[5]); 109 si->next = svc->sockets; 110 svc->sockets = si; 111 break; 112 } 113 case K_user: 114 if (nargs != 2) { 115 parse_error(state, "user option requires a user id\n"); 116 } else { 117 svc->uid = decode_uid(args[1]); 118 } 119 break; 120 default: 121 parse_error(state, "invalid option '%s'\n", args[0]); 122 } 123 }
这里以class这个keyword为例,会将当前class所属的svc进行类名的变革,变为main类别,类似的socket和onrestart类似。
到此为止整个service都解析完成 ,开始下一个section的内容。
ps:当zygote解析完后,得到 (1) service_list链表将解析后的service全部链接到一起。 (2) socketinfo也是一个双向链表,zygote只有一个socket,会被链接到socketinfo中。 (3) onrestart通过commands 指向一个commands 链表,zygote有三个commands。
但是zygote这个服务进程的启动还没有开始,将在下面分析。
知识点5:init启动service
1.启动
1.1 分析queue_builtin_action和action_for_each_trigger
queue_builtin_action(console_init_action, "console_init"); //第二个开机画面显示函数。该函数实现将console_init 这个action添加到action_queue全局链表中。
action_for_each_trigger("boot", action_add_queue_tail); //把boot这个action添加到action_queue链表中
void action_for_each_trigger(const char *trigger, void (*func)(struct action *act)) { struct listnode *node; struct action *act; list_for_each(node, &action_list) { act = node_to_item(node, struct action, alist); if (!strcmp(act->name, trigger)) { func(act); } } }
在该函数中,首先遍历action_list链表,找到action,看是否有名字叫boot的trigger存在,我们知道刚才在解析init.rc中的on boot时,将boot这个作为action的name加入到了action_list中去,所以可以找到这个boot的action。成功匹配后调用action_add_queue_tail,将这个action再次加入到action_queue中,等待着执行。
1.2 for(;;)循环中执行execute_one_command
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);//执行class_start等 INFO("command '%s' r=%d\n", cur_command->args[0], ret); }
使用action_remove_queue_head获取action_queue链表中的action后,移除该节点,使用get_first_command获得在action中的命令,比如这里出现的boot和console_init这两个action。针对console_init启动console_init_action这个函数。如果是boot则会对boot这个action所具有的commands链表进行cmd的获取,class_start的func指针函数为do_class_start:
int do_class_start(int nargs, char **args) { //init.rc只有一个参数,就是default /* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */ //这个方法将从service_list中找到classname为"default"的service,然后调用service_start_if_not_disabled方法。 service_for_each_class(args[1], service_start_if_not_disabled); return 0; }
可以看到提取了命令行的第二个参数入main,core等。在service_for_each_class中遍历service_list查找属于该类的service,如我们前面提到的zygote,查找到后执行service_start_if_not_disabled ----> service_start,至此我们进入了启动service的代码。
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; /* starting a service removes it from the disabled * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING)); svc->time_started = 0; /* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless * they are ONESHOT */ if (svc->flags & SVC_RUNNING) { return; //如果这个service已经在运行,则不用处理 } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } /* service运行在另一个进程中,这个进程也是init的子进程,所以启动service前需要判断对应的可执行文件是否存在,zygote对应的可执行文件是/system/bin/app_process */ if (stat(svc->args[0], &s) != 0) { //通过文件名获取文件信息保存到s的buf中 ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name); svc->flags |= SVC_DISABLED; return; } if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) { ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]); svc->flags |= SVC_DISABLED; return; } NOTICE("starting '%s'\n", svc->name); pid = fork(); //调用fork创建子进程 if (pid == 0) { //pid=0,代现在运行在子进程中 struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; if (properties_inited()) { //得到属性存储空间的信息并加到环境变量中。 get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); } for (ei = svc->envvars; ei; ei = ei->next) //添加环境变量信息 add_environment(ei->name, ei->value); //根据socketinfo创建socket for (si = svc->sockets; si; si = si->next) { int socket_type = ( !strcmp(si->type, "stream") ? SOCK_STREAM : (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET)); int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid); if (s >= 0) { //在环境变量中添加socket信息 publish_socket(si->name, s); } } if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { ERROR("Failed to set pid %d ioprio = %d,%d: %s\n", getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno)); } } if (needs_console) { setsid(); open_console(); } else { zap_stdio(); } #if 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif setpgid(0, getpid()); /* as requested, set our gid, supplemental gids, and uid */ if (svc->gid) { setgid(svc->gid); } if (svc->nr_supp_gids) { setgroups(svc->nr_supp_gids, svc->supp_gids); } if (svc->uid) { setuid(svc->uid); } if (!dynamic_args) { //执行/system/bin/app_process,这样就进入到app_process的main方法中了。 if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; char *bword; /* Copy the static arguments */ memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *))); while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); svc->pid = 0; return; } //父进程init的处理,设置service的信息,如启动时间、进程号,以及状态等。 svc->time_started = gettime(); svc->pid = pid; svc->flags |= SVC_RUNNING; if (properties_inited()) //每一个service都有一个属性,zygote的属性为init.svc.zygote,现在设置它的值为running。 notify_service_state(svc->name, "running"); }
分析这段代码,主要内容:
a.检查当前service如zygote的flag 即SVC_RUNNING(服务运行中),SVC_DISABLE等。
b.fork一个子进程,子进程中会建立一个socket用于通信,同时使用if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0)执行zygote对应的可执行文件,至此service zygote真正地启动起来。
c.启动service后,会将其属性设置为running。
2.补充知识点---重启
1.init中由handle_signal管理进程的重启。一旦出现service崩溃,poll函数会接收到相关文件变化的信息,执行handle_signal中的wait_for_one_process。
static int wait_for_one_process(int block) { pid_t pid; int status; struct service *svc; struct socketinfo *si; time_t now; struct listnode *node; struct command *cmd; while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); if (pid <= 0) return -1; INFO("waitpid returned pid %d, status = %08x\n", pid, status); svc = service_find_by_pid(pid); if (!svc) { ERROR("untracked pid %d exited\n", pid); return 0; } ..... svc->flags |= SVC_RESTARTING; /* 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); } notify_service_state(svc->name, "restarting"); return 0; }
该函数使用waitpid,找到子进程退出的进程号pid,然后查找到该service,对service中的onrestart这个commands进行操作,如restart media等。同时将service的flag设置为SVC_RESTARTING,这样就能够通过restart_processes重新启动该服务进程(见下面第2点)。
2. service进程的重启在restart_processes中进行,它会重启flag为SVC_RESTARTING的服务。
参考: