一羡宙、前言
logd 守護(hù)進(jìn)程是日志系統(tǒng)的管家凌盯,內(nèi)部維持三個(gè)日志 Socket : logd付枫、logdr、logdw 來與客戶端進(jìn)行通信驰怎。同時(shí)負(fù)責(zé)維護(hù)幾個(gè)環(huán)形緩沖區(qū)阐滩,用于存放系統(tǒng)中的各種日志,緩沖區(qū)包含 main砸西、system叶眉、events址儒、radio芹枷、crash、kernel 莲趣;但是在 Android 5.0 之前鸳慈,logd 進(jìn)程并不存在,日志是保留在 /dev/log/main喧伞、/dev/log/system走芋、/dev/log/radio、/dev/log/event 等節(jié)點(diǎn)中潘鲫,但是這樣面臨的一個(gè)問題就是當(dāng) Android 系統(tǒng)大版本升級(jí)時(shí)翁逞,linux kernel 需要升級(jí)對(duì)應(yīng)的日志驅(qū)動(dòng),因此在后續(xù)的版本中就有了 logd 進(jìn)程溉仑。
二挖函、logd 進(jìn)程詳解
2.1 logd 進(jìn)程框架
在 Android 日志系統(tǒng)分析(一):概述 一文中,總結(jié)了整個(gè)日志讀寫的主要流程浊竟,因此對(duì)于 logd 進(jìn)程是如何同外界溝通進(jìn)而讀寫日志的過程不再贅述怨喘,而著重于 logd 本身的一些知識(shí)點(diǎn)津畸,這里先看一下 logd 的系統(tǒng)框圖:
知識(shí)點(diǎn):
① logd 是日志系統(tǒng)的核心進(jìn)程,由 init 啟動(dòng)必怜,是屬于守護(hù)進(jìn)程常駐后臺(tái)
② logd 維護(hù)各個(gè)日志節(jié)點(diǎn)緩存隊(duì)列肉拓,提供 socket 接口進(jìn)行讀、寫梳庆、控制功能
③ logd 進(jìn)程啟動(dòng)后暖途,分別啟動(dòng) LogReader、LogListener膏执、CommandListener 三個(gè)線程丧肴,監(jiān)聽并處理來自三個(gè) socket 的消息。在收到消息后胧后,會(huì)通過 LogBuffer 類保存日志到對(duì)應(yīng)的 RAM buffer 中
④ LogAudit 模塊用于接收 Kernel selinux 信息芋浮,即可以在用戶空間打印 selinux 日志信息
⑤ LogKlog 用于接收 kernel 日志信息,通過設(shè)置 property 壳快,可以通過 logcat 命令讀取內(nèi)核日志
⑥ LogStatistics 是日志統(tǒng)計(jì)模塊纸巷,默認(rèn)開啟統(tǒng)計(jì)數(shù)據(jù)較少,僅能以 pid/uid 緯度統(tǒng)計(jì)打印日志的數(shù)量眶痰。如果設(shè)置了 logd.statistic = true 瘤旨。會(huì)打印更多緯度的統(tǒng)計(jì)信息,包括哪些 pid/uid/tid/TAG 日志量比較大竖伯,可用于日志裁剪相關(guān)
2.2 logd 啟動(dòng)流程
在 main 函數(shù)中存哲,會(huì)打開 /dev/kmsg 來讀取內(nèi)核日志,通過 LogKlog 來進(jìn)行存儲(chǔ)七婴;若是配置了 ro.logd.kernel 屬性祟偷,則打開 /proc/kmsg 讀取內(nèi)核日志;
2.3 logd.rc 解析
logd 作為 Native Service 打厘,系統(tǒng)啟動(dòng)時(shí)會(huì)讀取 init.rc 腳本去啟動(dòng)修肠,它的相關(guān)屬性被定義在 logd.rc 文件中:
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
oneshot
disabled
user logd
group logd
writepid /dev/cpuset/system-background/tasks
......
這里主要分為兩部分:啟動(dòng) logd 服務(wù) 和 啟動(dòng) logd-reinit 服務(wù) (在Android 10 上添加了 logd-auditctl 服務(wù),目的是為了限制 selinux denia打印日志為5秒一次)户盯;先來看一下 啟動(dòng) logd 服務(wù) 的同時(shí)做了些什么:
① 創(chuàng)建 logd嵌施、logdr、logdw 這三個(gè) socket 為后面的通信做準(zhǔn)備
② logdw 定義為 dgram 類型的 socket 莽鸭,類似與 UDP類型的 Socket 吗伤,這么做的原因是考慮到性能問題,在多個(gè)進(jìn)程同時(shí)寫日志的情況下硫眨,write 函數(shù)寫入到 socket 的 buffer 中即可返回足淆,這樣不會(huì) block 業(yè)務(wù)邏輯太長時(shí)間。如果是 TCP 類型的 Socket ,客戶端需要等到 TCP 收到 ACK 響應(yīng)才能返回缸浦,這樣就會(huì)過多的消耗性能和資源夕冲;
啟動(dòng) logd-reinit 服務(wù):
這個(gè)服務(wù)的主要作用是重新初始化 logd 的 LogBuffer,在配置中 oneshot 表示開機(jī)只啟動(dòng)一次裂逐。在上面的 main.cpp 中的 main 函數(shù)內(nèi)歹鱼,logd 在啟動(dòng)后,會(huì)創(chuàng)建一個(gè)線程 reinit_thread_start () 卜高,當(dāng) logd-reinit 傳入?yún)?shù) reinit 后弥姻,進(jìn)行功能執(zhí)行:
① 如果 reinit 啟動(dòng)后,并且 /deg/kmsg 打開成功掺涛,把 logd.daemon: renit 寫入 kmsg
② 重新初始化各個(gè) log buffer 的大小庭敦,以及其他參數(shù)的初始化,但不會(huì)重新生成 LogBuffer 對(duì)象
main.cpp##main
int main(int argc, char* argv[]) {
......
// 啟動(dòng) Reinit線程薪缆,當(dāng)logd-reinit傳入?yún)?shù)reinit時(shí)秧廉,進(jìn)行調(diào)用,reinit開機(jī)只啟動(dòng)一次
sem_init(&reinit, 0, 0);
sem_init(&uidName, 0, 0);
sem_init(&sem_name, 0, 1);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
memset(¶m, 0, sizeof(param));
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
pthread_t thread;
reinit_running = true;
if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
reinit_running = false;
}
}
pthread_attr_destroy(&attr);
}
......
exit(0);
}
main.cpp#reinit_thread_start()
static void* reinit_thread_start(void* /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
if (fdDmesg >= 0) {
static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
'l','o','g','d','.','d','a','e','m','o','n',':',' ','r','e','i','n','i','t','\n' };
write(fdDmesg, reinit_message, sizeof(reinit_message));
}
//重新初始化各個(gè)log buffer的大小拣帽,以及其他參數(shù)的初始化疼电,但不會(huì)重新生成LogBuffer對(duì)象
if (logBuf) {
logBuf->init();
logBuf->initPrune(nullptr);
}
android::ReReadEventLogTags();
}
return nullptr;
}
參考
[ 1 ] 深入理解安卓日志系統(tǒng)(logcat / liblog / logd)
[ 2 ] Android10.0 日志系統(tǒng)分析(二)-logd、logcat架構(gòu)分析及日志系統(tǒng)初始化