內核日誌:API 及實現

使用日誌進行調試的方法由來已久。日誌不僅在理解系統的內部操作方面很有用,而且對於通過時間標記的日誌中按時間順序排列的消息所記錄的系統活動的計時和關係也非常有用。

本文首先通過介紹用於配置和收集日誌信息的應用程序接口(API)來說明了內核的日誌(見圖 1 關於總結框架和組件的示意圖)。然後,本文介紹了日誌數據從內核到用戶空間的移動過程。最後,本文還介紹了基於內核的日誌數據的目標:用戶空間中使用 rsyslog 進行日誌管理。


圖 1. 內核日誌生態系統和主要組件
內核日誌生態系統和主要組件 

內核 API

內核的日誌是通過 printk 函數實現的,它與用戶空間對應函數 printf(按格式打印)具有相似的作用。printf 命令在編程語言中已存在很長時間,最近出現是在 C 語言中,但是最早出現可以追溯到 50 年代和 60 年代的 Fortran(PRINT 和 FORMAT 語句)、BCPL(writf 函數;BCPL 是 C 的前身)和 ALGOL 68 語言(printf 和 putf)。

在內核中,printk(打印內核)可以使用與 printf 函數幾乎一樣的格式將將格式化消息寫入到緩衝區。您可以在 ./linux/include/linux/kernel.h(及其實現 ./linux/kernel/printk.c)中看到 printk 的格式:

int printk( const char * fmt, ... );

這個格式表示的是一個用於定義文本和格式的字符串(類似於 printf),它同時帶有一組可變個數參數(由省略號表示 [...])。

內核配置與錯誤

通過 printk 實現的日誌是通過內核配置選項CONFIG_PRINTK 激活的。雖然 CONFIG_PRINTK 一般都是激活的,但是不包含這個選項的系統對內核的調用會返回一個 ENOSYS 錯誤返回值。

在使用 printk 時,您首先會發現的不同點更多是關於協議,而不是功能的。這個特性使用了 C 語言的一種模糊方面來簡化消息級別和優先級的規範。內核允許每一個消息根據日誌級別(定義不同消息重要必的八種級別之一)來分類。這些級別可以用來判斷系統是否不可用(緊急消息)、是否發現嚴重狀況(嚴重消息)或者是否爲簡單報告消息。 這個內核代碼直接將日誌級別定義消息的第一個參數,下面這個例子說明的就是嚴重消息的定義:

				printk( KERN_CRIT "Error code %08x.\n", val );

注意,第一個參數並不一個真正的參數,因爲其中沒有用於分隔級別(KERN_CRIT)和格式字符的逗號(,)。KERN_CRIT 本身只是一個普通的字符串(事實上,它表示的是字符串 "<2>";表 1 列出了完整的日誌級別清單)。作爲預處理程序的一部分,C 會自動地使用一個名爲 字符串串聯 的功能將這兩個字符串組合在一起。組合的結果是將日誌級別和用戶指定的格式字符串包含在一個字符串中。注意,如果調用者未將日誌級別提供給 printk,那麼系統就會使用默認值 KERN_WARNING(表示只有 KERN_WARNING 級別以上的日誌消息會被記錄。)


表 1. 日誌級別、標識符和使用方法
標識符 字符串 使用方法
KERN_EMERG <0> 緊急消息(導致系統崩潰)
KERN_ALERT <1> 必須立即處理的錯誤
KERN_CRIT <2> 嚴重錯誤(硬件或軟件)
KERN_ERR <3> 錯誤狀況(一般出現在驅動程序上)
KERN_WARNING <4> 警告狀況(可能導致錯誤)
KERN_NOTICE <5> 不是錯誤,但是一個重要狀況
KERN_INFO <6> 報告消息
KERN_DEBUG <7> 僅用於調試的消息
KERN_DEFAULT <d> 默認內核日誌級別
KERN_CONT <c> 日誌行繼續(避免增加新的時間截)

printk 可以在內核的任意上下文中調用。這個調用從 ./linux/kernel/printk.c 中的 printk 函數開始,它會在使用 va_start 解析可變長度參數之後調用 vprintk(在同一個源文件)。

日誌輔助函數

內核也提供了一些日誌輔助函數,它們可以簡化日誌函數的使用。每一個日誌級別都有一個對應的函數,它會擴展爲 printk 函數的一個宏。例如,如果要使用 printk 處理KERN_EMERG 日誌級別時,您可以直接使用 pr_emerg。所有宏都已列在 ./linux/include/linux/kernel.h 文件中。

vprintk 函數執行了許多管理級檢查(遞歸檢查),然後獲取日誌緩衝區的鎖(__log_buf)。接下來,它會對輸入的字符串進行日誌級別檢查; 如果發現日誌級別信息,那麼對應的日誌級別就會被設置。最後,vprintk 會獲取當前時間(使用函數 cpu_clock)並使用sprintf(不是標準庫版本,而是在 ./linux/lib/vsprintf.c 中實現的內部內核版本)將它轉換成一個字符串。這個字符串會被傳遞給 printk,然後它會被一個管理緩衝邊界(emit_log_char)的特殊函數複製到內核日誌緩衝區中。這個函數最後將獲取和釋放執行控制檯信號,並將下一條日誌消息發送到控制檯(在 release_console_sem 中執行)。內核緩衝緩衝區的大小初始值爲 4KB,但是最新的內核大小已經升級到 16KB(在不同的體系架構上,這個值最高可以達到 1MB)。

至此,您已經瞭解用於將日誌消息插入到內核環緩衝區的 API。現在,讓我們討論一下用於將數據從內核移動到用戶空間的方法。

內核日誌與接口

多用途的 syslog 系統調用提供了內核的日誌緩衝區訪問方法。這個調用執行了很多個操作,所有操作都可以在用戶空間執行,但是隻有一個操作可以被非 root 用戶執行。syslog 系統調用的原型定義位於 ./linux/include/linux/syslog.h;而它的實現位於 ./linux/kernel/printk.c。

syslog(2) vs. syslog(3)

注意,這裏定義的 syslogsyslog(2))與發送消息到系統日誌(syslog(3))的 API 是不同的。後者允許將消息發送到 syslog(通過以特定優先級調用的日誌處理函數openclose 和 write 實現)。

syslog 調用是作爲內核日誌消息環緩衝區的輸入/輸出(I/O)和控制接口。通過 syslog 調用,應用程序可以讀取日誌消息(部分、整體或者只讀取新消息), 以及控制環緩衝區的行爲(清除內容、設置日誌的消息級別、啓用或禁用控制檯等等)。圖 2 用圖形說明了使用所討論的主要組件進行日誌記錄的過程。


Figure 2. 標識主要組件的內核日誌
標識主要組件的內核日誌 

syslog 調用(在內核中調用 ./linux/kernel/printk.c 的 do_syslog)是一個相對較小的函數,它能夠讀取和控制內核環緩衝區。注意在 glibc 2.0 中,由於詞彙 syslog 使用過於廣泛,這個函數的名稱被修改成 klogctl,它指的是各種調用和應用程序。syslog 和klogctl(在用戶空間中)的原型函數定義爲:

int syslog( int type, char *bufp, int len );
int klogctl( int type, char *bufp, int len );

type 參數是用於傳遞所執行的命令,它指定了可選的緩衝區長度。有一些命令(如清除環緩衝)是忽略 bufp 和 len 這兩個參數的。雖然前面兩個命令類型不會對內核進行任何操作,但是其餘命令則是用於讀取日誌消息或控制日誌。其中有三個命令是用於讀取日誌消息的。SYSLOG_ACTION_READ 用於阻塞操作,直至日誌消息到達後才釋放該操作,然後將它們返回到所提供的緩衝區。這個命令會處理這些消息(舊的消息將不會出現在這個命令的後續調用中)。SYSLOG_ACTION_READ_ALL 命令會從日誌讀取最後 n 個字符(而 n 是在傳遞給 klogctl 的參數 'len' 中定義的)。SYSLOG_ACTION_READ_CLEAR 命令會先執行 SYSLOG_ACTION_READ_ALL 操作,然後執行SYSLOG_ACTION_CLEAR 命令(清除環緩衝區)。SYSLOG_ACTION_CONSOLE ON 和 OFF 可以將日誌級別設置爲激活或禁用日誌消息輸出到控制檯,而 SYSLOG_CONSOLE_LEVEL 則允許調用者定義控制檯所接受的日誌消息級別。最後,SYSLOG_ACTION_SIZE_BUFFER 是用於返回內核環緩衝區大小,而 SYSLOG_ACTION_SIZE_UNREAD 則返回當前內核環緩衝區可讀取的字符數。表 2 顯示了 SYSLOG 命令的完整清單。


