Linux内核日志

printk概述

对于做Linux内核开发的人来说,printk实在是再熟悉不过了。内核启动时显示的各种信息大部分都是通过printk来实现的,编写驱动时也经常使用printk来作为一种调试手段。printk的设计是通过一个ring buffer(环形缓冲区)实现的。

printk使用限制:在系统启动过程的早期,例如终端和控制台初始化之前,虽然可以使用printk,但是并不能立即输出而是将信息缓存在printk的简单ring buffer中,待终端和控制台初始化之后,将所有缓存信息一并输出。

在终端上,可以通过dmesgcat /proc/kmsg命令查看ring buffer的内容,通过内核CONFIG_LOG_BUF_SHIFT配置可以修改这个环形缓冲区的大小。

日志的使用

日志级别

在调试内核模块时,有些调试信息无法打印的控制台上,这是由日志等级来控制的。内核根据日志级别来判断是否在终端(console)上打印消息。

printk有8个loglevel,定义在include/linux/kern_levels.h中:

#define KERN_EMERG	KERN_SOH "0"	/* system is unusable 系统不可使用 */
#define KERN_ALERT	KERN_SOH "1"	/* action must be taken immediately */
									/* 需要立即采取行动 */
#define KERN_CRIT	KERN_SOH "2"	/* critical conditions 严重情况 */
#define KERN_ERR	KERN_SOH "3"	/* error conditions 错误情况 */
#define KERN_WARNING	KERN_SOH "4"	/* warning conditions 警告情况 */
#define KERN_NOTICE	KERN_SOH "5"	/* normal but significant condition 正常情况 */
#define KERN_INFO	KERN_SOH "6"	/* informational 信息型消息 */
#define KERN_DEBUG	KERN_SOH "7"	/* debug-level messages 调试级别的信息 */
/* 使用默认内核日志级别 */
#define KERN_DEFAULT	KERN_SOH "d"	/* the default kernel loglevel */
/*
 * Annotation for a "continued" line of log printout (only done after a
 * line that had no enclosing \n). Only to be used by core/arch code
 * during early bootup (a continued line is not SMP-safe otherwise).
 * 标注一个"连续"的日志打印输出行
 */
#define KERN_CONT	KERN_SOH "c"

内核用这些宏指定日志等级,并和当前终端的日志等级console_loglevel来决定是不是向终端打印信息,printk的使用如下:

printk(KERN_EMERG "log_level:%s\n", KERN_EMERG);

如果使用printk时没有指定日志等级,内核会选用MESSAGE_LOGLEVEL_DEFAULT,这个定义在include/linux/printk.h中:

#define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT

相关辅助宏:如果每次使用printk时都要指定日志级别,似乎有点麻烦了。所有内核中定义了一些宏来方便使用printk,如下:

//include\linux\printk.h
#ifndef pr_fmt
#define pr_fmt(fmt) fmt
#endif
/*
 * These can be used to print at the various log levels.
 * All of these will print unconditionally, although note that pr_debug()
 * and other debug macros are compiled out unless either DEBUG is defined
 * or CONFIG_DYNAMIC_DEBUG is set.
 */
#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__)
/*
 * Like KERN_CONT, pr_cont() should only be used when continuing
 * a line with no newline ('\n') enclosed. Otherwise it defaults
 * back to KERN_DEFAULT.
 */
#define pr_cont(fmt, ...) \
	printk(KERN_CONT fmt, ##__VA_ARGS__)

/* pr_devel() should produce zero code unless DEBUG is defined */
#ifdef DEBUG
#define pr_devel(fmt, ...) \
	printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel(fmt, ...) \
	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif

/* If you are writing a driver, please use dev_dbg instead */
/* 如果你在写一个驱动,请使用dev_dbg代替 */
#if defined(CONFIG_DYNAMIC_DEBUG)
#include <linux/dynamic_debug.h>

/* 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_devel和pr_debug这些宏只有在定义了DEBUG之后才会产生实际的printk代码,而在写驱动程序时,经常使用dev_dbg来代替printk

控制台日志级别

内核会把级别比某个特定值低的所有消息显示在终端上,但是所有信息都会记录在printk的ring buffer中。通过cat /proc/sys/kernel/printk可以查看内核设置的这个日志等级的特定值。

# cat /proc/sys/kernel/printk
7  4  1  7
#上面四个值分别对应:
控制台日志级别:级别高于该值的消息将被打印到控制台
默认的消息日志级别:用该等级打印没有优先级的消息
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)
默认的控制台日志级别:控制台日志级别的缺省值
#注:数值越小,优先级越高

上述四个值在kernel/printk/printk.c中被定义,如下:

int console_printk[4] = {
	CONSOLE_LOGLEVEL_DEFAULT,	/* console_loglevel */
	MESSAGE_LOGLEVEL_DEFAULT,	/* default_message_loglevel */
	CONSOLE_LOGLEVEL_MIN,		/* minimum_console_loglevel */
	CONSOLE_LOGLEVEL_DEFAULT,	/* default_console_loglevel */
};

当然我们可以手动控制printk打印,例如,屏蔽掉所有的内核printk打印,使用echo 1 4 1 7 > /proc/sys/kernel/printk将控制台日志级别设置到0或1的较高优先级。

使用cmdline设置控制日志打印

一个简单的应用,使用cmdline的关键字控制内核日志打印

/* 直接添加到kernel/printk/printk.c中 */
//根据kdbg的值设置console_loglevel 
static int __init kdbg_config(char *str)
{
    /*get the value from kdbg parameter*/	
    if(str) {
        console_loglevel = *str-'0';
    }
    else {
        console_loglevel = 0;
    }
    return 0;
}
//解析cmdline,处理kdbg
__setup("kdbg=",kdbg_config);
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值