当前位置:操作系统 > 安卓/Android >>

解读Android LOG机制的实现:(5)获取LOG的应用程序LogCat

 

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命令详解。

补充:移动开发 , Android ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,