表 2. 使用 syslog/klogctl 系統調用實現的命令
命令/操作代碼 作用
SYSLOG_ACTION_CLOSE (0) 關閉日誌(未實現)
SYSLOG_ACTION_OPEN (1) 打開日誌(未實現)
SYSLOG_ACTION_READ (2) 從日誌讀取
SYSLOG_ACTION_READ_ALL (3) 從日誌讀取所有消息(非破壞地)
SYSLOG_ACTION_READ_CLEAR (4) 從日誌讀取並清除所有消息
SYSLOG_ACTION_CLEAR (5) 清除環緩衝區
SYSLOG_ACTION_CONSOLE_OFF (6) Disable printks to the console
SYSLOG_ACTION_CONSOLE_ON (7) 激活控制檯 printk
SYSLOG_ACTION_CONSOLE_LEVEL (8) 將消息級別設置爲控制接受
SYSLOG_ACTION_SIZE_UNREAD (9) 返回日誌中未讀取的字符數
SYSLOG_ACTION_SIZE_BUFFER (10) 返回內核環緩衝區大小

在實現上面的 syslog/klogctl 層之後,kmsg proc 文件系統成爲一個 I/O 通道(在 ./linux/fs/proc/kmsg.c 中實現的),它提供了從內核緩衝區讀取日誌消息的二進制接口。這個讀取操作通常是由一個守護程序(klogd 或 rsyslogd)實現的,它會處理這些消息,然後將它們傳遞給 rsyslog,以便(基於它的配置)轉發到正確的日誌文件中。

文件 /proc/kmsg 實現了少數等同於內部 do_syslog 的文件操作。在內部,open 調用與 SYSLOG_ACTION_OPEN 有關,而SYSLOG_ACTION_CLOSE 則與 release 有關(每一個調用都實現爲一個 No Operation Performed [NOP])。這個輪循操作會等待文件活動的完成,然後才調用 SYSLOG_ACTION_SIZE_UNREAD 確定可以讀取的字符數。最後,read 操作會被映射到 SYSLOG_ACTION_READ,以處理可用的日誌消息。注意,用戶是不會用到 /proc/kmsg 文件的:守護程序用它來獲取日誌消息,並將它們轉發到 /var 空間內必要的日誌文件中。

用戶空間應用程序

用戶空間提供了許多讀取和管理內核日誌的訪問方法。我們開始先介紹較底層的接口(如 /proc 文件系統配置元素),然後再介紹更高層的應用程序。

/proc 文件系統不僅提供了一個訪問日誌消息(kmsg)的二進制接口。它還有許多與上面討論的 syslog/klogctl 相關或無關的配置元素。清單 1 顯示了這些參數。


清單 1. /proc 中的 printk 配置參數
				
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 當前使用的日誌級別。這些日誌級別表示了控制檯的日誌級別、默認消息日誌級別、最小控制檯日誌級別和默認控制檯日誌級別。printk_delay 值表示的是 printk 消息之間的延遲毫秒數(用於提高某些場景的可讀性)。注意,這裏它的值爲 0,而它是不可以通過 /proc 設置的。printk_ratelimit 定義了消息之間允許的最小時間間隔(當前定義爲每 5 秒內的某個內核消息數)。消息數量是由 printk_ratelimit_burst 定義的(當前定義爲 10)。如果您擁有一個非正式內核而又使用有帶寬限制的控制檯設備(如通過串口), 那麼這非常有用。注意,在內核中,速度限制是由調用者控制的,而不是在 printk 中實現的。如果一個 printk 用戶要求進行速度限制,那麼該用戶就需要調用 printk_ratelimit 函數。

