Linux kernel log之1.printk & demsg


一.printk

printk函数主要做两件事情:第一件就是将信息记录到log中,而第二件事就是调用控制台驱动来将信息输出。printk的相关函数定义在linux/printk.h。

1.日志级别

printk需要设置日志级别,用来控制printk打印的这条信息是否在终端上显示的,当printk设置的日志级别高于控制台级别时,printk要打印的信息才会在控制台打印出来。

内核日志一共有8种级别:

#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                        */

2.控制台级别 

#define MINIMUM_CONSOLE_LOGLEVEL 1 /*可以使用的最小日志级别*/
#define DEFAULT_CONSOLE_LOGLEVEL 7 /*默认的控制台级别*/
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* 默认的日志级别 */

int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL,/*控制台日志级别:优先级高于该值的消息将被打印至控制台*/
DEFAULT_MESSAGE_LOGLEVEL,/*缺省的消息日志级别:将用该优先级来打印没有优先级的消息*/
MINIMUM_CONSOLE_LOGLEVEL,/*最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)*/
DEFAULT_CONSOLE_LOGLEVEL,/*缺省的控制台日志级别:控制台日志级别的缺省值*/
};

#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])

使用命令 cat /proc/sys/kernel/printk来查看这四个值

其中的 4 4 1 7,分别对应与:console_loglevel、default_message_loglevel、minimum_console_loglevel、default_console_loglevel

default_message_loglevel是缺省时的消息日志级别,因此当printk未指定优先级时,将以该默认级别输出,也就是DEFAULT_MESSAGE_LOGLEVEL =4, 对应KERN_WARNING。

也就是说printk("hello world\n");就表示printk(KERN_WARNING "hello world\n");

那如果我们将控制台级别设成<4,如:

Echo 3 > /proc/sys/kernel/printk

那么printk("hello world\n");就无法输出到控制台。

3.如何修改控制台级别

Echo "n" > /proc/sys/kernel/printk

Eg: Echo 8 > /proc/sys/kernel/printk

那么此时所有的printk日志级别都会被输出到控制台,如下图所示。

printk ( KERN_EMERG "Hello, EMERG.\n" ) ;
printk ( KERN_ALERT "Hello, ALERT.\n" ) ;
printk ( KERN_CRIT "Hello, CRIT.\n" ) ;
printk ( KERN_ERR "Hello, ERR.\n" ) ;
printk ( KERN_WARNING "Hello, WARNING.\n" ) ;
printk ( KERN_NOTICE "Hello, NOTICE.\n" ) ;
printk ( KERN_INFO "Hello, INFO.\n" ) ;
printk ( KERN_DEBUG "Hello, DEBUG.\n" ) ;

除了上面的方法修改console打印级别外,还可以修改bootargs来设定:

Uboot中修改“console=ttyS0,115200”改为“loglevel=7 console=ttyS0,115200”,表示设置内核的console_loglevel 值=7,开机cat /proc/sys/kernel/printk,可以看到控制台级别被设置成了7。

4. printk带时间戳讯息

make menuconfig开启如下:

二 linux/prink.h相关函数

#define pr_emerg(fmt, ...) \
        printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
        printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
        printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
        printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
        printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
        printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
        printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

/* If you are writing a driver, please use dev_dbg instead */ #if defined(CONFIG_DYNAMIC_DEBUG) #include /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) #elif defined(DEBUG) #define pr_debug(fmt, ...) \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif

pr_emergpr_info都是一些基本的kernel打印函数,用来设置内核日志打印级别,可以看到它和下面这种打印本质上并无差异。 

printk ( KERN_EMERG "Hello, EMERG.\n" ) ;
printk ( KERN_ALERT "Hello, ALERT.\n" ) ;
printk ( KERN_CRIT "Hello, CRIT.\n" ) ;
printk ( KERN_ERR "Hello, ERR.\n" ) ;
printk ( KERN_WARNING "Hello, WARNING.\n" ) ;
printk ( KERN_NOTICE "Hello, NOTICE.\n" ) ;
printk ( KERN_INFO "Hello, INFO.\n" ) ;
printk ( KERN_DEBUG "Hello, DEBUG.\n" ) ;

而pr_debug则有3种输出方式,当开启dynamic_debug后,则走dynamic_pr_debug流程(dynamic_debug见下一节)。当用户开启了DEBUG宏,则走printk流程,否则什么都不打印。

三 dmesg命令

那kernel中printk所有日志级别的打印都会将开机信息存储在ring buffer中。dmesg命令是从kernel ring buffer中读取内核日志信息。开机信息亦保存在/var/log目录中,名称为dmesg的文件里。因此可以用dmesg

命令查看。

  -c: 当完成打印显示后清除环缓冲内的内容。

  -s: 缓冲区大小

  定义一个大小为"缓冲区大小"的缓冲区用于查询内核环缓冲区。默认大小为 8196,如果你设置了一个大于默认值的环缓冲区,那你就可以用这个选项定义一个相当的缓冲区来查看完整的环缓冲区内容。

  -n:级别

其实用dmesg -n也是可以设置控制台打印级别。