1、基本原理
(1)在UBOOT里设置console=ttySAC0或者console=tty1
这里是设置控制终端,tySAC0 表示串口, tty1 表示lcd
(2)内核用printk打印
内核就会根据命令行参数来找到对应的硬件操作函数,并将信息通过对应的硬件终端打印出来!
 
2、printk的使用
(1)printk函数的信息如何才能在终端显示出来
在内核代码include/linux/kernel.h中,定义了控制台的级别:
extern int console_printk[];
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
 
我们在到kernel/printk.c里找到console_printk的定义:
 
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
 
/* We show everything that is MORE important than this.. */
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
 
DECLARE_WAIT_QUEUE_HEAD(log_wait);
 
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 */
};
 
于是我们知道控制台的级别是:
7   4     1     7
我们当然可以再这里修改,但是还有一个更简单的修改方法,即在用户空间使用下面的命令:
 
echo “1 4 1 7” > /proc/sys/kernel/printk
 
将1 4 1 7写入 /proc/sys/kernel/printk即可!
 
当我们使用printk函数时往往要加上信息级别,比如:
 
printk(KERN_WARNING"there is a warning here!\n")
 
其中KERN_WARNING就表示信息的级别,相关宏在函数include/linux/kernel.h中:
 
#define KERN_EMERG      "<0>"   /* system is unusable                   */
#define KERN_ALERT      "<1>"   /* action must be taken immediately     */
#define KERN_CRIT       "<2>"   /* critical conditions                  */
#define KERN_ERR        "<3>"   /* error conditions                     */
#define KERN_WARNING    "<4>"   /* warning conditions                   */
#define KERN_NOTICE     "<5>"   /* normal but significant condition     */
#define KERN_INFO       "<6>"   /* informational                        */
#define KERN_DEBUG      "<7>"   /* debug-level messages                 */
 
如果没有指明信息级别的话,就会采用默认的信息级别,这个默认的信息级别我们在上面见到过的,就是:
#define default_message_loglevel (console_printk[1])
没有改动的情况下是4
 
上面我们说到了信息级别和控制台级别,下面我们要说到重点了!当信息级别的数值小于控制台的级别时,printk要打印的信息才会在终端打印出来,否则不会显示在终端!
 
(2)串口控制台
printk
        vprintk(fmt, args);
                 vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
                          vsnprintf(buf,size,fmt,args);//先把输出信息输入到临时buffer
                          //把临时buffer里面的数据稍作处理,写入log_buffer
                          //可以将信息级别与信息合并
                          //在用户空间使用命令dmesg可以把log_buffer里面的数据打印出来
                          release_console_sem();
                                  call_console_drivers(_con_start, _log_end);
                                          _call_console_drivers(start_print, cur_index, msg_level);
                                                   __call_console_drivers(start, end);
                                                            con->write(con, &LOG_BUF(start), end - start);//调用具体的输出函数
 
这个输出函数要把数据从串口输出的话,肯定要调用到串口硬件相关的函数
我们到文件:drivers/serial/s3c2410.c里面去这里有个串口初始化函数:
 
s3c24xx_serial_initconsole
       register_console(&s3c24xx_serial_console);
 
我们来看看它的注册函数:
static struct console s3c24xx_serial_console =
{
.name = S3C24XX_SERIAL_NAME,
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.index = -1,
.write = s3c24xx_serial_console_write,
.setup = s3c24xx_serial_console_setup
};
里面的确有个write函数!
 
但是我们还不知道printk选择的控制台为什么是串口呢?
 
我们知道uboot传入了参数:console=ttySAC0 或者 console=tty1
 
内核就通过如下的函数来处理传入的参数:
__setup("console=", console_setup);
这是个宏 ,它的作用就是用函数console_setup来处理我们传入的参数
console_ setup
         //先解码字符串为:name, idx, options,然后就使用这些:name, idx, options
        add_preferred_console(name, idx, options);
                  strcmp(console_cmdline[i].name, name)
                  console_cmdline[i].index == idx   //将索引和名字都记录在了console_cmdline数组中了
 
我们记住这个数组,回过头来再来看:
 register_console(&s3c24xx_serial_console);
         if (strcmp(console_cmdline[i].name, console->name) != 0)
                 continue;
我们看到在注册串口控制台的时候,会将串口控制台的名字与uboot传进来的参数相比较,一旦匹配才会注册。这样的话,只有与uboot传进来的控制台参数相一致的控制台才能注册成功。那么也就是说,printk会通过uboot设置的控制台的write函数,将信息打印出来!
 
另外还有一点需要牢记的就是,printk输出的信息会先保存在缓冲区log_buf中,所以我们当然可以通过查看log_buf来看输出信息了!而这个查看命令就是:dmesg
实际上,dmesg这个命令的作用就是去读/proc/kmsg这个文件。也就是说log_buf里面的内容是存放在/proc/kmsg这个文件里面的!


(3)使用printk
方法一:
我们可以再内核中使用如下打印语句:
#define DEG_PRINTK printk
//#define DEG_PRINTK(x...)
 
