前几天,我在想printk中到底是哪些信息会打印到console上,哪些东西可以通过dmesg来查看。参考了网上一些资料以及自己做的一些实验,总结一下Linux中的console loglevel以及printk, dmesg知识。
只有当printk打印信息时的loglevel小于console loglevel的值(即:优先级高于console loglevel),这些信息才会被打印到console上。
改变console loglevel的方法有如下几种:
1.启动时Kernel boot option:loglevel=level
2.运行时Runtime: dmesg -n level
(注意:demsg -n level 改变的是console上的loglevel,dmesg命令仍然会打印出所有级别的系统信息。)
3.运行时Runtime: echo $level > /proc/sys/kernel/printk
4.运行时Runtime:写程序使用syslog系统调用(可以man syslog)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <unistd.h> #include <sys/syscall.h> static inline int syslog(int type, char *bufp, int len) { return syscall(SYS_syslog, type, bufp, len); } int main() { /* Set the console loglevel to 4 */ syslog(8, 0, 4); return 0; } |
在kernel boot option中:
loglevel Set the default console log level.
loglevel=level
Specify the initial console log level. Any log messages with levels less than this (that is, of higher priority) will be printed to the console, whereas any messages with levels equal to or greater than this will not be displayed.
The console log level can also be changed by the klogd program, or by writing the specified level to the /proc/sys/kernel/printk file. (在2.6.32及之上kernel的Linux系统中,我没有找到klogd这个程序了,应该是有所变化了)
The kernel log levels are:
0 (KERN_EMERG)
The system is unusable.
1 (KERN_ALERT)
Actions that must be taken care of immediately.
2 (KERN_CRIT)
Critical conditions.
3 (KERN_ERR)
Noncritical error conditions.
4 (KERN_WARNING)
Warning conditions that should be taken care of.
5 (KERN_NOTICE)
Normal, but significant events.
6 (KERN_INFO)
Informational messages that require no action.
7 (KERN_DEBUG)
Kernel debugging messages, output by the kernel if the developer enabled debugging at compile time.
KERN_ERR, KERN_DEBUG等是一些宏定义,在$Linux_SRC/include/linux/printk.h中可以查看到。
dmesg是从kernel的ring buffer(环缓冲区)中读取信息的.
man dmesg得到如下信息:
dmesg is used to examine or control the kernel ring buffer.
The program helps users to print out their bootup messages. Instead of copying the messages by hand, the user need only: dmesg > dmesg.log
那什么是ring buffer呢?
在LINUX中,所有的系统信息(包内核信息)都会传送到ring buffer中。而内核产生的信息由printk()打印出来。系统启动时所看到的信息都是由该函数打印到屏幕中。printk()打出的信息往往以 <0>...<2>... 这的数字表明消息的重要级别。高于一定的优先级别(当前的console loglevel)就会打印到console上,否则只会保留在系统的缓冲区中(ring buffer)。
至于dmesg具体是如何从ring buffer中读取的,大家可以看dmesg.c源代码。$Linux-SRC/arch/m68k/tools/amiga/dmesg.c,很短,比较容易读懂。
默认的loglevel在kernel/printk.c中有定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL /* 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 */ DECLARE_WAIT_QUEUE_HEAD(log_wait); 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
4 4 1 7
所得到的信息是$Linux_SRC/include/linux/printk.h中定义的宏:
#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])
依次分别为:
控制台日志级别:优先级高于该值的消息将被打印至控制台
缺省的消息日志级别:将用该优先级来打印没有优先级的消息
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)
缺省的控制台日志级别:控制台日志级别的缺省值
为了下面我做的一个实验:
首先,需要一个module,我的loglevel.c代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for log level */ #include <linux/init.h> /* Needed for the macros */ MODULE_AUTHOR("Jay, <Jay's email> "); MODULE_DESCRIPTION("To test: console log level."); MODULE_LICENSE("GPL"); MODULE_VERSION("Version-0.0.1"); static int __init hello_start(void) { printk(KERN_INFO "Loading loglevel module...\n"); printk(KERN_INFO "Hello, Jay.\n"); printk(KERN_EMERG "------------------------------------\n"); 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"); printk(KERN_EMERG "------------------------------------\n"); return 0; } static void __exit hello_end(void) { printk(KERN_INFO "Goodbye, Jay.\n"); } module_init(hello_start); module_exit(hello_end); |
将这个module编译好之后,有了loglevel.ko这个module文件。
1 2 3 |
[root@vt-snb9 ~]# cat /proc/sys/kernel/printk 7 4 1 7 [root@vt-snb9 ~]# insmod /root/loglevel/loglevel.ko |
console上打印的信息如下:
##注意DEBUG级别的信息没有打印在console上
Loading loglevel module...
Hello, Jay.
------------------------------------
Hello, EMERG.
Hello, ALERT.
Hello, CRIT.
Hello, ERR.
Hello, WARNING.
Hello, NOTICE.
Hello, INFO.
------------------------------------
改变console loglevel之后,
1 2 3 |
[root@vt-snb9 ~]# cat /proc/sys/kernel/printk 4 4 1 7 [root@vt-snb9 ~]# insmod /root/loglevel/loglevel.ko |
##注意"Hello, Jay."这样的INFO级别的就没有打印到console上
------------------------------------
Hello, EMERG.
Hello, ALERT.
Hello, CRIT.
Hello, ERR.
------------------------------------
在kernel启动项目中加入了debug,则启动后:
1 2 3 |
[root@vt-snb9 ~]# cat /proc/sys/kernel/printk 10 4 1 7 [root@vt-snb9 ~]# insmod /root/loglevel/loglevel.ko |
##注意这次DEBUG等级的信息也被打印在console上了
Loading loglevel module...
Hello, Jay.
------------------------------------
Hello, EMERG.
Hello, ALERT.
Hello, CRIT.
Hello, ERR.
Hello, WARNING.
Hello, NOTICE.
Hello, INFO.
Hello, DEBUG.
------------------------------------
而dmesg始终都是可以打印出module所print出来的所有信息的,不管console loglevel为多少,当insmod loglevel之时,dmesg始终会打印如下信息:
Loading loglevel module...
Hello, Jay.
------------------------------------
Hello, EMERG.
Hello, ALERT.
Hello, CRIT.
Hello, ERR.
Hello, WARNING.
Hello, NOTICE.
Hello, INFO.
Hello, DEBUG.
------------------------------------
另外,最后见说一下syslogd吧(或者新的叫做rsyslogd进程)。 在比较新的系统(比如RHEL6.1)中,syslogd已经存在了,取而代之的是rsyslogd,功能是差不多的,配置文件在/etc/rsyslogd.conf
syslogd这个守护进程根据/etc/syslog.conf,将不同的服务产生的Log记录到不同的文件中.
LINUX系统启动后,由/etc/init.d/sysklogd先后启动klogd,syslogd两个守护进程。
其中klogd会通过syslog()系统调用或者读取/proc/kmsg文件来从系统缓冲区(ring buffer)中得到由内核printk()发出的信息.而syslogd是通过klogd来读取系统内核信息.
(1)所有系统信息是输出到ring buffer中去的,dmesg所显示的内容也是从ring buffer中读取的.
(2)LINUX系统中/etc/init.d/sysklogd会启动2个守护进程:Klogd, Syslogd
(3)klogd是负责读取内核信息的,有2种方式:
syslog()系统调用(这个函数用法比较全,大家去MAN一下看看);
直接的对/proc/kmsg进行读取(再这提一下,/proc/kmsg是专门输出内核信息的地方)
(4)Klogd的输出结果会传送给syslogd进行处理,syslogd会根据/etc/syslog.conf的配置把log信息输出到/var/log/下的不同文件中.
可以将printk与syslog接合使用, 用在内核开发方面很不错的应用:
修改/etc/syslog.conf (或者是/etc/rsyslogd.conf)
kern.* /tmp/my_kernel_debug.txt
就可将kernel的信息输出到文件中了,这样更方便查看。
日志文件详细地记录了系统每天发生的各种各样的事件。用户可以通过日志文件检查错误产生的原因,或者在受到攻击和黑客入侵时追踪攻击者的踪迹。日志的两个比较重要的作用是:审核和监测。
Linux系统的日志主要分为两种类型:
1.进程所属日志
由用户进程或其他系统服务进程自行生成的日志,比如服务器上的access_log与error_log日志文件。
2.syslog消息 【syslogd, klogd协同作用,前面以及提及,新版本中都没了klogd了】
系统syslog记录的日志,任何希望记录日志的系统进程或者用户进程都可以给调用syslog来记录日志。
日志系统可以划分为三个子系统:
1.连接时间日志--由多个程序执行,把纪录写入到/var/log/wtmp和/var/run/utmp,login等程序更新wtmp和utmp文件,使系统管理员能够跟踪谁在何时登录到系统。
2.进程统计--由系统内核执行。当一个进程终止时,为每个进程往进程统计文件(pacct或acct)中写一个纪录。进程统计的目的是为系统中的基本服务提供命令使用统计。
3.错误日志--由syslogd(8)执行。各种系统守护进程、用户程序和内核通过syslog(3)向文件/var/log/messages报告值得注意的事件。