转载自 http://blog.csdn.net/stone_kingnet/article/details/3196225
在头文件 中定义了 【8种可用的日志级别字符串】
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
//所以一般情况下下两句都不运行
spin_lock_init(&logbuf_lock); //初始化logbuf_lock
init_MUTEX(&console_sem); //初始化console_sem为互斥的信号量,初值为1
}
spin_lock_irqsave(&logbuf_lock,
flags);
//一般spin_lock在单cpu中无效的,所以spin_lock_irqsave真正的作用是关中断
和保存状态寄存器。
va_start(args,
fmt);
printed_len =
vsnprintf(printk_buf, sizeof(printk_buf), fmt,
args);
//先把数据格式化到printk_buf中去
va_end(args);
//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
{
spin_unlock_irqrestore(&logbuf_lock,
flags);
goto
out;
}
if
(!down_trylock(&console_sem)) //lock
ok
{
spin_unlock_irqrestore(&logbuf_lock,
flags);
console_may_schedule
= 0;
release_console_sem(); //在这个函数中把数据发送到串口并释放console_sem
} else
{
spin_unlock_irqrestore(&logbuf_lock,
flags);
}
out:
return
printed_len;
}
static unsigned long log_start;
static unsigned long con_start;
static unsigned long log_end;
static unsigned long
logged_chars;
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;
_con_start =
con_start;
_log_end =
log_end;
con_start =
log_end;
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) {
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))
{
//缓冲区循环使用就会出现(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;
for (con =
console_drivers; con; con = con->next)
{
if ((con->flags
& CON_ENABLED) &&
con->write)
con->write(con, &LOG_BUF(start), end -
start);
}
//调用console_drivers->write来把数据发送出去
}
接下来理解一下console_drivers这个结构体指针
struct console
{
char
name[8];
void (*write)(struct
console *, const char *, unsigned);
int (*read)(struct
console *, const char *, unsigned);
kdev_t
(*device)(struct console *);
int (*wait_key)(struct
console *);
void
(*unblank)(void);
int (*setup)(struct
console *, char *);
short
flags;
short
index;
int
cflag;
struct console
*next;
};
而开始console_drivers这个指针是NULL的,在什么时候被赋值的呢,原本以为应该在用printk以前就被初始化了,其实不然,它是在start_kernel函数中调用的console_init()函数中被初始化的.最好的办法还是看看代码.
void __init console_init(void)
{
memset(ldiscs, 0,
sizeof(ldiscs));
(void)
tty_register_ldisc(N_TTY,
&tty_ldisc_N_TTY);
memset(&tty_std_termios,
0, sizeof(struct termios));
memcpy(tty_std_termios.c_cc,
INIT_C_CC, NCCS);
tty_std_termios.c_iflag
= ICRNL | IXON;
tty_std_termios.c_oflag
= OPOST | ONLCR;
#ifdef CONFIG_MIZI
//CONFIG_MIZI=1
tty_std_termios.c_cflag
= B115200 | CS8 | CREAD | HUPCL;
#else
tty_std_termios.c_cflag
= B38400 | CS8 | CREAD | HUPCL;
#endif
tty_std_termios.c_lflag
= ISIG | ICANON | ECHO | ECHOE | ECHOK
|
ECHOCTL | ECHOKE |
IEXTEN;
#ifdef CONFIG_VT
//如果不是串口控制台就定义这个虚拟控制台
con_init();
//于是输入是键盘输出是显示器
#endif
#ifdef
CONFIG_AU1000_SERIAL_CONSOLE
au1000_serial_console_init();
#endif
#ifdef CONFIG_SERIAL_CONSOLE
#if (defined(CONFIG_8xx) ||
defined(CONFIG_8260))
console_8xx_init();
#elif defined(CONFIG_MAC_SERIAL) &&
defined(CONFIG_SERIAL)
if (_machine ==
_MACH_Pmac)
mac_scc_console_init();
else
serial_console_init();
#elif
defined(CONFIG_MAC_SERIAL)
mac_scc_console_init();
#elif defined(CONFIG_PARISC)
pdc_console_init();
#elif defined(CONFIG_SERIAL)
serial_console_init();
#endif
#ifdef CONFIG_SGI_SERIAL
sgi_serial_console_init();
#endif
#if defined(CONFIG_MVME162_SCC) ||
defined(CONFIG_BVME6000_SCC) ||
defined(CONFIG_MVME147_SCC)
vme_scc_console_init();
#endif
#if defined(CONFIG_SERIAL167)
serial167_console_init();
#endif
#if defined(CONFIG_SH_SCI)
sci_console_init();
#endif
#endif
#ifdef CONFIG_TN3270_CONSOLE
tub3270_con_init();
#endif
#ifdef CONFIG_TN3215
con3215_init();
#endif
#ifdef CONFIG_HWC
hwc_console_init();
#endif
#ifdef CONFIG_STDIO_CONSOLE
stdio_console_init();
#endif
#ifdef CONFIG_SERIAL_CORE_CONSOLE
//
CONFIG_SERIAL_CORE_CONSOLE=1
uart_console_init();
//这里唯一一个被运行的函数
#endif
#ifdef CONFIG_ARC_CONSOLE
arc_console_init();
#endif
#ifdef
CONFIG_SERIAL_TX3912_CONSOLE
tx3912_console_init();
#endif
}
void __init
uart_console_init(void)
{
#ifdef
CONFIG_SERIAL_AMBA_CONSOLE
ambauart_console_init();
#endif
#ifdef
CONFIG_SERIAL_ANAKIN_CONSOLE
anakin_console_init();
#endif
#ifdef
CONFIG_SERIAL_CLPS711X_CONSOLE
clps711xuart_console_init();
#endif
#ifdef
CONFIG_SERIAL_21285_CONSOLE
rs285_console_init();
#endif
#ifdef
CONFIG_SERIAL_SA1100_CONSOLE
sa1100_rs_console_init();
#endif
#ifdef
CONFIG_SERIAL_8250_CONSOLE
serial8250_console_init();
#endif
#ifdef
CONFIG_SERIAL_UART00_CONSOLE
uart00_console_init();
#endif
#ifdef CONFIG_SERIAL_S
3C2400_CONSOLE
s3c2400_console_init();
#endif
#ifdef
CONFIG_SERIAL_S3C2410_CONSOLE
s3c2410_console_init();
//这个函数被运行
#endif
}
void __init
s3c2410_console_init(void)
{
register_console(&s3c2410_cons);
//调用注册控制台的函数
}
static struct console s3c2410_cons =
{
name:
"ttyS",
write:
s3c2410_console_write,
device:
s3c2410_console_device,
wait_key:
s3c2410_console_wait_key,
setup:
s3c2410_console_setup,
flags:
CON_PRINTBUFFER,
index:
-1,
};
//这个就是console_drivers所指向的结构了
void register_console(struct console *
console)
{ //该函数就是把console_drivers这个全局指针指向console结构体了,而且在注册完后
//会把存在缓冲区中的都发送出去,所以在注册console以前调用的printk并不发送数据
//而只是把数据存到缓冲区里,注册了以后才能被马上发送.多个控制台被注册的话就会
//形成一个链表结构,都能发送数据.
int
i;
unsigned long
flags;
if (preferred_console
< 0) {
if (console->index
< 0)
console->index = 0;
if (console->setup
== NULL ||
console->setup(console, NULL) == 0)
{
console->flags |= CON_ENABLED |
CON_CONSDEV;
preferred_console = 0;
}
}
for(i = 0; i <
MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
{
if
(strcmp(console_cmdline[i].name, console->name) !=
0)
continue;
if (console->index
>= 0 &&
console->index
!= console_cmdline[i].index)
continue;
if (console->index
< 0)
console->index =
console_cmdline[i].index;
if (console->setup
&&
console->setup(console, console_cmdline[i].options) !=
0)
break;
console->flags |=
CON_ENABLED;
console->index =
console_cmdline[i].index;
if (i ==
preferred_console)
console->flags |= CON_CONSDEV;
break;
}
if
(!(console->flags &
CON_ENABLED))
return;
acquire_console_sem();
if ((console->flags
& CON_CONSDEV) || console_drivers == NULL)
{
console->next =
console_drivers;
console_drivers =
console;
} else
{
console->next =
console_drivers->next;
console_drivers->next = console;
}
if (console->flags
& CON_PRINTBUFFER) {
spin_lock_irqsave(&logbuf_lock,
flags);
con_start =
log_start;
spin_unlock_irqrestore(&logbuf_lock,
flags);
}
release_console_sem();
}
加载中,请稍候......