DEG_PRINTK("%s %s %d\n",_FILE_,_FUNCTION_,_LINE_);
这行打印语句的意思就是讲本行代码所在的文件的名(包括路径)、所在的函数、所在的行打印出来!
当我们需要调试的时候,就使用#define DEG_PRINTK printk这个宏,当不需要调试的时候,就使用#define DEG_PRINTK(x...)这个宏。其中#define DEG_PRINTK(x...)里面的"..."的意思是DEG_PRINTK的参数是可变的!
当代码比较少的时候,我们可以在每一行都加上这个打印语句,这样很容易就会发现错误的位置!
当代码比较多的时候,我们可以采用对半查找的方法!先在代码中间加上打印语句!然后判断出错位置在打印语句之前还是之后,如果出现在之前,就在之前的代码代码里面再次采用对半查找!
 
方法二:
上面我们说到过,要打印的信息会存放在log_buf中,通过文件/proc/kmsg可以来访问这个buf,然后将信息打印出来。由此我们就想了,我们是否可以构造这样一个mylog_buf,里面存放我们所需要的打印信息,通过一个/proc/kmsg文件可以访问该buf,然后打印出来?答案是肯定的!下面我们就一步步来完成!

驱动程序调试方法之printk——printk的原理与直接使用的更多相关文章

  1. 驱动程序调试方法之printk——自制proc文件(二)

    上一节的程序很振奋人心,我们自己实现了一个myprintk打印函数.但是这个函数存在一个致命的缺陷,那就是只能使用一次cat /proc/mymsg命令来读取mylog_buf的值.这是因为读到最后会 ...

  2. 驱动程序调试方法之printk——自制proc文件(一)

    首先我们需要弄清楚proc机制,来看看fs/proc/proc_misc.c这个文件,从入口函数开始看: proc_misc_init(void)         #ifdef CONFIG_PRIN ...

  3. linux设备驱动程序第四部分:从如何定位oops对代码的调试方法,驱动线

    在一个我们谈到了如何编写一个简单的字符设备驱动程序,我们不是神,编写肯定会失败的代码,在这个过程中,我们需要继续写代码调试.在普通c应用.我们经常使用printf输出信息.或者使用gdb要调试程序,然 ...

  4. Linux内核调试方法总结【转】

    转自:http://my.oschina.net/fgq611/blog/113249 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核 ...

  5. 【转】Linux内核调试方法总结

    目录[-] 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2  dump_sta ...

  6. Linux内核调试方法总结

    Linux内核调试方法总结 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2   ...

  7. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  8. linux设备驱动第四篇:驱动调试方法

    http://www.cnblogs.com/donghuizaixian/archive/2015/04/02/4387083.html 上一篇我们大概聊了如何写一个简单的字符设备驱动,我们不是神, ...

  9. linux设备驱动第四篇:从如何定位oops的代码行谈驱动调试方法

    上一篇我们大概聊了如何写一个简单的字符设备驱动,我们不是神,写代码肯定会出现问题,我们需要在编写代码的过程中不断调试.在普通的c应用程序中,我们经常使用printf来输出信息,或者使用gdb来调试程序 ...

随机推荐

  1. Vue框架学习笔记

    <div id="app"> </div> var app = new Vue({ el:"#app", // 绑定的元素 data:{ ...

  2. pycharm 配置autopep8(亲测可行)

    autopep8是一个可以将Python代码自动排版为PEP8风格第三方包,使用它可以轻松地排版出格式优美整齐的代码.网络上有很多介绍如何在pycharm中配置autopep8的方案,但很多方案中还是 ...

  3. APK文件浅析-Android

    2011~2015,5年时间,断断续续学习了Android.  最近打算在2011年2个月认真学习的基础上,深入学习下.  由于有之前的Android基础,加上N年的Java等变成经验,自我感觉And ...

  4. 【例题 8-2 UVA-1605】Building for UN

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 两层 然后n*n就够了 第一层类似 aaa.. bbb.. ccc.. ... 第二次则变成 abc.... abc.... abc ...

  5. js 数组操作大集合

    js数组的操作 用 js有非常久了,但都没有深究过js的数组形式.偶尔用用也就是简单的string.split(char).这段时间做的一个项目.用到数组的地方非常多.自以为js高手的自己竟然无从下手 ...

  6. Docker---(4)Docker 部署spring web项目

    原文:Docker---(4)Docker 部署spring web项目 版权声明:欢迎转载,请标明出处,如有问题,欢迎指正!谢谢!微信:w1186355422 https://blog.csdn.n ...

  7. VC6.0调试知识大全

    VC6.0调试知识大全 分类: C++ 2010-09-06 21:33 7080人阅读 评论(5) 收藏 举报 debuggingmfcfunctionmenumicrosoftdll My Not ...

  8. Hadoop ecosystem 生态圈

    Cascading: hadoop上面的workflow Sqoop(发音:skup)是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql.postgresql...)间进行 ...

  9. [D3] Reuse Transitions in D3 v4

    D3 transitions start executing as soon as they’re created, and they’re destroyed once they end. This ...

  10. JS学习笔记 - 透明度运动框

    该练习的笔记如下: 1.  var cur=0;  //先声明一个变量. 2.  parseInt会舍掉小数,而opacity值恰恰是小数,所以对于opacity,必须用parseFloat. cur ...