android logd 原理及實現


一、logd介紹

logd 是Android L版本提出來的概念,其作用是保存Android運行期間的log(日志)。在Android L之前,log由kernel的ring buffer 保存,在Android L之后,log保存在用戶空間。

1)  logd進程啟動

系統啟動到init函數時會解析init.rc文件,啟動logd進程和logd-reinit(重新初始化logd) 進程,init.rc文件中的相關內容如下:

onload_persist_props_action

     load_persist_props

     start logd             

     start logd-reinit

在system/core/logd中的logd.rc文件中,啟動logdservice和logd-reinit service,具體內容如下:

service logd /system/bin/logd                  

    socket logd stream 0666logd logd

    socket logdr seqpacket0666 logd logd

    socket logdw dgram 0222logd logd

    group root system readproc

    writepid/dev/cpuset/system-background/tasks

 

service logd-reinit /system/bin/logd --reinit

    oneshot

    disabled

    writepid/dev/cpuset/system-background/tasks

注:logd-reinit只會執行一次,logd和logd-reinit都會調用system/core/logd/main.cpp中的main函數,但logd-reinit通過netlink給logd進程發送reinit命令后就會退出。

2)  logd 實現log管理

主要原理是:上層應用(Android層)通過調用Log/Slog/Rlog中的v/d方法打印log,最終log會通過netlink傳遞給logd,logd會將log保存在內存中(由C++中的list容器管理,后續會介紹),logcat等log獲取工具同樣通過netlink方式從logd獲取log。

 

二、logd實現流程

本文主要介紹load側的實現,對於Androidlog如何傳遞給logd,只做簡要介紹。

1.      Android層往logd寫log

Android層調用Log/Slog/Rlog中的v/d方法打印log,最終會調用到

system/core/liblog/logger_write.c 中的__android_log_buf_write函數,調用流程如下:

__android_log_buf_write

->write_to_log

           ->__write_to_log_init

                    ->__write_to_log_initialize

                             ->logdOpen

                    ->__write_to_log_daemon

                             ->logdWrite

文件最終寫到 “/dev/socket/logdw”中,此時logd中的LogListener會監測到有log需寫入,待log保存后,會通知LogReader將新保存的log傳遞給logcat等。

 

2.      Logd log保存機制

在system/core/lodgd/main.cpp文件的main函數中,創建了LogBuffer,LogReader,LogListener和CommandListener四個對象,

LogBuffer用於管理log;

LogReader用於將log傳遞給logcat;

LogListener用於監聽是否有log寫入logd;

CommandListener用於監聽是否有命令發送給logd。

 

接下來介紹log的保存流程:

1)  創建LogBuffer對象,在classLogBuffer類中,定義了一個list容器,保存了指向LogBufferElement對象的指針,創建LogBuffer對象時在其構造函數中會調用LogBuffer::init()函數初始化各log域(如main/system/kernel/crash等)的大小。

2)  創建LogListener對象並開始監聽

    LogListener *swl = newLogListener(logBuf, reader);

   // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value

    if(swl->startListener(600)) {
        exit(1);

          }

3)  在startListener函數中創建線程,線程注冊函數為SocketListener::threadStart;

4)  執行runListener函數,如果socket監聽到數據,則執行onDataAvailable函數進行處理;

5)  調用logbuf->log(LogBuffer::log),這個函數很重要,新建一個LogBufferElement對象(用於保存log),調用mLogElements.insert將LogBufferElement加入list容器,實現log的保存。

3.       

三、Logd-reinit 進程

Logd-reinit 進程在啟動時,會給logd進程發送reinit命令,logd在收到命令后,會重新初始化LogBuffer。

 

在system/core/lodgd/main.cpp文件的main函數中會創建一個線程用於監測是否有reinit請求

if (pthread_create(&thread, &attr,reinit_thread_start, NULL))

 

在reinit_thread_start函數中,會重新初始化各個log區的大小,以及其他參數的初始化,但不會重新生成LogBuffer對象。相關代碼如下:

//Anything that reads persist.<property>

if(logBuf) {
    logBuf->init();                     

    logBuf->initPrune(NULL);

}

 

四、Logd log 獲取

可通過logcat工具獲取logd log,logcat 相關代碼所在路徑:system/core/logcat

執行adb logcat 即可查看log

注:通過logcat獲取的log,並不完全是按照log分類來打印的,如在KERNEL log中可能存在MAIN log。


logcat實現的大部分函數都在logcat/logcat.cpp文件中,其中__logcat函數是最重要的函數,其負責logcat 輸入參數的解析以及log的處理。

logcat 最終讀取log通過liblog/logd_reader.c 中的logdRead函數實現。此函數負責打開/dev/logdr,並通過socket獲取log。


最近在使用logcat 獲取log時,存在log丟失的情況,不知大家是否有遇到過。

調查發現,當同時啟動6個線程分別獲取main/kernel/system/crash/events等log時,由於啟動的線程太多(即多個socket 與server 連接),導致有log丟失。


原文鏈接:https://blog.csdn.net/yinjian1013/article/details/78261527

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM