注意上面说到的是:“几乎”在内核的任何地方都可以使用。那什么地方使用会有“问题”?那就是在系统启动过程的早期,终端初始化之前的某些地方虽然可以使用,但是在终端和控制台被初始化之前所有信息都被缓存在printk的简单的ring buffer(环形缓冲区)中,直到终端和控制台被初始化之后,所有缓存信息都被一并输出。
- #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);
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- //#define __LIBRARY__/* _syscall3and friends are only available through this */
- //#include<linux/unistd.h>
- /* define the systemcall, 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);
- }
- #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
在调试的时候,有时某些部分可能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)
- 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 */
日志级别的范围是0~7,没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL,其定义列出如下(在linux26/kernel/printk.c中):
#define DEFAULT_MESSAGE_LOGLEVEL 4 |
内核可把消息打印到当前控制台上,可以指定控制台为字符模式的终端或打印机等。默认情况下,“控制台”就是当前的虚拟终端。
为了更好地控制不同级别的信息显示在控制台上,内核设置了控制台的日志级别console_loglevel。printk日志级别的作用是打印一定级别的消息,与之类似,控制台只显示一定级别的消息。
当日志级别小于console_loglevel时,消息才能显示出来。控制台相应的日志级别定义如下:
#define MINIMUM_CONSOLE_LOGLEVEL 1 #define DEFAULT_CONSOLE_LOGLEVEL 7 int console_printk[4] = { DEFAULT_CONSOLE_LOGLEVEL, DEFAULT_MESSAGE_LOGLEVEL, MINIMUM_CONSOLE_LOGLEVEL, DEFAULT_CONSOLE_LOGLEVEL, }; |
如果系统运行了klogd和syslogd,则无论console_loglevel为何值,内核消息都将追加到/var/log/messages中。如果klogd没有运行,消息不会传递到用户空间,只能查看/proc/kmsg。
变量console_loglevel的初始值是DEFAULT_CONSOLE_LOGLEVEL,可以通过sys_syslog系统调用进行修改。调用klogd时可以指定-c开关选项来修改这个变量。如果要修改它的当前值,必须先杀掉klogd,再加-c选项重新启动它。
注:#ps -e 查看所有进程PID,然后KILL。
通过读写/proc/sys/kernel/printk文件可读取和修改控制台的日志级别。查看这个文件的方法如下:
#cat /proc/sys/kernel/printk 6 4 1 7 |
上面显示的4个数据分别对应控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别。
可用下面的命令设置当前日志级别:
# echo 8 > /proc/sys/kernel/printk