標籤:
1、基本原理(1)在UBOOT裡設定console=ttySAC0或者console=tty1這裡是設定控制終端,tySAC0 表示串口, tty1 表示lcd
(2)核心用printk列印核心就會根據命令列參數來找到對應的硬體操作函數,並將資訊通過對應的硬體終端列印出來!
2、printk的使用
(1)printk函數的資訊如何才能在終端顯示出來在核心代碼include/linux/kernel.h中,定義了控制台的層級:extern int console_printk[];#define console_loglevel (console_printk[0])#define default_message_loglevel (console_printk[1])#define minimum_console_loglevel (console_printk[2])#define default_console_loglevel (console_printk[3]) 我們在到kernel/printk.c裡找到console_printk的定義: /* printk‘s without a loglevel use this.. */#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */ /* We show everything that is MORE important than this.. */#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */ DECLARE_WAIT_QUEUE_HEAD(log_wait); int console_printk[4] = { DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */ DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */ MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */ DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */}; 於是我們知道控制台的層級是:7 4 1 7我們當然可以再這裡修改,但是還有一個更簡單的修改方法,即在使用者空間使用下面的命令: echo “1 4 1 7” > /proc/sys/kernel/printk 將1 4 1 7寫入 /proc/sys/kernel/printk即可! 當我們使用printk函數時往往要加上資訊層級,比如: printk(KERN_WARNING"there is a warning here!\n") 其中KERN_WARNING就表示資訊的層級,相關宏在函數include/linux/kernel.h中: #define KERN_EMERG "<0>" /* system is unusable */#define KERN_ALERT "<1>" /* action must be taken immediately */#define KERN_CRIT "<2>" /* critical conditions */#define KERN_ERR "<3>" /* error conditions */#define KERN_WARNING "<4>" /* warning conditions */#define KERN_NOTICE "<5>" /* normal but significant condition */#define KERN_INFO "<6>" /* informational */#define KERN_DEBUG "<7>" /* debug-level messages */ 如果沒有指明資訊層級的話,就會採用預設的資訊層級,這個預設的資訊層級我們在上面見到過的,就是:#define default_message_loglevel (console_printk[1])沒有改動的情況下是4 上面我們說到了資訊層級和控制台層級,下面我們要說到重點了!當資訊層級的數值小於控制台的層級時,printk要列印的資訊才會在終端列印出來,否則不會顯示在終端!
(2)串口控制台printk vprintk(fmt, args); vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); vsnprintf(buf,size,fmt,args);//先把輸出資訊輸入到臨時buffer //把臨時buffer裡面的資料稍作處理,寫入log_buffer //可以將資訊層級與資訊合并 //在使用者空間使用命令dmesg可以把log_buffer裡面的資料列印出來 release_console_sem(); call_console_drivers(_con_start, _log_end); _call_console_drivers(start_print, cur_index, msg_level); __call_console_drivers(start, end); con->write(con, &LOG_BUF(start), end - start);//調用具體的輸出函數 這個輸出函數要把資料從串口輸出的話,肯定要調用到串口硬體相關的函數我們到檔案:drivers/serial/s3c2410.c裡面去這裡有個串口初始化函數: s3c24xx_serial_initconsole register_console(&s3c24xx_serial_console); 我們來看看它的註冊函數:static struct console s3c24xx_serial_console ={ .name = S3C24XX_SERIAL_NAME, .device = uart_console_device, .flags = CON_PRINTBUFFER, .index = -1, .write = s3c24xx_serial_console_write, .setup = s3c24xx_serial_console_setup};裡面的確有個write函數! 但是我們還不知道printk選擇的控制台為什麼是串口呢? 我們知道uboot傳入了參數:console=ttySAC0 或者 console=tty1 核心就通過如下的函數來處理傳入的參數:__setup("console=", console_setup);這是個宏 ,它的作用就是用函數console_setup來處理我們傳入的參數console_ setup //先解碼字串為:name, idx, options,然後就使用這些:name, idx, options add_preferred_console(name, idx, options); strcmp(console_cmdline[i].name, name) console_cmdline[i].index == idx //將索引和名字都記錄在了console_cmdline數組中了 我們記住這個數組,回過頭來再來看: register_console(&s3c24xx_serial_console); if (strcmp(console_cmdline[i].name, console->name) != 0) continue;我們看到在註冊串口控制台的時候,會將串口控制台的名字與uboot傳進來的參數相比較,一旦匹配才會註冊。這樣的話,只有與uboot傳進來的控制台參數相一致的控制台才能註冊成功。那麼也就是說,printk會通過uboot設定的控制台的write函數,將資訊列印出來! 另外還有一點需要牢記的就是,printk輸出的資訊會先儲存在緩衝區log_buf中,所以我們當然可以通過查看log_buf來看輸出資訊了!而這個查看命令就是:dmesg實際上,dmesg這個命令的作用就是去讀/proc/kmsg這個檔案。也就是說log_buf裡面的內容是存放在/proc/kmsg這個檔案裡面的!
(3)使用printk
方法一:我們可以再核心中使用如下列印語句:
#define DEG_PRINTK printk
//#define DEG_PRINTK(x...)
DEG_PRINTK("%s %s %d\n",_FILE_,_FUNCTION_,_LINE_);這行列印語句的意思就是講本行代碼所在的檔案的名(包括路徑)、所在的函數、所在的行列印出來!當我們需要調試的時候,就使用
#define DEG_PRINTK printk這個宏,當不需要調試的時候,就使用
#
define DEG_PRINTK(x...)這個宏。其中#define DEG_PRINTK(x...)裡面的"..."的意思是DEG_PRINTK的參數是可變的!當代碼比較少的時候,我們可以在每一行都加上這個列印語句,這樣很容易就會發現錯誤的位置!當代碼比較多的時候,我們可以採用對半尋找的方法!先在代碼中間加上列印語句!然後判斷出錯位置在列印語句之前還是之後,如果出現在之前,就在之前的代碼代碼裡面再次採用對半尋找!
方法二:上面我們說到過,要列印的資訊會存放在log_buf中,通過檔案/proc/kmsg可以來訪問這個buf,然後將資訊列印出來。由此我們就想了,我們是否可以構造這樣一個mylog_buf,裡面存放我們所需要的列印資訊,通過一個/proc/kmsg檔案可以訪問該buf,然後列印出來?答案是肯定的!下面我們就一步步來完成!
驅動程式調試方法之printk——printk的原理與直接使用