开发板:tiny4412ADK+S700 4GB Flash
主机:Wind7 64位
虚拟机:Vmware+Ubuntu12_04
u-boot:U-Boot 2010.12
Linux内核版本:linux-3.0.31
Android版本:android-4.1.2
源码:kernel/printk.c
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);
return r;
}
asmlinkage int vprintk(const char *fmt, va_list args)
{
int printed_len = 0;
int current_log_level = default_message_loglevel;
/*
在include/linux/printk.h中:
#define default_message_loglevel (console_printk[1])
在kernel/printk.c中:
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
};
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
在.config中:
#define CONFIG_DEFAULT_MESSAGE_LOGLEVEL 4
*/
unsigned long flags;
int this_cpu;
char *p;
size_t plen;
char special;
/*
由于没有定义宏CONFIG_BOOT_PRINTK_DELAY,所以boot_delay_msec是空函数
*/
boot_delay_msec();
/*
printk_delay这个函数中判断变量printk_delay_msec的值,它的初始值是0
static inline void printk_delay(void)
{
if (unlikely(printk_delay_msec)) {
int m = printk_delay_msec;
while (m--) {
mdelay(1);
touch_nmi_watchdog();
}
}
}
那么在什么地方设置这个值呢?通过分析代码发现,是通过写文件/proc/sys/kernel/printk_delay实现的,可以执行如echo “44” > /proc/sys/kernel/printk_delay 来改变这个变量的值
在文件kernel/sysctl.c中:
static struct ctl_table kern_table[] = {
……
{
.procname = "printk_delay",
.data = &printk_delay_msec,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &ten_thousand,
},
……
}
当向文件/proc/sys/kernel/printk_delay中写入数据,函数proc_dointvec_minmax会调用,然后将这个数字赋值给printk_delay_msec
static struct ctl_table root_table[] = {
{
.procname = "kernel",
.mode = 0555,
.child = kern_table,
},
……
}
内核在启动时会同一处理root_table,然后再/proc下生成相应的文件,方便内核调试
*/
printk_delay();
preempt_disable(); // 关抢占
/* This stops the holder of console_sem just where we want him */
raw_local_irq_save(flags);
this_cpu = smp_processor_id(); //读取当前cpu核的id号
/*
* Ouch, printk recursed into itself! 处理printk中再次调用printk的情况
*/
if (unlikely(printk_cpu == this_cpu)) {
/*
* If a crash is occurring during printk() on this CPU,
* then try to get the crash message out but make sure
* we can't deadlock. Otherwise just return to avoid the
* recursion and return - but flag the recursion so that
* it can be printed at the next appropriate moment:
*/
if (!oops_in_progress) {
recursion_bug = 1;
goto out_restore_irqs;
}
zap_locks();
}
lockdep_off();
spin_lock(&logbuf_lock);
printk_cpu = this_cpu;
if (recursion_bug) {
recursion_bug = 0;
/*
static const char recursion_bug_msg [] =
KERN_CRIT "BUG: recent printk recursion!n";
如果在printk中继续调用printk,上面这句就会不断出现,可以试试
*/
strcpy(printk_buf, recursion_bug_msg); // 缓存入printk_buf,待下面处理
printed_len = strlen(recursion_bug_msg);
}
/* Emit the output into the temporary buffer
先将要打印的字符串缓存入printk_buf中
*/
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
/*
如果配置和宏CONFIG_DEBUG_LL的话,
这个以后分析,printascii是一个用汇编实现的函数,在文件arch/arm/kernel/debug.S中
他会直接将传入的printk_buf参数中的内容打印出来
*/
#ifdef CONFIG_DEBUG_LL
printascii(printk_buf);
#endif
p = printk_buf; // 将临时缓冲区的首地址赋给指针p
/*
Read log level and handle special printk prefix
这个函数会解析printk_buf的前三个字符,因为一般我们在驱动中使用printk的时候会加一个前缀如 printk(KERN_DEBUG “hello worldn”); 其中KERN_DEBUG宏展开后就是 “<7>”
这个函数的目的就是解析这里的 ”<7>”,会把解析出来的数字7赋值给current_log_level
如果是” 上面两种情况下,返回值是3,也就是字符串” */ plen = log_prefix(p, ¤t_log_level, &special); if (plen) { // 如果是类似printk(“hello world”);则plen是0,这个if条件不成立 p += plen; // 跳过printk_buf开头的 “ switch (special) { // 一般不会是 ‘c’ 或者 ‘d’ case 'c': /* Strip plen = 0; break; case 'd': /* Strip plen = 0; default: if (!new_text_line) { // 这个变量初始值是1 emit_log_char('n'); /* #define CONFIG_LOG_BUF_SHIFT 17 在make menuconfig的时候可以修改 #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) static char __log_buf[__LOG_BUF_LEN] __nosavedata; // 定义了128KB的log缓冲区 static int log_buf_len = __LOG_BUF_LEN; // 128K static char *log_buf = __log_buf; #define LOG_BUF_MASK (log_buf_len-1) #define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK]) // 获得log_buf中第idx个字符,这个技巧保证了log_buf中的内容可以以环形缓冲区的形式存放,不至于溢出 static void emit_log_char(char c) // 将c表示的字符存入log_buf中,并调整相关索引值 { LOG_BUF(log_end) = c; // 存入 log_buf[log_end&(128k-1)] log_end++;
相关文章