KERN_EMERG 用于紧急事件消息,它们一般是系统崩溃之前提示的消息。
KERN_ALERT 用于需要立即采取动作的情况。
KERN_CRIT 临界状态,通常涉及严重的硬件或软件操作失败。
KERN_ERR 用于报告错误状态;设备驱动程序会经常使用KERN_ERR来报告来自硬件的问题。
KERN_WARNING 对可能出现问题的情况进行警告,这类情况通常不会对系统造成严重问题。
KERN_NOTICE 有必要进行提示的正常情形。许多与安全相关的状况用这个级别进行汇报。
KERN_INFO 提示性信息。很多驱动程序在启动的时候,以这个级别打印出它们找到的硬件信息。
KERN_DEBUG 用于调试信息。
dmesg是从kernel的ring buffer(环缓冲区)中读取信息的.
那什么是ring buffer呢?
在LINUX中,所有的系统信息(包内核信息)都会传送到ring buffer中.而内核产生的信息由printk()打印出来。系统启动时所看到的信息都是由该函数打印到屏幕中。 printk()打出的信息往往以 <0>...<2>... 这的数字表明消息的重要级别。高于一定的优先级别会打印到屏幕上,否则只会保留在系统的缓冲区中(ring buffer)。
至于dmesg具体是如何从ring buffer中读取的,大家可以看dmesg.c源代码.很短,比较容易读懂.
是syslogd这个守护进程根据/etc/syslog.conf,将不同的服务产生的Log记录到不同的文件中.
LINUX系统启动后,由/etc/init.d/sysklogd先后启动klogd,syslogd两个守护进程。
其中klogd会通过syslog()系统调用或者读取proc文件系统来从系统缓冲区(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接合使用, 用在内核开发方面很不错.
内核用的打印函数printk完全是和stdin或stdout无关的,因为一开始到start_kernel函数刚开始进入内核就可以用printk 函数了,而建立stdin和stdout是在init函数中实现的。有个问题,这里的代码中,建立stdin和stdout如下
if (open("/dev/null", O_RDWR, 0) < 0)
printk("Warning: unable to open an initial console./n");
(void) dup(0);
(void) dup(0);
问题在于它打开的是/dev/null,而一般pc机上的linux打开的都是/dev/console,而且我把这几行代码删除也没有问题,所以我猜想这里建立stdin和stdout并没什么用,肯定在shell中建立了定位到串口的stdin和stdout。所以接下来还需要看看busybox的代码吧。
在这里还是主要分析一下printk实现的原理。
static spinlock_t logbuf_lock = SPIN_LOCK_UNLOCKED; //定义logbuf_lock,并初始化为unlock状态
static char log_buf[LOG_BUF_LEN]; //保存日志数据的缓冲区
#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
static DECLARE_MUTEX(console_sem); //定义全局互斥信号量console_sem并初始化为1
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
unsigned long flags;
int printed_len;
char *p;
static char printk_buf[1024];
static int log_level_unknown = 1;
if (oops_in_progress) // default : oops_in_progress = 0
{ //oops_in_progress指示进程发生错误,只有在panic()函数中才等于1
//所以一般情况下下两句都不运行
/* If a crash is occurring, make sure we can't deadlock */
spin_lock_init(&logbuf_lock); //初始化logbuf_lock
/* And make sure that we print immediately */
init_MUTEX(&console_sem); //初始化console_sem为互斥的信号量,初值为1
}
/* This stops the holder of console_sem just where we want him */
spin_lock_irqsave(&logbuf_lock, flags);
//一般spin_lock在单cpu中无效的,所以spin_lock_irqsave真正的作用是关中断 和保存状态寄存器。
/* Emit the output into the temporary buffer */
va_start(args, fmt);
printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
//先把数据格式化到printk_buf中去
va_end(args);
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
*/
//emit_log_char 把字符存入log_buf中等待被发送,具体的参见下面的分析
for (p = printk_buf; *p; p++) {
if (log_level_unknown) {
if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {
emit_log_char('<');
emit_log_char(default_message_loglevel + '0');
emit_log_char('>');
}
//如果没有提供<1>类似的日志级别,则在此加上<4>
//我这里的default_message_loglevel=4
log_level_unknown = 0;
}
emit_log_char(*p);
if (*p == '/n') //每一行前面都需要加<4>之类的日志级别
log_level_unknown = 1;
}
if (!arch_consoles_callable()) // unexecute
{
/* 控制台是否可调用,一般下面的不会被执行
* On some architectures, the consoles are not usable
* on secondary CPUs early in the boot process.
*/
spin_unlock_irqrestore(&logbuf_lock, flags);
goto out;
}
if (!down_trylock(&console_sem)) //lock ok
{
/* down_trylock获取信号量,lock则返回0,否则立即返回非0值
* We own the drivers. We can drop the spinlock and let
* release_console_sem() print the text
*/
spin_unlock_irqrestore(&logbuf_lock, flags);
console_may_schedule = 0;
release_console_sem(); //在这个函数中把数据发送到串口并释放console_sem
} else {
/*
* Someone else owns the drivers. We drop the spinlock, which
* allows the semaphore holder to proceed and to call the
* console drivers with the output which we just produced.
*/
spin_unlock_irqrestore(&logbuf_lock, flags);
}
out:
return printed_len;
}
static unsigned long log_start; /* Index into log_buf: next char to be read by syslog() */
static unsigned long con_start; /* Index into log_buf: next char to be sent to consoles */
static unsigned long log_end; /* Index into log_buf: most-recently-written-char + 1 */
static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */
static void emit_log_char(char c)
{
LOG_BUF(log_end) = c; //把字符c存到log_buf缓冲区中,缓冲区满了就会覆盖开始的数据
log_end++; //
if (log_end - log_start > LOG_BUF_LEN) //log_start指示syslog读取的开始
log_start = log_end - LOG_BUF_LEN;//缓冲区满了会把开始的指针向前推
if (log_end - con_start > LOG_BUF_LEN) //con_start指示控制台读取的开始
con_start = log_end - LOG_BUF_LEN;
if (logged_chars < LOG_BUF_LEN)
logged_chars++;
}
void release_console_sem(void)
{
unsigned long flags;
unsigned long _con_start, _log_end;
unsigned long must_wake_klogd = 0;
for ( ; ; ) {
spin_lock_irqsave(&logbuf_lock, flags);//关中断和保存flag
must_wake_klogd |= log_start - log_end; //唤醒klogd标志
if (con_start == log_end)
break; /* Nothing to print */
_con_start = con_start;
_log_end = log_end;
con_start = log_end; /* Flush , con_start向前移用了,可见缓冲区是循环使用的 */
spin_unlock_irqrestore(&logbuf_lock, flags);
call_console_drivers(_con_start, _log_end);//在这个函数中发送数据,见下面的分析
}
console_may_schedule = 0; //指示数据发送时是否能进行任务调度,在使用串口控制台时没用
up(&console_sem); //释放信号量
spin_unlock_irqrestore(&logbuf_lock, flags);
if (must_wake_klogd && !oops_in_progress)
wake_up_interruptible(&log_wait);
}
static void call_console_drivers(unsigned long start, unsigned long end)
{
unsigned long cur_index, start_print;
static int msg_level = -1;
if (((long)(start - end)) > 0)
BUG();
cur_index = start;
start_print = start;
while (cur_index != end) {
if ( msg_level < 0 &&
((end - cur_index) > 2) &&
LOG_BUF(cur_index + 0) == '<' &&
LOG_BUF(cur_index + 1) >= '0' &&
LOG_BUF(cur_index + 1) <= '7' &&
LOG_BUF(cur_index + 2) == '>')
{
msg_level = LOG_BUF(cur_index + 1) - '0';
cur_index += 3;
start_print = cur_index;
} //去除每行开头的类似<4>的日志级别,把它赋给msg_level
while (cur_index != end) {
char c = LOG_BUF(cur_index);
cur_index++;
if (c == '/n') {
if (msg_level < 0) {
/*
* printk() has already given us loglevel tags in
* the buffer. This code is here in case the
* log buffer has wrapped right round and scribbled
* on those tags
*/
msg_level = default_message_loglevel;
}
_call_console_drivers(start_print, cur_index, msg_level);
//发送一行数据
msg_level = -1;
start_print = cur_index;
break;
}
}
}
_call_console_drivers(start_print, end, msg_level); //发送剩余的数据
}
struct console *console_drivers; //全局的console类型的结构体
static void _call_console_drivers(unsigned long start, unsigned long end, int msg_log_level)
{
//如果msg_log_level < console_loglevel 并且 console_drivers存在 并且 start != end
if (msg_log_level < console_loglevel && console_drivers && start != end) {
if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
/* wrapped write */
//缓冲区循环使用就会出现(start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)
//的清况,于是就分两部分来发送.
//__call_console_drivers才是真正的发送函数
__call_console_drivers(start & LOG_BUF_MASK, LOG_BUF_LEN);
__call_console_drivers(0, end & LOG_BUF_MASK);
} else {
__call_console_drivers(start, end);
}
}
}
static void __call_console_drivers(unsigned long start, unsigned long end)
{
struct console *con;