解读Android LOG机制的实现(5)

Android提供的LOG机制的实现贯穿了Java,JNI,本地c/c++实现以及LINUX内核驱动等Android的各个层次,并且简单明晰,是一个相当不错的解读案例。本系列文章针对LOG机制的内部实现机理进行解读,本文是本系列的第五篇,解读应用程序LogCat如何通过对设备文件的open()/select()/read()来获取LOG信息。

从前文知道,LOG被写入到了驱动的节点,那如何获取这些LOG信息并呈现出来的呢?ANDROID里是有个叫LogCat的应用程序被用来获取LOG信息。LogCat不仅从设备节点处获取LOG,并且还提供了很多选项供用户来过滤、控制输出格式等。本文只讲解如何获取LOG部分,相关的LogCat的使用方式,可参考Android的Logcat命令详解。

LogCat是在文件system/core/logcat/logcat.cpp中实现的。

从Logger设备驱动的实现知道,Log的读取是阻塞的操作,亦即,有数据可用,读出数据;否则,读操作会被BLOCK,相应的读进程也会被挂起等待。下面看应用程序LogCat中如何实现读的,这可能需要不断回头与写操作和驱动实现结合来看。

看具体实现之前,先看一个logcat中定义的重要的结构体log_device_t。其中的重要的成员在后面用到的时候再具体解释。

 

一、打开设备节点

Android的Logcat命令详解的命令参数-b <buffer>知道,logcat是可以通过参数来指定对哪个buffer(main/radio/event)进行操作的。Logcat的b参数解析的地方,是通过传递进来的参数(main/radio/event)来创建了一个上面的结构变量,而这些结构通过log_device_t.next链接起来。

if (devices) {       dev = devices;       while (dev->next) {           dev = dev->next;       }       dev->next = new log_device_t(buf, binary, optarg[0]);   else {       devices = new log_device_t(buf, binary, optarg[0]);   }  

而创建实例的时候的参数被保留了下来,用于后续操作。

<buf>是由LOG_FILE_DIR和optarg(-b参数)组合在一起的(为:“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”),保留在device: char*;

<binary>保留在binary: bool;

<optarg[0]>是-b参数的第一个字符,保存在label: char中。

好了,下面就有了打开设备节点时的参数:

dev->fd = open(dev->device, mode);

dev->device根据-b的参数可能为“/dev/log/main”,“/dev/log/event”或“/dev/log/radio”;

mode缺省时为O_RDONLY,读取。只要在运行logcat时,用了-c参数清除log时才以O_WRONLY打开。

而打开文件的文件操作符保存在log_device_t的fd域中,用于后续的操作。

获取Log的操作都是在readLogLines(log_device_t* devices)中实现的。

因为logcat可能会同时操作多个Buffer,而read()会阻塞读取进程,对其他Buffer的读取就不能进行,所以这里用select()来判断可读取的Buffer。

二、select选取可读取的Buffer

Logcat把log_device_t中的所有的buffer的文件操作符dev->fd,都放在readset中[line#7],做为select()的里的<readfds: fd_set*>读参数,来获取可读取的Buffer。这样当任何一个Buffer上有LOG数据时,select()都会返回。当然等待过程中也忽略掉其他signal的影响。相应的代码如下:

fd_set readset;       do {        timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.         FD_ZERO(&readset);        for (dev=devices; dev; dev = dev->next) {            FD_SET(dev->fd, &readset);        }        result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);    } while (result == -1 && errno == EINTR);  

三、读LOG操作

select()返回之后,通过循环判定dev->fd是否在readset里被设置(FD_ISSET)[line#3],知道哪个log buffer里已经有数据了。

if (result >= 0) {       for (dev=devices; dev; dev = dev->next) {           if (FD_ISSET(dev->fd, &readset)) {               queued_entry_t* entry = new queued_entry_t();               /* NOTE: driver guarantees we read exactly one full entry */               ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);      //…  

通过read()读取[line#6]已经有数据的LOG Buffer的文件操作符dev->fd就可得到新到来的log了。

应用程序logcat中已经获取了LOG信息,接下来对数据的处理就都可以在这里进行了,可以过滤,写文件,格式化输入等操作。详细的logcat的命令参数可参见Android的Logcat命令详解。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwzgpy.html