二分法与printk()

转自《Linux内核修炼之道

       人生就是一个茶几,上面摆满了杯具。内核也是一个大茶几,不过它上面的杯具是一个个的bug。确定 bug 什么时候被引入是一个很关键的步骤, 在这个定位 bug 的过程中,不论有意或无意,都会很自然地用到二分查找的方法。

二分查找法的基本原理

      对于二分查找法,我们不会也不应该会感到陌生。作为 一种高效的查找算法,它曾出现在我们的数据结构课堂里,出现在一次又一次的面试里,更是会频繁地应用在我们的代码里。在我们所接触到的各种算法里,它可以 说是最为大众化、最充满生活智慧的一个,很多人并不知道二分查找法的概念,却能够在生活中熟练的去应用。

     比如,一个工人要维修一条 10km 长的电话线,首先他 需要定位出故障所在,如果沿着线路一小段一小段地查找,显然非常得困难,每查一个点都要爬一次电线杆,10km 长 的距离会有大约 200 多根电线杆。假设电线两端分别为 A、B,这时他会很自然地首先从中间的C 开始查起,用话机 向两端测试时,发现 AC 段正常,故而断定故障在 BC 段, 再到 BC段的中点 D,如果发现 BD 段正常,则故障在 CD 段,然后再到 CD 的中间点 E 查找,这样每查一次,就可以把待查线路的 长度缩减一半,因而经过 7 次查找,就可以将故障发生的范围缩小到 50~100m 左右, 即在一两根电线杆附近。如此一来要 节省很多的精力与时间。
       这是二分查找法在生活中的一个典型应用,实际上,查 找内核的 bug 与查找电话线的故障相比,本质上都是相同的,并没有高深到哪里去,都是首先要定位出 故障的位置,然后去解决它。

        比如你在使用某个版本的内核时,发现了一个内核 bug, 这时你需要知道它究竟是在应用哪个补丁时被引入的,如果一个一个的去还原那些补丁,每还原一个补丁就要测试一次内核,那么必然会浪费过多的时间,而应用二 分查找法,首先确定一个肯定没有出现该 bug 的内核版本,然后去测试位于这两个版本中间的那个版 本,这样重复筛选,就能够很容易的定位出是从哪个版本开始出现了这个 bug。

printk()
     应该是每一个驱动开发者最为亲密的 伙伴了, 我们常常将它与二分查找法结合在一起寻找代码中发生问题的位置。

      通常情况下,对于代码中的两个 printk()语句, 如果一个正常执行,而另一个没有被执行,就说明问题发生在这两个 printk()之间,接下来就可 以在这个范围内应用二分查找法定位有问题的代码。

1.printk()与 printf()

     用户空间有 printf(), 内核空间有 printk(),它们就如代表善与恶的命运双生子,即使长相功能如何的接近,都不能在 代码中共存。

       对于我们来说,最容易犯的错误是,在需要printk()的地方误用了printf(), 而在 需要printf()的地方却又误用了 printk(), 通常这都不会是因为不知道它们的区别,而只是习惯使然。民间流传有这样的说法:当你在编写用户空间应用程序的时候,下意识写出的都是 printk(),那么就说明你是个标准的内核开发者了。

2.printk()的消息级别

      printk()与 printf()的一个重要区别就是前者可以指定消息的打印级别, 内核根据这个指定的级别来决定是否将消息打印 到终端上。如下表所示,printk()共有 8 个 级别。

级别                                                                                                描述
KERN_EMERG                                                               紧急情况,系统可能会崩溃
KERN_ALERT                                                                            必须立即响应
KERN_CRIT                                                                                  临界情况
KERN_ERR                                                                                   错误信息
KERN_WARNING                                                                        警告信息
KERN_NOTICE                                                              普通的但可能需要注意的信息
KERN_INFO                                                                                提示性信息
                        
KERN_DEBUG                                                                             调试信息

      如果没有指定消息的级别,printk()会使用默认的 DEFAULT_MESSAGE_LOGLEVEL(通 常是 KERN_WARNING) 。

3. 控制台的日志级别(console_loglevel)  
      当 printk 指 定的消息级别小于指定的控制台日志级别时,消息的内容就会显示在该控制台上 。 控 制 台 的 日 志 级 别 定 义 在 include/linux/kernel.h 文  件 中 , 默 认 为DEFAULT_CONSOLE_LOGLEVEL(值等于 7) ,也就是说默认情况下,比 KERN_DEBUG
级别 高的 printk()消息内容都可以在控制台上显示。

     我们可以执行下面的命令使任何级别的 printk()消息都被打印在终端上
$echo8>/proc/sys/kernel/printk

4.printk()的变体
     内核在 include/linux/kernel.h 文 件中提供了两个 printk()的变体 pr_debug 和 pr_info,它们的定义为:
#definepr_debug(fmt,arg...)\
printk(KERN_DEBUGfmt,##arg)
#definepr_info(fmt,arg...)\
printk(KERN_INFOfmt,##arg)

5.printk()不是万能的
     printk()虽然很好用,但它并不是万能的,在 系统启动时,终端还没有初始化之前,它并不能被使用,不过如果不是在调试系统的启动过程的话,这并不能算是个问题。其实内核提供了一个 printk()的变 体 early_printk(),专门用于在系统启动的初期在终端上打印消息,它与 printk()的区别仅仅在于名字的不同以及它能够更早地工作。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值