本文分析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的定义

[cpp] view plain copy
  1. #defineT_EOF0
  2. #defineT_TEXT1
  3. #defineT_NEWLINE2

2.2 关键字定义

[cpp] view plain copy
  1. KEYWORD(capability,OPTION,0,0)
  2. KEYWORD(chdir,COMMAND,1,do_chdir)
  3. KEYWORD(chroot,COMMAND,1,do_chroot)
  4. KEYWORD(class,OPTION,0,0)
  5. KEYWORD(class_start,COMMAND,1,do_class_start)
  6. KEYWORD(class_stop,COMMAND,1,do_class_stop)
  7. KEYWORD(console,OPTION,0,0)
  8. KEYWORD(critical,OPTION,0,0)
  9. KEYWORD(disabled,OPTION,0,0)
  10. KEYWORD(domainname,COMMAND,1,do_domainname)
  11. KEYWORD(exec,COMMAND,1,do_exec)
  12. KEYWORD(export,COMMAND,2,do_export)
  13. KEYWORD(group,OPTION,0,0)
  14. KEYWORD(hostname,COMMAND,1,do_hostname)
  15. KEYWORD(ifup,COMMAND,1,do_ifup)
  16. KEYWORD(insmod,COMMAND,1,do_insmod)
  17. KEYWORD(import,COMMAND,1,do_import)
  18. KEYWORD(keycodes,OPTION,0,0)
  19. KEYWORD(mkdir,COMMAND,1,do_mkdir)
  20. KEYWORD(mount,COMMAND,3,do_mount)
  21. KEYWORD(on,SECTION,0,0)
  22. KEYWORD(oneshot,OPTION,0,0)
  23. KEYWORD(onrestart,OPTION,0,0)
  24. KEYWORD(restart,COMMAND,1,do_restart)
  25. KEYWORD(service,SECTION,0,0)
  26. KEYWORD(setenv,OPTION,2,0)
  27. KEYWORD(setkey,COMMAND,0,do_setkey)
  28. KEYWORD(setprop,COMMAND,2,do_setprop)
  29. KEYWORD(setrlimit,COMMAND,3,do_setrlimit)
  30. KEYWORD(socket,OPTION,0,0)
  31. KEYWORD(start,COMMAND,1,do_start)
  32. KEYWORD(stop,COMMAND,1,do_stop)
  33. KEYWORD(trigger,COMMAND,1,do_trigger)
  34. KEYWORD(symlink,COMMAND,1,do_symlink)
  35. KEYWORD(sysclktz,COMMAND,1,do_sysclktz)
  36. KEYWORD(user,OPTION,0,0)
  37. KEYWORD(wait,COMMAND,1,do_wait)
  38. KEYWORD(write,COMMAND,2,do_write)
  39. KEYWORD(copy,COMMAND,2,do_copy)
  40. KEYWORD(chown,COMMAND,2,do_chown)
  41. KEYWORD(chmod,COMMAND,2,do_chmod)
  42. KEYWORD(loglevel,COMMAND,1,do_loglevel)
  43. KEYWORD(ioprio,OPTION,0,0)

2.3 struct action (动作的结构体)和struct command(命令的结构体)

[cpp] view plain copy
  1. structaction{
  2. /*nodeinlistofallactions*/
  3. structlistnodealist; //所有Action组成的动作列表
  4. /*nodeinthequeueofpendingactions*/
  5. structlistnodeqlist;
  6. /*nodeinlistofactionsforatrigger*/
  7. structlistnodetlist;
  8. unsignedhash;
  9. constchar*name; //动作名字
  10. structlistnodecommands; //命令列表
  11. structcommand*current; //当前命令指针
  12. };

[cpp] view plain copy
  1. structcommand
  2. {
  3. /*listofcommandsinanaction*/
  4. structlistnodeclist; //一个动作的命令列表
  5. int(*func)(intnargs,char**args); //函数()
  6. intnargs;
  7. char*args[1];
  8. };


三、对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。




更多相关文章

  1. Android 数据存储02之文件读写
  2. Android XML文件解析
  3. Android 如何在XML文件中定义动画
  4. Android 资源文件中的符号含义与说明: @ ?
  5. android 读取properties文件
  6. adb 全部命令

随机推荐

  1. SQL SERVER 数据库备份的三种策略及语句
  2. sql server递归子节点、父节点sql查询表
  3. 微信小程序滚动选择器(时间日期)详解及实
  4. SqlServer中模糊查询对于特殊字符的处理
  5. 分组字符合并SQL语句 按某字段合并字符串
  6. 分组后分组合计以及总计SQL语句(稍微整理
  7. sql分组后二次汇总(处理表重复记录查询和
  8. SQL去除重复记录(七种)
  9. java连接mysql数据库 java连接sql server
  10. Sql Server 2012完全卸载方法 只需8步轻