printk是Linux核心中最常用的偵錯函數之一,它用來向內核緩衝區或控制台輸出偵錯訊息,如變數的值,函數的執行流程,錯誤的原因等。 printk的優點是簡單易用,不需要額外的設備或驅動。 printk的實作涉及到核心緩衝區,日誌等級,格式化字串等概念。在本文中,我們將介紹Linux核心調試技術之printk的原理和方法,並舉例說明它們的使用方法和注意事項。
在內核偵錯技術之中,最簡單的就是printk的使用了,它的用法和C語言應用程式中的printf使用類似,在應用程式中依賴的是stdio.h中的函式庫,而在linux核心中沒有這個函式庫,所以在linux核心中,使用這個printk就要對核心的實作有一定的了解。
printf和printk的差別:printk會在開頭處加上””樣式的字符,N的範圍是0~7,表示這個資訊的等級。
當printk(“”…);中的n
在核心檔案中Printk.c (kernel) 初始化這個console_loglevel 的等級為7.
/* 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 */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 */ };
## cat /proc/sys/kernel/printk
7 4 1 7
其中的 7 4 1 7,分別對應與:console_loglevel、default_message_loglevel、minimum_console_loglevel、default_console_loglevel
#echo “1 4 1 7”>/proc/sys/kernel/printk 改變這四個值,當被console_loglevel設定為1的時候,所有的偵錯資訊都會被印出來。
在核心檔案:Kernel.h (include\linux) 定義了0~7這8個等級的名稱
#define KERN_EMERG "" /* system is unusable */ #define KERN_ALERT "" /* action must be taken immediately */ #define KERN_CRIT "" /* critical conditions */ #define KERN_ERR "" /* error conditions */ #define KERN_WARNING "" /* warning conditions */ #define KERN_NOTICE "" /* normal but significant condition*/ #define KERN_INFO "" /* informational*/ #define KERN_DEBUG "" /* debug-level messages */ #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])
使用printk:
① printk(KERN_WARNING"there is a warning here!\n");//注意,中间没有逗号间隔。 ② printk(KERN_DEBUG"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
在①和②中,我們需要查看緩衝區log_buf中的數據,才能看到列印出來的資訊。需要使用指令 #dmesg
#``# dmesg``Linux version 2.6.22.6 (book@book-desktop) (gcc version 3.4.5) #19 Thu Dec 8 14:06:03 CST 2016``CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177``Machine: SMDK2440``Memory policy: ECC disabled, Data cache writeback``On node 0 totalpages: 16384`` ``DMA zone: 128 pages used ``for` `memmap`` ``DMA zone: 0 pages reserved`` ``DMA zone: 16256 pages, LIFO batch:3`` ``Normal zone: 0 pages used ``for` `memmap``CPU S3C2440A (id 0x32440001)................................
也可以使用 cat /proc/kmsg& 後台運行,即時列印出偵錯資訊。在②中,__FILE__, FUNCTION, __LINE__分別對應的是那個文件,那個函數,第幾行。非常實用。
# cat /proc/kmsg&``# ./firstdrvtest on``/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 23``/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 25`` /work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 27``# ./firstdrvtest off` `/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 23``/work/LinuxDrives/ 20.printk_debug/first_drv.c first_drv_open 25``/work/LinuxDrives/20.printk_debug /first_drv.c first_drv_open 27
printk vprintk vscnprintf //先把输出信息放入临时BUFFER // Copy the output into log_buf. // 把临时BUFFER里的数据稍作处理,再写入log_buf // 比如printk("abc")会得到"abc", 再写入log_buf // 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息 // 调用硬件的write函数输出 release_console_sem call_console_drivers //从log_buf得到数据,算出打印级别 _call_console_drivers if ((msg_log_level write //con是console_drivers链表中的项,对用具体的输出函 数 在drives/serial/s3c2410.c中查看 在该函数中注册一个console结构体 static void s3c24xx_serial_console_write(struct console *co, const char *s, unsigned int count) { uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); }
串口輸出函數,會呼叫s3c24xx_serial_console_putchar函數
static int s3c24xx_serial_initconsole(void) { ........................... register_console(&s3c24xx_serial_console); return 0; } static struct console s3c24xx_serial_console = { .name = S3C24XX_SERIAL_NAME,//这个宏被定义为:TTYSAC .device = uart_console_device, //init进程,用户程序打开/dev/console的时候会调用 .flags = CON_PRINTBUFFER,//打印还没有初始化化console前保存在log_buf里面的数据 .index = -1,//选择那个串口,由uboot中的命令决定 .write = s3c24xx_serial_console_write,//控制台输出函数 .setup = s3c24xx_serial_console_setup //串口设置函数 };
這個函數和硬體相關,讀取暫存器,看資料是否被傳送完成,最後將資料一個位元組一個位元組的寫入暫存器,s3c2440的串列控制器會自動把資料送出去
static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); while (!s3c24xx_serial_console_txrdy(port, ufcon)) barrier(); wr_regb(cons_uart, S3C2410_UTXH, ch); }
uboot的console=ttySAC0如果在linux中被辨識出來是串口0的,在/kernel/printk.c中有一下程式碼,
__setup("console=", console_setup); static int __init console_setup(char *str) { ........ add_preferred_console(name, idx, options); }
核心開始執行時,會發現「console=…」的命令列參數時候,就會呼叫console_setup函數進行資料解析,對於命令列參數」console=ttySAC0″,會解析出:裝置名稱(name)為ttySAC,索引index為0,這些資訊被保存在類型為console_cmdline、名稱為console_cmdline的全域數組中(注意:數組名稱和數組的類型相同)
在後面的register_console(&s3c24xx_serial_console);的時候,會將s3c24xx_serial_console結構和console_cmdline數組中的設備進行比較。
①解析出來的name為S3C24XX_SERIAL_NAME 既“ttySAC”,而console_cmdline中的指令中的name也為“ttySAC”
②s3c24xx_serial_console結構體中的索引index為-1,表示使用命令列參數「console=ttySAC0」中的索引,index = 0
Through this article, we have learned about the principles and methods of printk, the Linux kernel debugging technology, which can be used to debug and output the kernel. We should choose the appropriate method based on actual needs and follow some basic principles, such as using the correct log level, using the correct format string, using the correct output function, etc. Printk is one of the simplest and most effective debugging functions in the Linux kernel. It can realize feedback and monitoring of the kernel, and can also improve the maintainability and scalability of the kernel. I hope this article can be helpful and inspiring to you.
以上是Linux核心中的偵錯輸出函數:printk詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!