浅析Android设备中grep命令处理流程

2017-04-18

 

概述

    在TV开发板中,可以在串口中直接使用grep命令。这是因为在/system/bin/下有一个'grep'链接。这个链接指向'/system/bin/toolbox'程序。
浅析Android设备中grep命令处理流程
【图1】
当在串口中键入:
grep um
命令时也就等效于键入
toolbox grep um
 
  toolbox程序是Android系统中携带的工具​之一。其源码位于:
./system/core/toolbox/

浅析Android设备中grep命令处理流程

【图2】

  由上图2可以看到该目录下包含有大多数常用命令的源代码。toolbox程序的意义就是将这些零散的程序的入口进行打包。根据用户在串口中键入的toolbox命令后面的参数不同来选择执行对应的程序。例如,当键入上述的 ‘toolbox grep um’命令时,系统会先将命令的参数通过toolbox的进行解析,最终调用grep.c中的grep_main()函数进行二次解析。

 

toolbox处理流程

./system/core/toolbox/toolbox.c
  该源码文件结构简单。内部仅两个函数:1.int main(int argc, char **argv);2.static int toolbox_main(int argc, char **argv);以及一个封装了各工具集入口函数的结构体数组tools[]。在这个源码中,最重要的就是这个静态结构体数组,其定义如下:
 #define TOOL(name) int name##_main(int, char**);
#include "tools.h"
#undef TOOL static struct
{
const char *name;
int (*func)(int, char**);
} tools[] = {
{ "toolbox", toolbox_main },
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL
{ , },
};
  上述代码中,第1~3行的定义方式在我看来很新奇。但此处就简单把它理解成从tools.h中读取定义的信息,事实上也确实是这样。然后将这些字符串替换成相应的函数声明。tools.h是在本目录下的Android.mk文件中生成的。位于out目录下。
 浅析Android设备中grep命令处理流程
其内容类型如下
浅析Android设备中grep命令处理流程
上述代码中,第1~3行是声明各工具的入口函数。第11~13行是使用这些外部函数。经过这样的声明以后,toolbox程序在运行时这个tools数组的实际值为
 tools[] = {
{ "toolbox", toolbox_main },
{"ls", ls_main},
.
.
.
{"grep", grep_main},
{ , },
};
而tools结构体的第二个参数又是一个函数指针,因此,tools数组中包含的就是toolbox目录下的所有工具的入口函数。估计这也是它会被取名为toolbox的原因吧。
 
  在运行toolbox程序的时候,首先会调用其主函数main()。在这个主函数main()中主要就是取跟在toolbox后面的参数来决定到底是要执行哪一个工具程序。如当键入:toolbox ls。则toolbox.c的执行顺序为:1.main();2.toolbox_main();3.main();4.ls_main()
 

grep

    grep程序的入口函数为:grep_main()
 ./system/core/toolbox/grep/grep.c
在这个文件的首部,定义了多个标志位用于区分不同的参数响应。
浅析Android设备中grep命令处理流程
常用的功能及其执行参数都已标示了出来。
    在grep_main()函数中,首先判断了键入参数的首字符。 __progname[0]等效于argv[0][0]。一般我们使用grep命令时,都是键入grep -n "xx",这时,__progname[0]得到的值就是字符g。看注释貌似是说区分于二进制模式下的什么什么的。对于我们由串口键入命令来执行程序的情况,是统一解析成字符g的,故而不需要理会它的真实用意。
浅析Android设备中grep命令处理流程
 
    然后会从环境变量中读取搜索环境。
浅析Android设备中grep命令处理流程
读取到环境变量后做了什么事,我们姑且不管。不过这个的结果一般都是(null),因此实际上会执行如下操作:
浅析Android设备中grep命令处理流程
    接下来就是去解析命令后面跟的参数了。
浅析Android设备中grep命令处理流程
图【3】
函数getopt_long()的含义可查询百度。
不得不承认,这个函数的功能很强大。它能解析各种花式输入的参数。同时它还具有排序的功能,将花式输入的命令行重新排序成'grep'在前,参数在其中,需要筛选的字符串在末尾的顺序。具体功能验证如下系列图所示,getopt_long()的排序功能验证如下图7所示。
浅析Android设备中grep命令处理流程

浅析Android设备中grep命令处理流程

浅析Android设备中grep命令处理流程

浅析Android设备中grep命令处理流程

浅析Android设备中grep命令处理流程

    总之,经过图3所示的while循环后,能够识别所有的参数。例如,识别-n参数的代码如下图所示。
浅析Android设备中grep命令处理流程
    接下来就是筛减aargv中的参数。
