Chinaunix首页 | 论坛 | 博客
  • 博客访问: 55619
  • 博文数量: 19
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2015-02-04 10:43
文章分类
文章存档

2015年(19)

我的朋友

分类: LINUX

2015-04-28 15:34:33

原文地址:深入分析printk函数 作者:枫露清愁

一、概述
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传进来的参数决定。


阅读(2303) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~