一種更為合理的Linux核心列印機制

2022-05-18 03:55:50 字數 2793 閱讀 3594

2020-04-26

關鍵字:printk_ratelimit、printk_ratelimited

在linux核心開發中,幾乎所有的日誌、資訊的列印都是通過 printk() 函式實現的。

printk 首先會將所有來自程式的資訊都放到乙個緩衝區中,然後各個「監控程式」再根據自己的需要將這些資訊讀取出來。如console會將日誌資訊直接顯示在螢幕上等。linux核心中的日誌緩衝區本質上就是乙個環形fifo。

既然有緩衝區,那就意味著它裡面的資料有被覆蓋的風險。而事實上也確實如此,當乙個核心或其程式太過龐大而又不對列印資訊加以控制的話,很容易就出現老舊的日誌資訊尚未被讀取走就被新生日誌給覆蓋掉了。

為了避免這種情況的發生,我們一般都會為核心程式中的列印按重要性劃分等級並新增列印控制開關。這確實是乙個很有效的控制手段。

但有時確實是會有出現了某些很嚴重的問題而不得不持續列印相關提示資訊的情況。為了避免核心在短時間內產生大量重複資訊而將其它有用資訊覆蓋掉的情況,我們可以在編寫相應**時人為加個延時列印的判斷機制上去。這雖然很有效,但也難以掩蓋它會給我們的程式設計帶來額外的工作量的事實。

所幸,linux核心就有這麼一套機制,我們僅需呼叫乙個介面,並根據介面的返回值來決定是否持續列印我們的資訊。這個介面的內部會自動幫我們判斷我們即將要列印的資訊是否過於頻繁。以下直接貼出使用**:

if

(printk_ratelimit())

是的,就是這麼簡單。這個 printk_ratelimit() 函式是定義在 上的。

這個函式的機理是會判斷我們的列印的頻率的,即它會限制這條列印在指定時間間隔內最多只能出現指定次數條。

這個限制條件是可以動態修改的,它們被定義在 /proc 目錄下的兩個檔案中:

/proc/sys/kernel/printk_ratelimit

/proc/sys/kernel/printk_ratelimit_burst

printk_ratelimit 表示時間間隔,printk_ratelimit_burst 表示頻次間隔。預設情況下 printk_ratelimit 的值是 5 ,printk_ratelimit_burst 的值是 10。即指定的列印在每5秒的時間裡最多只能列印10次。我們可以隨時更改這兩個檔案的值來控制列印頻率。

int i = 0

;

intret;

for(; i < 100; i++)

msleep(

100);

}

當 /proc/sys/kernel/printk_ratelimit 的值為 1 且 /proc/sys/kernel/printk_ratelimit_burst 的值為 2 時,以上**的列印結果將是在 i 的值為 0,1、11,12、22,23、33,34、44,45、55,56、66,67、77,78、88,89、99 的時候會列印一條 ok 字樣的日誌。有興趣的同學可以自行嘗試一下。

btw,其實在 printk.h 中是有給出建議讓我們不要用 printk_ratelimit() 來作為限制列印的,給出的原因是因為這個函式並不會判斷列印的內容,如果有多條列印都呼叫了這個函式的話,那麼它們是會共享系統中的頻次條件的。以下是這個建議的原版:

如果不太理解的話,我們就實際寫乙個**來體驗一下就是了,還是上面的**,只不過再額外加多一次判斷:

int i = 0

;

intret;

for(; i < 100; i++)

ret =printk_ratelimit();

printk(

"ret2:%d\n

", ret);

if(ret)

msleep(

100);

}

上面這段**原本我們期望的結果是當 i 的值為 0,1 時各列印一次 ok 及 ko 字樣的日誌。但真實情況卻是 i 只在 0 的時候列印了一次 ok 及 ko 字樣,而在 i 為 1 時並沒有任何列印!這也充分印證了官方說明文件中的「共享頻次」的意思。如果要讓系統按內容來限制頻次,則可以使用 printk_ratelimited() 巨集定義來實現。這個巨集定義同樣位於 printk.h 標頭檔案中,它的原型如下圖所示:

首先它是乙個巨集定義,而 printk_ratelimit() 卻是乙個函式。

其次,這個巨集定義就是用來替換 printk() 進行列印的。

最後,不用想也知道 printk_ratelimited() 巨集定義因為要根據內容來區分頻次,它肯定比 printk_ratelimit() 函式需要消耗更多的系統資源。

話不多說,直接改寫我們上面的**:

#include int i = 0

;

intret;

for(i = 0; i < 100; i++)

不再需要判斷,直接用 printk_ratelimited 替換掉 printk 即可。

另外,使用這個巨集定義需要我們額外引入 ratelimit.h 標頭檔案。

最後,printk_ratelimited 巨集定義關於時間與頻率的限制是定義在 ratelimit.h 標頭檔案中的,以巨集的形式來定義,如下圖所示:

預設情況下也是每 5 秒指定內容的列印資訊最多只能出現 10 次。

一種更為高效的資料庫無限級分類表設計

一般地,大家在資料庫設計無限級分類表時只是多新增了乙個parent id欄位,這樣如果要讀取所有分類的話只能進行遞迴讀取資料庫表操作,這樣的效能可想而知。先看個表及測試資料 idname parent id 1clothing 衣服 02 men s 13 women s 14 suits 套裝 2...

一種定位記憶體洩露的方法(Linux)

本文是 一種定位記憶體洩露的方法 solaris 對應的 linux 版本,偵錯程式使用 gdb。主要介紹例項部分。其他請見 一種定位記憶體洩露的方法 solaris 模擬 new失敗的程式 include class abc int i int j void f throw std bad all...

讓學習linux變成一種習慣

無論學習什麼技術,都是從小白開始,沒有大佬是天生會linux的,你羨慕的那些大佬,都是通過不斷的刻苦學習從而蛻變成大佬的。由於需要解決生存問題,加上我也對linux比較有興趣,所以就打算從事故事linux運維的工作。首先,學好linux運維的門檻其實很低,其實很多人覺得這種工作適合高學歷,其實並沒有...