浅析Android设备中grep命令处理流程
其中,optind是getopt_long()函数中的一个变量,它的值是键入的'grep'字符串以及所有的参数的数量。
 
    接下来是去读取需要过滤的字符串。
浅析Android设备中grep命令处理流程
当键入的参数中包含-e  -f 时,needpattern的值就是0,否则为1。
由此处代码可知,grep命令仅能搜索一个参数。add_pattern()函数的实现如下图所示。
浅析Android设备中grep命令处理流程
【图4】
    此函数的代码也算简单。grep命令是在pattern[]数组中保存要过滤的字符串的(pattern是一个二维数组)。而这个数组又被设计成“自动扩容”的形式。上图4所示代码中第230行的条件块就是一个自动扩容的处理流程了。grep_realloc()函数的定义位于
 ./system/core/toolbox/grep/util.c
然后将这个要过滤的字符串添加到pattern[]数组的末尾。至此,整个命令的解析就已经完成了。接下来就要开始执行过滤操作了。
    接下来就是
浅析Android设备中grep命令处理流程
这个grepbehave在grep_main()的首部就已被赋值。对于我们由串口键入命令的情况来说,其值恒为GREP_BASIC。这里,不必理会。
 
    接下来是
浅析Android设备中grep命令处理流程
这一块的作用我也不知道。程序一般会走694行。看695行的函数名fastcomp(),猜它应该是一个快速对比的功能。这个函数的定义位于
 ./system/core/toolbox/grep/fastgrep.c
有兴趣的话可以去详细阅读一下源码。这个函数返回0表示成功,返回-1表示失败。经测试,都是返回-1。此处略过这块代码。
 
    接下来是搜索前的最后的设置。
浅析Android设备中grep命令处理流程
707行的lbflag是行缓冲。浅析Android设备中grep命令处理流程目前并不知道它的具体作用。经测试,由串口键入命令的方式,该值一般为0。此处暂时不予理会。
710行的Hflag,浅析Android设备中grep命令处理流程。该值默认为0。
由713行,当键入的过滤字符串只有一个时,就进行过滤操作,然后退出grep程序。
否则,就执行下面的代码。多个过滤字符串中后面的将会被认为是文件或目录的路径。这样就会被系统理解为在这个目录或文件中执行搜索命令。
浅析Android设备中grep命令处理流程
dirbehave在键入的参数中有'-r'时其值会为DIR_RECURSE。其它情况,则执行else分支。
这里,暂时不讨论指定文件或目录的情况。
 
procfile()函数位于
 ./system/core/toolbox/grep/util.c
浅析Android设备中grep命令处理流程
    这个函数内部定义了几个结构体变量。其中file与str结构体的定义位于
 ./system/core/toolbox/grep/grep.h
浅析Android设备中grep命令处理流程

浅析Android设备中grep命令处理流程

    file结构体结构简单,内部仅保存文件的句柄及这个文件是否是二进制文件的标志。str结构体也简单。其中*dat保存的是当前剩余要过滤的数据。对于在串口中使用grep而言,就是当前获得的文件流中的所有内容。
浅析Android设备中grep命令处理流程
对于只有一个过滤字符串的情况,会执行上图所示代码中的197行代码块。且对于在串口中键入命令的情况,198行的fn得到的值是(standard input)。即标准输入流,而非某个文件流。接着去grep_open(NULL)函数,这个函数位于
 ./system/core/toolbox/grep/file.c
这个函数的结构也简单,
浅析Android设备中grep命令处理流程
这个函数的重点在于第241行处。它会将文件结构体的文件句柄指向标准输入流处。
    接着再回到util.c处继续往下执行,现在就到了从流中过滤数据的时候了。核心代码如下图所示,
浅析Android设备中grep命令处理流程
上面这个for循环语句就是用来过滤关键字并将结果打印在控制台用的。在第226行for循环的条件判断处可以看到两个状态位lflag与qflag。前面的状态位是标志是否只打印文件名,后面的状态位则是标志是否静默搜索(将结果输出到文件或其它什么的)。
 
    上图所示代码中,第228行的if语句的作用就是逐行读取源数据ln.dat。然后跳到第242行。这里调用了procline函数。上面的procfile是处理整个文件流,读取出一行数据后就传到procline中作进一步处理。随后procline在匹配到符合条件的行后,传到printline函数中打印出来。这三个函数均在util.c文件中。具体的实现流程就不再赘述了。
 
grep的详细流程在我看来是很复杂的,没有必要去弄懂这个流程中每一行代码的意义,只需要了解个大概流程就好了,毕竟是抱着扩充知识面而非研究代码的目的去学习的。
上一篇:svn利用TortoiseSVN忽略文件或文件夹


下一篇:django的 信号