解讀Android LOG機制的實現:(5)獲取LOG的應用程序LogCat – Android移動開發技術文章_手機開發 Android移動開發教學課程

 

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

You May Also Like