2015年(33)
分类: LINUX
2015-03-04 19:29:31
一、概述
printk类似于应用程序的printf,可打印变量的值,在内核出错的时候,通过加打印信息判断出错的
位置。优点是简单易用,缺点是效率低,可以需要加大量打印才能判断,对于不容易重现的内核bug,几乎
无用武之地。
二、printk原理(linux-3.3版本内核)
首先解决一个疑问:系统启动时,接上串口线,在PC打开一个串口终端(如SecuCRT),启动信息会打印在
串口终端,为什么?
在uboot下设置了bootargs console=ttyS2,115200n8,这就是uboot传给内核的变量,当内核接收了“console=”
根据后面给的串口名称就会知道你要用的是哪个串口,跟进内核看一下(全局查找“console=”)
以下只贴出函数间的调用关系,不贴出全部源码
__setup("console=", console_setup); //当匹配了uboot的“console=”字样,调用console_setup函数
console_setup
__add_preferred_console
strlcpy(c->name, name, sizeof(c->name)); //就是把ttyS赋给console_cmdline[i]->name
c->index = idx; //就是把ttyS后面的数字赋给console_cmdline[i]->index,代表第几个串口,这里是2
在内核启动的时候,会调用初始化函数serial8250_console_init,下面来跟踪一下
serial8250_console_init
register_console(&serial8250_console); //serial8250_console.name = "ttyS"
//从这里可以看出,只有当console_cmdline[i].name与newcon->name(即serial8250_console.name)
//相同时,才能注册串口,所以uboot传进来的参数只能是ttyS*(有的是ttySAC*)
if (strcmp(console_cmdline[i].name, newcon->name) != 0)
continue;
newcon->index = console_cmdline[i].index; //这个值是ttyS后面的数字
console_drivers = newcon;
exclusive_console = newcon;
因为我们看到的所有的串口打印最终都是通过printk输出的,所以接下来跟踪一下这个函数
printk
vprintk
console_unlock
call_console_drivers(_con_start, _log_end);
_call_console_drivers(start_print, cur_index, msg_level);
if ((msg_log_level < console_loglevel || ignore_loglevel) &&
console_drivers && start != end) //根据打印级别判断是否打印到串口
__call_console_drivers(start, end);
con->write(con, &LOG_BUF(start), end - start); //con即为上面的serial8250_console
所以,启动内核的时候会根据uboot传递的参数注册相应串口,当我们调用printk进行打印的时候,会根据注册的串口
号来驱动硬件进行打印,最终调用的函数是serial8250_console.write = serial8250_console_write
serial8250_console_write
uart_console_write(&up->port, s, count, serial8250_console_putchar); //看到这个函数的注释:写数据到串口,第一个参数表示哪一个串口
// 上面这个函数真正调用的是
serial8250_console_putchar
serial_out(up, UART_TX, ch);
下面的就不分析了
所以printk最终都会打印在串口上,打印在哪个串口号上,是由uboot传进来的参数决定。