dmesg 命令也可用於打印和控制內核環緩衝區。這個命令使用 klogctl 系統調用來讀取內核環緩衝區,並將它轉發到標準輸出(stdout)。這個命令也可以用來清除內核環緩衝區(使用 -c 選項),設置控制檯日誌級別(-n 選項),以及定義用於讀取內核日誌消息的緩衝區大小(-s 選項)。注意,如果沒有指定緩衝區大小,那麼 dmesg 會使用 klogctl 的 SYSLOG_ACTION_SIZE_BUFFER 操作確定緩衝區大小。

最後,所有日誌應用程序都是基於一個標準化日誌框架 syslog,主流操作系統(包括 Linux® 和 Berkeley Software Distribution [BSD])都實現了這個框架。syslog 使用自身的協議實現在不同傳輸協議的事件通知消息傳輸(將組件分成發起者、中繼者和收集者)。在許多情況中,所有這三種組件都在一個主機上實現。除了 syslog 的許多有意思的特性,它還規定了日誌信息是如何收集、過濾和存儲的。syslog 已經經過了許多的變化和發展。您可能聽過 syslogklog 或 sysklogd。最新版本的 Ubuntu 使用的是名爲rsyslog(基於原先的 syslog)的新版本 syslog,它指的是可靠的和擴展的 syslogd

rsyslogd 守護程序通過它的配置文件 /etc/rsyslog.conf 來理解 /proc 文件系統的 kmsg 接口,並使用這些接口獲取內核日誌消息。注意,在內部,所有日誌級別都是通過 /proc/kmsg 寫入的,這樣所傳輸的日誌級別就不是由內核決定的,而是由 rsyslog 本身決定的。然後這些內核日誌消息會存儲在 /var/log/kern.log(及其他配置的文件)。在 /var/log 中有許多的日誌文件,包括一般消息和系統相調用(/var/log/messages)、系統啓動日誌(/var/log/boot.log)、認證日誌(/var/log/auth.log)等等。

雖然您可以檢查這些日誌,但是您也可以使用它們進行自動審計和檢查。有許多日誌文件分析器可以用於故障修復, 或者滿足安全規範要求,以及自動地使用諸如模式識別或相關性分析(甚至是跨系統的)來發現問題。

結束語

本文簡單地介紹了內核日誌和一些應用程序—包括在內核中創建內核日誌消息,在內核的環緩衝區存儲消息,使用 syslog/klogctl 或 /proc/kmsg 實現到用戶空間的消息傳輸,通過 rsyslog 日誌框架實現轉發,以及它在 /var/log 子樹的最終測試位置。Linux 提供了(包括內核及外部空間的)豐富且靈活的日誌框架。


參考資料

學習

  • 瀏覽 rsyslog(替代 syslog 和 klog 的新系統日誌框架)的 在線手冊 及其 Wiki 網站。 

  • Ubuntu 提供了一個非常有用的關於 rsyslog 日誌的頁面。這個 白皮書 提供了關於日誌和 rsyslog 的詳細介紹,包括配置和複雜的多主機日誌網絡。 

  • syslog(2) 的 man 頁面 提供了一個關於 syslog(2) 及其各種選項和配置的非常好的介紹。 

  • printk 是基於 C 語言中一個名爲字符串 逐字連接 的特性。Wikipedia 的這個 C 語言頁面 介紹了這個技術。 

  • syslog 協議實際上是通過 Internet Engineering Task Force 的 Request for Comments (RFC) 定義的一個標準化協議。請閱讀 syslog RFC 5424。 

  • 日誌文件分析在機器學習和監控工具領域是一個非常熱門的主題。在 Wikipedia 上了解更多關於 一般日誌分析 的信息,以及瞭解 SourceForge 上一個活躍日誌文件監控工具 Swatch。 

  • 在 developerWorks Linux 專區 尋找爲 Linux 開發人員(包括 Linux 新手入門)準備的更多參考資料,查閱我們 最受歡迎的文章和教程。 

  • 在 developerWorks 上查閱所有 Linux 技巧 和 Linux 教程。 

  • 隨時關注 developerWorks 技術活動網絡廣播。 

  • 加入免費的 developerWorks Live! 簡介,獲取最新的 IBM 產品,工具以及 IT 行業趨勢的資訊。 

  • 觀看 developerWorks 演示中心,包括面向初學者的產品安裝和設置演示,以及爲經驗豐富的開發人員提供的高級功能。 

獲得產品和技術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章