本文分析Android中如何解析init.rc文件
init.rc启动脚本路径:system/core/rootdir/init.rc
这里的配置文件主要指init.rc。读者可以进到Android的shell,会看到根目录有一个init.rc文件。该文件是只读的,即使有了root权限,可以修改该文件也没有。因为我们在根目录看到的文件只是内存文件的镜像。也就是说,android启动后,会将init.rc文件装载到内存。而修改init.rc文件的内容实际上只是修改内存中的init.rc文件的内容。一旦重启android,init.rc文件的内容又会恢复到最初的装载。想彻底修改init.rc文件内容的唯一方式是修改Android的ROM中的内核镜像(boot.img)。其实boot.img名曰内核镜像,不过该文件除了包含完整的Linux内核文件(zImage)外,还包括另外一个镜像文件(ramdisk.img)。ramdisk.img就包含了init.rc文件和init命令。所以只有修改ramdisk.img文件中的init.rc文件,并且重新打包boot.img文件,并刷机,才能彻底修改init.rc文件。如果读者有Android源代码,编译后,就会看到out目录中的相关子目录会生成一个root目录,该目录实际上就是ramdisk.img解压后的内容。会看到有init命令和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()解析。
二、关键数据类型原型及关键数据定义
2.1 Token的定义
- #defineT_EOF 0
- #defineT_TEXT 1
- #defineT_NEWLINE 2
2.2 关键字定义
- 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(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, COMMAND, 1, do_import)
- 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(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(ioprio, OPTION, 0, 0)
2.3 struct action (动作的结构体)和struct command(命令的结构体)
- struct action {
- /* node in list of all actions */
-
struct listnode alist;
//所有Action组成的动作列表 - /* node in the queue of pending actions*/
- struct listnode qlist;
- /* node in list of actions for atrigger */
- struct listnode tlist;
- unsigned hash;
-
const char *name;
//动作名字 -
struct listnode commands;
//命令列表 -
struct command *current;
//当前命令指针 - };
- struct command
- {
- /* list of commands in an action */
-
struct listnode clist;
//一个动作的命令列表 -
int (*func)(int nargs, char **args);
//函数() - int nargs;
- char *args[1];
- };
三、对action的解析
结合init的启动过程以及前面讲述的init.rc的解析,总结一下对init对init.rc里action的解析.
3.1action的解析
调用parse_action()解析Action,并申请了struct action *act(动作的结构体对象),设置:
1) act->name为动作的名字(比如boot/fs/);
2) 初始化list ,act->commands(一个动作的命令列表)
3) 通过act->alist(所有Action组成的动作列表)把该动作加入到action_list的列尾(也就是加入到act->alist列表)
这样,action创建并加入到了action_list中。
3.2 action里的command的解析
action里的command,调用parse_line_action()来解析
1) 查找关键字,核对是否是COMMAND,参数数目是否正确
2) 申请struct command *cmd
- cmd->func从keyword表中获取;
- 设置参数个数给cmd->nargs,拷贝参数给cmd->args;
- 通过cmd->clist把该命令加入到act->commands的列尾
这样,command加入到了action中。
3.3 action_list里的action加入action_queue中
action_for_each_trigger()把队列action_list里所匹配的action,追加到action_queue的队尾;
queue_builtin_action()把执行的函数组成command,创建action,挂在action_list上,并追加到action_queue的队尾。
3.4 命令的执行
Init的无限循环中execute_one_command():执行action_queue队列中的Action(system/core/init/init.c)
1) 从action_queue取下struct action *act 赋给cur_action;
2) 从cur_action获得struct command *赋给cur_command;
3) 执行cur_command->func(cur_command->nargs, cur_command->args)
上面步骤中1, 2 & 3是一次执行的,4是无限循环执行,从action_queue上取下action,action里获得command,然后执行command。