Tim 的聯(lián)系方式
Tim 是最著名且多產(chǎn)的撰稿人之一,。瀏覽 developerWorks 上
Tim 的所有文章。訪問
Tim 的帳戶,。然后在 My developerWorks 與 Tim,、其他作者及讀者聯(lián)系交流。
使用日志進行調(diào)試的方法由來已久,。日志不僅在理解系統(tǒng)的內(nèi)部操作方面很有用,,而且對于通過時間標(biāo)記的日志中按時間順序排列的消息所記錄的系統(tǒng)活動的計時和關(guān)系也非常有用。
本文首先通過介紹用于配置和收集日志信息的應(yīng)用程序接口(API)來說明了內(nèi)核的日志(見圖
1 關(guān)于總結(jié)框架和組件的示意圖),。然后,,本文介紹了日志數(shù)據(jù)從內(nèi)核到用戶空間的移動過程。最后,,本文還介紹了基于內(nèi)核的日志數(shù)據(jù)的目標(biāo):用戶空間中使用
rsyslog
進行日志管理,。
圖 1. 內(nèi)核日志生態(tài)系統(tǒng)和主要組件
內(nèi)核 API
內(nèi)核的日志是通過 printk
函數(shù)實現(xiàn)的,,它與用戶空間對應(yīng)函數(shù)
printf
(按格式打印)具有相似的作用,。printf
命令在編程語言中已存在很長時間,,最近出現(xiàn)是在 C
語言中,但是最早出現(xiàn)可以追溯到 50 年代和 60 年代的 Fortran(PRINT
和
FORMAT
語句),、BCPL(writf
函數(shù),;BCPL 是
C
的前身)和 ALGOL 68 語言(printf
和
putf
)。
在內(nèi)核中,,printk
(打印內(nèi)核)可以使用與
printf
函數(shù)幾乎一樣的格式將將格式化消息寫入到緩沖區(qū),。您可以在
./linux/include/linux/kernel.h(及其實現(xiàn) ./linux/kernel/printk.c)中看到
printk
的格式:
int printk( const char * fmt, ... );
這個格式表示的是一個用于定義文本和格式的字符串(類似于
printf
),它同時帶有一組可變個數(shù)參數(shù)(由省略號表示
[...
]),。
內(nèi)核配置與錯誤
通過 printk
實現(xiàn)的日志是通過內(nèi)核配置選項
CONFIG_PRINTK
激活的,。雖然
CONFIG_PRINTK
一般都是激活的,但是不包含這個選項的系統(tǒng)對內(nèi)核的調(diào)用會返回一個
ENOSYS
錯誤返回值,。
在使用 printk
時,,您首先會發(fā)現(xiàn)的不同點更多是關(guān)于協(xié)議,而不是功能的,。這個特性使用了
C
語言的一種模糊方面來簡化消息級別和優(yōu)先級的規(guī)范,。內(nèi)核允許每一個消息根據(jù)日志級別(定義不同消息重要必的八種級別之一)來分類。這些級別可以用來判斷系統(tǒng)是否不可用(緊急消息),、是否發(fā)現(xiàn)嚴(yán)重狀況(嚴(yán)重消息)或者是否為簡單報告消息,。
這個內(nèi)核代碼直接將日志級別定義消息的第一個參數(shù),下面這個例子說明的就是嚴(yán)重消息的定義:
printk( KERN_CRIT "Error code %08x.\n", val );
注意,,第一個參數(shù)并不一個真正的參數(shù),,因為其中沒有用于分隔級別(KERN_CRIT
)和格式字符的逗號(,
)。KERN_CRIT
本身只是一個普通的字符串(事實上,,它表示的是字符串
"<2>"
,;表 1
列出了完整的日志級別清單)。作為預(yù)處理程序的一部分,,C
會自動地使用一個名為 字符串串聯(lián)
的功能將這兩個字符串組合在一起,。組合的結(jié)果是將日志級別和用戶指定的格式字符串包含在一個字符串中。注意,,如果調(diào)用者未將日志級別提供給
printk
,,那么系統(tǒng)就會使用默認(rèn)值
KERN_WARNING
(表示只有
KERN_WARNING
級別以上的日志消息會被記錄。)
表 1. 日志級別,、標(biāo)識符和使用方法
標(biāo)識符 | 字符串 | 使用方法 |
---|
KERN_EMERG | <0> | 緊急消息(導(dǎo)致系統(tǒng)崩潰) |
KERN_ALERT | <1> | 必須立即處理的錯誤 |
KERN_CRIT | <2> | 嚴(yán)重錯誤(硬件或軟件) |
KERN_ERR | <3> | 錯誤狀況(一般出現(xiàn)在驅(qū)動程序上) |
KERN_WARNING | <4> | 警告狀況(可能導(dǎo)致錯誤) |
KERN_NOTICE | <5> | 不是錯誤,但是一個重要狀況 |
KERN_INFO | <6> | 報告消息 |
KERN_DEBUG | <7> | 僅用于調(diào)試的消息 |
KERN_DEFAULT | <d> | 默認(rèn)內(nèi)核日志級別 |
KERN_CONT | <c> | 日志行繼續(xù)(避免增加新的時間截) |
printk
可以在內(nèi)核的任意上下文中調(diào)用。這個調(diào)用從
./linux/kernel/printk.c 中的
printk
函數(shù)開始,,它會在使用
va_start
解析可變長度參數(shù)之后調(diào)用
vprintk
(在同一個源文件),。
日志輔助函數(shù)
內(nèi)核也提供了一些日志輔助函數(shù),它們可以簡化日志函數(shù)的使用,。每一個日志級別都有一個對應(yīng)的函數(shù),,它會擴展為
printk
函數(shù)的一個宏。例如,,如果要使用
printk
處理
KERN_EMERG
日志級別時,,您可以直接使用
pr_emerg
。所有宏都已列在
./linux/include/linux/kernel.h 文件中,。
vprintk
函數(shù)執(zhí)行了許多管理級檢查(遞歸檢查),,然后獲取日志緩沖區(qū)的鎖(__log_buf
)。接下來,,它會對輸入的字符串進行日志級別檢查,;
如果發(fā)現(xiàn)日志級別信息,那么對應(yīng)的日志級別就會被設(shè)置,。最后,,vprintk
會獲取當(dāng)前時間(使用函數(shù)
cpu_clock
)并使用
sprintf
(不是標(biāo)準(zhǔn)庫版本,而是在
./linux/lib/vsprintf.c 中實現(xiàn)的內(nèi)部內(nèi)核版本)將它轉(zhuǎn)換成一個字符串,。這個字符串會被傳遞給
printk
,,然后它會被一個管理緩沖邊界(emit_log_char
)的特殊函數(shù)復(fù)制到內(nèi)核日志緩沖區(qū)中。這個函數(shù)最后將獲取和釋放執(zhí)行控制臺信號,,并將下一條日志消息發(fā)送到控制臺(在
release_console_sem
中執(zhí)行),。內(nèi)核緩沖緩沖區(qū)的大小初始值為 4KB,但是最新的內(nèi)核大小已經(jīng)升級到
16KB(在不同的體系架構(gòu)上,,這個值最高可以達(dá)到 1MB),。
至此,您已經(jīng)了解用于將日志消息插入到內(nèi)核環(huán)緩沖區(qū)的
API?,F(xiàn)在,,讓我們討論一下用于將數(shù)據(jù)從內(nèi)核移動到用戶空間的方法。
回頁首
內(nèi)核日志與接口
多用途的 syslog
系統(tǒng)調(diào)用提供了內(nèi)核的日志緩沖區(qū)訪問方法,。這個調(diào)用執(zhí)行了很多個操作,所有操作都可以在用戶空間執(zhí)行,,但是只有一個操作可以被非 root 用戶執(zhí)行,。syslog
系統(tǒng)調(diào)用的原型定義位于 ./linux/include/linux/syslog.h;而它的實現(xiàn)位于
./linux/kernel/printk.c,。
syslog(2) vs. syslog(3)
注意,,這里定義的
syslog
(syslog(2)
)與發(fā)送消息到系統(tǒng)日志(syslog(3)
)的 API
是不同的。后者允許將消息發(fā)送到
syslog
(通過以特定優(yōu)先級調(diào)用的日志處理函數(shù) open
、close
和
write
實現(xiàn)),。
syslog
調(diào)用是作為內(nèi)核日志消息環(huán)緩沖區(qū)的輸入/輸出(I/O)和控制接口,。通過
syslog
調(diào)用,應(yīng)用程序可以讀取日志消息(部分,、整體或者只讀取新消息),,
以及控制環(huán)緩沖區(qū)的行為(清除內(nèi)容、設(shè)置日志的消息級別,、啟用或禁用控制臺等等),。圖 2
用圖形說明了使用所討論的主要組件進行日志記錄的過程。
Figure 2. 標(biāo)識主要組件的內(nèi)核日志
syslog
調(diào)用(在內(nèi)核中調(diào)用 ./linux/kernel/printk.c 的
do_syslog
)是一個相對較小的函數(shù),,它能夠讀取和控制內(nèi)核環(huán)緩沖區(qū),。注意在 glibc 2.0
中,由于詞匯 syslog 使用過于廣泛,,這個函數(shù)的名稱被修改成
klogctl
,,它指的是各種調(diào)用和應(yīng)用程序。syslog
和
klogctl
(在用戶空間中)的原型函數(shù)定義為:
int syslog( int type, char *bufp, int len );
int klogctl( int type, char *bufp, int len );
type
參數(shù)是用于傳遞所執(zhí)行的命令,,它指定了可選的緩沖區(qū)長度,。有一些命令(如清除環(huán)緩沖)是忽略
bufp
和 len
這兩個參數(shù)的。雖然前面兩個命令類型不會對內(nèi)核進行任何操作,,但是其余命令則是用于讀取日志消息或控制日志,。其中有三個命令是用于讀取日志消息的。SYSLOG_ACTION_READ
用于阻塞操作,,直至日志消息到達(dá)后才釋放該操作,,然后將它們返回到所提供的緩沖區(qū)。這個命令會處理這些消息(舊的消息將不會出現(xiàn)在這個命令的后續(xù)調(diào)用中),。SYSLOG_ACTION_READ_ALL
命令會從日志讀取最后 n 個字符(而 n 是在傳遞給 klogctl
的參數(shù) 'len' 中定義的),。SYSLOG_ACTION_READ_CLEAR
命令會先執(zhí)行 SYSLOG_ACTION_READ_ALL
操作,然后執(zhí)行 SYSLOG_ACTION_CLEAR
命令(清除環(huán)緩沖區(qū)),。SYSLOG_ACTION_CONSOLE
ON 和 OFF
可以將日志級別設(shè)置為激活或禁用日志消息輸出到控制臺,,而 SYSLOG_CONSOLE_LEVEL
則允許調(diào)用者定義控制臺所接受的日志消息級別。最后,,SYSLOG_ACTION_SIZE_BUFFER
是用于返回內(nèi)核環(huán)緩沖區(qū)大小,,而 SYSLOG_ACTION_SIZE_UNREAD
則返回當(dāng)前內(nèi)核環(huán)緩沖區(qū)可讀取的字符數(shù)。表 2 顯示了 SYSLOG
命令的完整清單,。
表 2. 使用 syslog/klogctl 系統(tǒng)調(diào)用實現(xiàn)的命令
命令/操作代碼 | 作用 |
---|
SYSLOG_ACTION_CLOSE (0) | 關(guān)閉日志(未實現(xiàn)) |
SYSLOG_ACTION_OPEN (1) | 打開日志(未實現(xiàn)) |
SYSLOG_ACTION_READ (2) | 從日志讀取 |
SYSLOG_ACTION_READ_ALL (3) | 從日志讀取所有消息(非破壞地) |
SYSLOG_ACTION_READ_CLEAR (4) | 從日志讀取并清除所有消息 |
SYSLOG_ACTION_CLEAR (5) | 清除環(huán)緩沖區(qū) |
SYSLOG_ACTION_CONSOLE_OFF (6) | Disable printk s to the console |
SYSLOG_ACTION_CONSOLE_ON (7) | 激活控制臺 printk |
SYSLOG_ACTION_CONSOLE_LEVEL (8) | 將消息級別設(shè)置為控制接受 |
SYSLOG_ACTION_SIZE_UNREAD (9) | 返回日志中未讀取的字符數(shù) |
SYSLOG_ACTION_SIZE_BUFFER (10) | 返回內(nèi)核環(huán)緩沖區(qū)大小 |
在實現(xiàn)上面的 syslog
/klogctl
層之后,,kmsg proc 文件系統(tǒng)成為一個 I/O 通道(在 ./linux/fs/proc/kmsg.c
中實現(xiàn)的),它提供了從內(nèi)核緩沖區(qū)讀取日志消息的二進制接口,。這個讀取操作通常是由一個守護程序(klogd
或
rsyslogd
)實現(xiàn)的,,它會處理這些消息,,然后將它們傳遞給
rsyslog
,以便(基于它的配置)轉(zhuǎn)發(fā)到正確的日志文件中,。
文件 /proc/kmsg 實現(xiàn)了少數(shù)等同于內(nèi)部 do_syslog
的文件操作,。在內(nèi)部,open
調(diào)用與
SYSLOG_ACTION_OPEN
有關(guān),,而 SYSLOG_ACTION_CLOSE
則與 release
有關(guān)(每一個調(diào)用都實現(xiàn)為一個 No Operation Performed
[NOP]),。這個輪循操作會等待文件活動的完成,然后才調(diào)用 SYSLOG_ACTION_SIZE_UNREAD
確定可以讀取的字符數(shù),。最后,,read
操作會被映射到
SYSLOG_ACTION_READ
,以處理可用的日志消息,。注意,,用戶是不會用到 /proc/kmsg
文件的:守護程序用它來獲取日志消息,并將它們轉(zhuǎn)發(fā)到 /var 空間內(nèi)必要的日志文件中,。
回頁首
用戶空間應(yīng)用程序
用戶空間提供了許多讀取和管理內(nèi)核日志的訪問方法,。我們開始先介紹較底層的接口(如 /proc
文件系統(tǒng)配置元素),然后再介紹更高層的應(yīng)用程序,。
/proc 文件系統(tǒng)不僅提供了一個訪問日志消息(kmsg
)的二進制接口,。它還有許多與上面討論的
syslog
/klogctl
相關(guān)或無關(guān)的配置元素。清單 1 顯示了這些參數(shù),。
清單 1. /proc 中的 printk 配置參數(shù)
mtj@ubuntu:~$ cat /proc/sys/kernel/printk
4 4 1 7
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_delay
0
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit
5
mtj@ubuntu:~$ cat /proc/sys/kernel/printk_ratelimit_burst
10
在清單 1 中,,第一項定義了 printk
API
當(dāng)前使用的日志級別。這些日志級別表示了控制臺的日志級別,、默認(rèn)消息日志級別,、最小控制臺日志級別和默認(rèn)控制臺日志級別。printk_delay
值表示的是 printk
消息之間的延遲毫秒數(shù)(用于提高某些場景的可讀性),。注意,,這里它的值為 0,而它是不可以通過 /proc
設(shè)置的,。printk_ratelimit
定義了消息之間允許的最小時間間隔(當(dāng)前定義為每 5 秒內(nèi)的某個內(nèi)核消息數(shù)),。消息數(shù)量是由
printk_ratelimit_burst
定義的(當(dāng)前定義為 10)。如果您擁有一個非正式內(nèi)核而又使用有帶寬限制的控制臺設(shè)備(如通過串口),,
那么這非常有用,。注意,在內(nèi)核中,,速度限制是由調(diào)用者控制的,,而不是在
printk
中實現(xiàn)的。如果一個
printk
用戶要求進行速度限制,,那么該用戶就需要調(diào)用
printk_ratelimit
函數(shù)。
dmesg
命令也可用于打印和控制內(nèi)核環(huán)緩沖區(qū)。這個命令使用 klogctl
系統(tǒng)調(diào)用來讀取內(nèi)核環(huán)緩沖區(qū),,并將它轉(zhuǎn)發(fā)到標(biāo)準(zhǔn)輸出(stdout),。這個命令也可以用來清除內(nèi)核環(huán)緩沖區(qū)(使用
-c
選項),設(shè)置控制臺日志級別(-n
選項),,以及定義用于讀取內(nèi)核日志消息的緩沖區(qū)大?。?code>-s
選項)。注意,,如果沒有指定緩沖區(qū)大小,,那么 dmesg
會使用 klogctl
的 SYSLOG_ACTION_SIZE_BUFFER
操作確定緩沖區(qū)大小。
最后,,所有日志應(yīng)用程序都是基于一個標(biāo)準(zhǔn)化日志框架
syslog
,,主流操作系統(tǒng)(包括 Linux?
和 Berkeley Software Distribution [BSD])都實現(xiàn)了這個框架。syslog
使用自身的協(xié)議實現(xiàn)在不同傳輸協(xié)議的事件通知消息傳輸(將組件分成發(fā)起者,、中繼者和收集者),。在許多情況中,所有這三種組件都在一個主機上實現(xiàn),。除了
syslog
的許多有意思的特性,,它還規(guī)定了日志信息是如何收集、過濾和存儲的,。syslog
已經(jīng)經(jīng)過了許多的變化和發(fā)展,。您可能聽過 syslog
、klog
或
sysklogd
,。最新版本的 Ubuntu
使用的是名為 rsyslog
(基于原先的 syslog
)的新版本
syslog
,,它指的是可靠的和擴展的
syslogd
。
rsyslogd
守護程序通過它的配置文件 /etc/rsyslog.conf
來理解 /proc 文件系統(tǒng)的 kmsg
接口,,并使用這些接口獲取內(nèi)核日志消息,。注意,在內(nèi)部,,所有日志級別都是通過 /proc/kmsg
寫入的,,這樣所傳輸?shù)娜罩炯墑e就不是由內(nèi)核決定的,而是由 rsyslog
本身決定的,。然后這些內(nèi)核日志消息會存儲在 /var/log/kern.log(及其他配置的文件),。在
/var/log 中有許多的日志文件,包括一般消息和系統(tǒng)相調(diào)用(/var/log/messages),、系統(tǒng)啟動日志(/var/log/boot.log),、認(rèn)證日志(/var/log/auth.log)等等。
雖然您可以檢查這些日志,,但是您也可以使用它們進行自動審計和檢查,。有許多日志文件分析器可以用于故障修復(fù),,
或者滿足安全規(guī)范要求,以及自動地使用諸如模式識別或相關(guān)性分析(甚至是跨系統(tǒng)的)來發(fā)現(xiàn)問題,。
回頁首
結(jié)束語
本文簡單地介紹了內(nèi)核日志和一些應(yīng)用程序—包括在內(nèi)核中創(chuàng)建內(nèi)核日志消息,,在內(nèi)核的環(huán)緩沖區(qū)存儲消息,使用
syslog
/klogctl
或 /proc/kmsg 實現(xiàn)到用戶空間的消息傳輸,,通過 rsyslog
日志框架實現(xiàn)轉(zhuǎn)發(fā),,以及它在 /var/log 子樹的最終測試位置。Linux
提供了(包括內(nèi)核及外部空間的)豐富且靈活的日志框架,。