//总结:后面代码不太相同; //补充: 另外一个函数queue_builtin_action来向init进程中的一个待执行action队列增加了一个名称等于“console_init”的action。这个action对应的执行函数为console_init_action,它就是用来显示第二个开机画面的。 queue_builtin_action中也会执行action_add_queue_tail;和接下来调用的action_for_each_trigger一样; action_list列表用来保存从启动脚本/init.rc解析得到的一系列action,以及一系列内建的action。当这些action需要执行的时候,它们就会被添加到action_queue列表中去,以便init进程可以执行它们。 回到init进程的入口函数main中,最后init进程会进入到一个无限循环中去。在这个无限循环中,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信号进行处理,即回收那些已经变成了僵尸的子进程。 注意,由于后面三个事件都是可以通过文件描述符来描述的,因此,init进程的入口函数main使用poll机制来同时轮询它们,以便可以提高效率。
—————————————————————— 在 Android中使用启动脚本init.rc,可以在系统的初始化过程中进行一些简单的初始化操作。这个脚本被直接安装到目标系统的根文件系统中,被 init可执行程序解析。 init.rc是在init启动后被执行的启动脚本. (1)android启动文件系统后调用的第一个应用程序是/init,此文件的很重要的内容是解析了init.rc和init.xxx.rc sysclktz<mins_west_of_gmt> 设置系统时区(GMT为0) class_start <serviceclass> 启动指定类别的所有服务 class_stop <serviceclass> 停止指定类别的所有服务 domainname <name> 设置域名 insmod <path> 加载路径为<path>的内核模块 mkdir <path> 创建路径为<path>目录 mount <type> <device> <dir> [ <mountoption> ]* 挂载类型为<type>的设备<device>到目录<dir>,<mountoption>为挂载参数,距离如下: mount ubifs ubi1_0 /data nosuid nodev setkey 暂时未定义 setprop <name> <value> 设置名为<name>的系统属性的值为<value> setrlimit <resource> <cur> <max> 设置资源限制, start <service> 启动服务(如果服务未运行) stop <service> 停止服务(如果服务正在运行) symlink <target> <path> 创建一个从<path>指向<target>的符号链接,举例: symlink /system/etc /etc write <path> <string> [ <string> ]* 打开路径为<path>的文件并将一个多这多个字符串写入到该文件中。 (g)系统属性(Property) android初始化过程中会修改一些属性,通过getprop命令我们可以看到属性值,这些属性指示了某些动作或者服务的状态,主要如下: init.action 如果当前某个动作正在执行则init.action属性的值等于该动作的名称,否则为"" init.command 如果当前某个命令正在执行则init.command属性的值等于该命令的名称,否则为"" init.svc.<name> 此属性指示个名为<name>的服务的状态("stopped", "running", 或者 "restarting").
init的源代码在文件:./system/core/init/init.c 中,init会一步步完成下面的任务: 2.解析/init.rc和/init.%hardware%.rc文件 3. 执行 early-init action in the two files parsed in step 2. 4. 设备初始化,例如:在 /dev 下面创建所有设备节点,下载 firmwares. 5.初始化属性服务器,Actually the property system is working as a share memory.Logically it looks like a registry under Windows system. 6. 执行 init action in the two files parsed in step 2. 7. 开启 属性服务。 8. 执行 early-boot and boot actions in the two files parsed in step 2. 9. 执行 Execute property action in the two files parsed in step 2. 10.进入一个无限循环 to wait for device/property set/child process exit events.例如,如果SD卡被插入,init会收到一个设备插入事件,它会为这个设备创建节点。系统中比较重要的进程都是由init来fork的,所以如果他们他谁崩溃了,那么init 将会收到一个 SIGCHLD 信号,把这个信号转化为子进程退出事件, 所以在loop中,init 会操作进程退出事件并且执行*.rc 文件中定义的命令。例如,在init.rc中,因为有: 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 所以,如果zygote因为启动某些服务导致异常退出后,init将会重新去启动它。 int main(int argc, char **argv) { … //需要在后面的程序中看打印信息的话,需要屏蔽open_devnull_stdio()函数 open_devnull_stdio(); … //初始化log系统 log_init(); //解析/init.rc和/init.%hardware%.rc文件 parse_config_file(”/init.rc”); … snprintf(tmp, sizeof(tmp), “/init.%s.rc”, hardware); parse_config_file(tmp); … //执行 early-init action in the two files parsed in step 2. action_for_each_trigger(”early-init”, action_add_queue_tail); drain_action_queue(); … /* execute all the boot actions to get us started */ /* 执行 init action in the two files parsed in step 2 */ action_for_each_trigger(”init”, action_add_queue_tail); drain_action_queue(); … /* 执行 early-boot and boot actions in the two files parsed in step 2 */ action_for_each_trigger(”early-boot”, action_add_queue_tail); action_for_each_trigger(”boot”, action_add_queue_tail); drain_action_queue(); /* run all property triggers based on current state of the properties */ queue_all_property_triggers(); drain_action_queue(); /* enable property triggers */ property_triggers_enabled = 1; … for(;;) { int nr, timeout = -1; … drain_action_queue(); restart_processes(); if (process_needs_restart) { timeout = (process_needs_restart – gettime()) * 1000; if (timeout 重要的数据结构两个列表,一个队列。 static list_declare(service_list); static list_declare(action_list); static list_declare(action_queue); *.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。 *.rc 脚本中所有 on 关键开头的项将会被会添加到 action_list 列表中。 每个action列表项都有一个列表,此列表用来保存该段落下的 Commands脚本解析过程: parse_config_file(”/init.rc”) int parse_config_file(const char *fn) { char *data; data = read_file(fn, 0); if (!data) return -1; parse_config(fn, data); DUMP(); return 0; } static void parse_config(const char *fn, char *s) { … case T_NEWLINE: if (nargs) { int kw = lookup_keyword(args[0]); if (kw_is(kw, SECTION)) { state.parse_line(&state, 0, 0); parse_new_section(&state, kw, nargs, args); } else { state.parse_line(&state, nargs, args); } nargs = 0; } … } parse_config会逐行对脚本进行解析,如果关键字类型为 SECTION ,那么将会执行 parse_new_section() 类型为 SECTION 的关键字有: on 和 sevice 关键字类型定义在 Parser.c (system/core/init) 文件中 Parser.c (system/core/init) #define SECTION 0×01 #define COMMAND 0×02 #define OPTION 0×04 关键字 属性 capability, OPTION, 0, 0) class, OPTION, 0, 0) class_start, COMMAND, 1, do_class_start) class_stop, COMMAND, 1, do_class_stop) console, OPTION, 0, 0) critical, OPTION, 0, 0) disabled, OPTION, 0, 0) domainname, COMMAND, 1, do_domainname) exec, COMMAND, 1, do_exec) export, COMMAND, 2, do_export) group, OPTION, 0, 0) hostname, COMMAND, 1, do_hostname) ifup, COMMAND, 1, do_ifup) insmod, COMMAND, 1, do_insmod) import, COMMAND, 1, do_import) keycodes, OPTION, 0, 0) mkdir, COMMAND, 1, do_mkdir) mount, COMMAND, 3, do_mount) on, SECTION, 0, 0) oneshot, OPTION, 0, 0) onrestart, OPTION, 0, 0) restart, COMMAND, 1, do_restart) service, SECTION, 0, 0) setenv, OPTION, 2, 0) setkey, COMMAND, 0, do_setkey) setprop, COMMAND, 2, do_setprop) setrlimit, COMMAND, 3, do_setrlimit) socket, OPTION, 0, 0) start, COMMAND, 1, do_start) stop, COMMAND, 1, do_stop) trigger, COMMAND, 1, do_trigger) symlink, COMMAND, 1, do_symlink) sysclktz, COMMAND, 1, do_sysclktz) user, OPTION, 0, 0) write, COMMAND, 2, do_write) chown, COMMAND, 2, do_chown) chmod, COMMAND, 2, do_chmod) loglevel, COMMAND, 1, do_loglevel) device, COMMAND, 4, do_device) parse_new_section()中再分别对 service 或者 on 关键字开头的内容进行解析。 … case K_service: state->context = parse_service(state, nargs, args); if (state->context) { state->parse_line = parse_line_service; return; } break; case K_on: state->context = parse_action(state, nargs, args); if (state->context) { state->parse_line = parse_line_action; return; } break; } … 对 on 关键字开头的内容进行解析 static void *parse_action(struct parse_state *state, int nargs, char **args) { … act = calloc(1, sizeof(*act)); act->name = args[1]; list_init(&act->commands); list_add_tail(&action_list, &act->alist); … } 对 service 关键字开头的内容进行解析 static void *parse_service(struct parse_state *state, int nargs, char **args) { struct service *svc; if (nargs 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; } 服务的表现形式: service [ ]* … 申请一个service结构体,然后挂接到service_list链表上,name 为服务的名称 pathname 为执行的命令 argument 为命令的参数。之后的 option 用来控制这个service结构体的属性,parse_line_service 会对 service关键字后的 内容进行解析并填充到 service 结构中 ,当遇到下一个service或者on关键字的时候此service选项解析结束。 例如: 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 当*.rc 文件解析完成以后: action_list 列表项目如下: on init on boot on property:ro.kernel.qemu=1 on property:persist.service.adb.enable=1 on property:persist.service.adb.enable=0 init.marvell.rc 文件 on early-init on init on early-boot on boot service_list 列表中的项有: service console service adbd service servicemanager service mountd service debuggerd service ril-daemon service zygote service media service bootsound service dbus service hcid service hfag service hsag service installd service flash_recovery 状态服务器相关: 在init.c 的main函数中启动状态服务器。 property_set_fd = start_property_service(); 状态读取函数: Property_service.c (system/core/init) const char* property_get(const char *name) Properties.c (system/core/libcutils) int property_get(const char *key, char *value, const char *default_value) 状态设置函数: Property_service.c (system/core/init) int property_set(const char *name, const char *value) Properties.c (system/core/libcutils) int property_set(const char *key, const char *value) 在终端模式下我们可以通过执行命令 setprop setprop 工具源代码所在文件: Setprop.c (system/core/toolbox) Getprop.c (system/core/toolbox): property_get(argv[1], value, default_value); Property_service.c (system/core/init) 中定义的状态读取和设置函数仅供init进程调用, handle_property_set_fd(property_set_fd); property_set() //Property_service.c (system/core/init) property_changed(name, value) //Init.c (system/core/init) queue_property_triggers(name, value) drain_action_queue() 只要属性一改变就会被触发,然后执行相应的命令: 例如: 在init.rc 文件中有 on property:persist.service.adb.enable=1 start adbd on property:persist.service.adb.enable=0 stop adbd 所以如果在终端下输入: setprop property:persist.service.adb.enable 1或者0 那么将会开启或者关闭adbd 程序。 执行action_list 中的命令: 从action_list 中取出 act->name 为 early-init 的列表项,再调用 action_add_queue_tail(act)将其插入到 队列 action_queue 尾部。drain_action_queue() 从action_list队列中取出队列项 ,然后执行act->commands 列表中的所有命令。 所以从 ./system/core/init/init.c mian()函数的程序片段: action_for_each_trigger(”early-init”, action_add_queue_tail); drain_action_queue(); action_for_each_trigger(”init”, action_add_queue_tail); drain_action_queue(); action_for_each_trigger(”early-boot”, action_add_queue_tail); action_for_each_trigger(”boot”, action_add_queue_tail); drain_action_queue(); /* run all property triggers based on current state of the properties */ queue_all_property_triggers(); drain_action_queue(); 可以看出,在解析完init.rc init.marvell.rc 文件后,action 命令执行顺序为: 执行act->name 为 early-init,act->commands列表中的所有命令 执行act->name 为 init, act->commands列表中的所有命令 执行act->name 为 early-boot,act->commands列表中的所有命令 执行act->name 为 boot, act->commands列表中的所有命令 关键的几个命令: class_start default 启动所有service 关键字定义的服务。 class_start 在act->name为boot的 act->commands列表中,所以当 class_start 被触发后,实际上调用的是函数 do_class_start() 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); } } } 因为在调用 parse_service() 添加服务列表的时候,所有服务 svc->classname 默认取值:”default”, 所以 service_list 中的所有服务将会被执行。 |
|