一直都知道内核printk分级机制,但是没有去了解过,前段时间和一个同事聊到开机启动打印太多,只需要设置一下等级即可;另外今天看驱动源码,也看到类似于Printk(KERN_ERR "....")的打印信息,以前用都是直接printk("...."),今晚回来就把printk这个机制熟悉一下。
转自:http://blog.csdn.net/tangkegagalikaiwu/article/details/8572365
另外/var/log下20个Linux日志文件详解
http://h2appy.blog.51cto.com/609721/781281/
一、printk概述
注意上面说到的是:“几乎”在内核的任何地方都可以使用。 那什么地方使用会有“问题”? 那就是在系统启动过程的早期,终端初始化之前的某些地方虽然可以使用,但是在终端和控制台被初始化之前所有信息都被缓存在printk的简单的ring buffer(环形缓冲区)中,直到终端和控制台被初始化之后,所有缓存信息都被一并输出。
- 函数名
- 能够更早地工作(输出信息)
- 她有自己的小缓存(一般为512B)
- 一次性输出到硬件设备,不再以ring buffer的形式保留信息。
二、printk的使用
(1)日志等级
#define KERN_EMERG "<0>" /* 系统不可使用 */ #define KERN_ALERT "<1>" /* 需要立即采取行动 */ #define KERN_CRIT "<2>" /* 严重情况 */ #define KERN_ERR "<3>" /* 错误情况 */ #define KERN_WARNING "<4>" /* 警告情况 */ #define KERN_NOTICE "<5>" /* 正常情况, 但是值得注意 */ #define KERN_INFO "<6>" /* 信息型消息 */ #define KERN_DEBUG "<7>" /* 调试级别的信息 */ /* 使用默认内核日志级别 */ #define KERN_DEFAULT "<d>" /* * 标注为一个“连续”的日志打印输出行(只能用于一个 * 没有用 \n封闭的行之后). 只能用于启动初期的 core/arch 代码 * (否则续行是非SMP的安全). */ #define KERN_CONT "<c>"
/* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
printk(KERN_EMERG "log_level:%s\n", KERN_EMERG);
printk( "<0>" "log_level:%s\n", KERN_EMERG);
kern_levels.h printk.h printk.c 几个文件里面分别定义了printk和console的printk显示等级
echo 8 > /proc/sys/kernel/printk设置console的loglevel
- 一种选择是保持终端的默认记录等级不变,给所有调试信息KERN CRIT或更低的等级以保证信息一定会被输出。
- 另一种方法则相反,给所有调试信息KERN DEBUG等级,而调整终端的默认记录等级为7,也可以输出所有调试信息。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> //#define __LIBRARY__ /* _syscall3 and friends are only available through this */ //#include <linux/unistd.h> /* define the system call, to override the library function */ //_syscall3(int, syslog, int, type, char *, bufp, int, len); int main(int argc, char **argv) { int level; if (argc == 2) { level = atoi(argv[1]); /* the chosen console */ } else { fprintf(stderr, "%s: need a single arg\n", argv[0]); exit(1); } if (klogctl(8, NULL, level) < 0) { fprintf(stderr, "%s: syslog(setlevel): %s\n", argv[0], strerror(errno)); exit(1); } exit(0); }
(2)相关辅助宏
#ifndef pr_fmt #define pr_fmt(fmt) fmt #endif #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__) #define pr_cont(fmt, ...) \ printk(KERN_CONT fmt, ##__VA_ARGS__) /* 除非定义了DEBUG ,否则pr_devel()不产生任何代码 */ #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 /* 如果你在写一个驱动,请使用dev_dbg */ #if defined(DEBUG) #define pr_debug(fmt, ...) \ printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #elif defined(CONFIG_DYNAMIC_DEBUG) /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif
(3)输出速率控制
在调试的时候,有时某些部分可能printk会产生大量输出, 导致系统无法正常工作,并可能使系统日志ring buffer溢出(旧的信息被快速覆盖)。特别地,当使用一个慢速控制台设备(如串口), 过量输出也能拖慢系统。这样反而难于发现系统出问题的地方。所以你应当非常注意:正常操作时不应当打印任何东西,打印的输出应当是指示需要注意的异常,并小心不要做过头。
#define printk_ratelimit() __printk_ratelimit(__func__)
这个函数应当在你认为打印一个可能会出现大量重复的消息之前调用,如果这个函数返回非零值, 继续打印你的消息, 否则跳过它。典型的调用如这样:
if (printk_ratelimit()) printk(KERN_NOTICE "The printer is still on fire\n");
- /proc/sys/kern/printk_ratelimit( 可以看作一个监测周期,在这个周期内只能发出下面的控制量的信息)
- /proc/sys/kernel/printk_ratelimit_burst(以上周期内的最大消息数,如果超过了printk_ratelimit()返回0)
(4)最后特别提醒
三、printk的内核实现
static char __log_buf[__LOG_BUF_LEN];
/* * 在指向log_buf时并没有用log_buf_len做限制 - 所以他们 * 在作为下标使用前必须用掩码处理(去除CONFIG_LOG_BUF_SHIFT以上的高位) */ static unsigned log_start; /* log_buf中的索引: 指向由syslog()读取的下一个字符 */ static unsigned con_start; /* log_buf中的索引: 指向发送到console的下一个字符 */ static unsigned log_end; /* log_buf中的索引:最近写入的字符地址 + 1 */
四、用户空间访问内核日志
- 通过glibc的klogctl函数接口调用内核的syslog系统调用
- 通过fs/proc/kmsg.c内核模块中导出的procfs接口:/proc/kmsg文件。