在这里还是主要分析一下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 {
&

本文详细分析了Linux内核中的printk函数实现,包括logbuf_lock、console_sem等同步机制,以及log_buf缓冲区的使用。printk首先将格式化的输出存入临时缓冲区,然后根据条件决定是否添加日志级别标签。接着,数据被复制到log_buf缓冲区,当控制台可调用时,通过console_sem信号量协调,调用console_drivers将数据发送到相应设备。通过对console_drivers结构体的注册和初始化,确保printk输出能够正确地发送到控制台。
最低0.47元/天 解锁文章

1132

被折叠的 条评论
为什么被折叠?



