⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch04-调试技术(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 4 页
字号:
(LDD) Ch04-调试技术(转载)
       
      第4章 调试技术
       
       
      对于任何编写内核代码的人来说,最吸引他们注意的问题之一就是如何完成调试。由于
      内核是一个不与某个进程相关的功能集,其代码不能很轻松地放在调试器中执行,而且
      也不能跟踪。
       
      本章介绍你可以用来监视内核代码和跟踪错误的技术。
       
      用打印信息调试
      最一般的调试技术就是监视,就是在应用内部合适的点加上printf调用。当你调试内核
      代码的时候,你可以用printk完成这个任务。
       
      Printk

      Printk
      在前些章中,我们简单假设printk工作起来和printf很类似。现在是介绍一下它们之间
      不同的时候了。
       
      其中一个不同点就是,printk允许你根据它们的严重程度,通过附加不同的“记录级”
      来对消息分类,或赋予消息优先级。你可以用宏来指示记录级。例如,KERN_INFO,我们
      前面已经看到它被加在打印语句的前面,它就是一种可能的消息记录级。记录级宏展开
      为一个字串,在编译时和消息文本拼接在一起;这也就是为什么下面的例子中优先级和
      格式字串间没有逗号。这有两个printk的例子,一个是调试信息,一个是关键信息:
       
      (代码)
       
      在<linux/kernel.h>中定义了8种记录级别串。没有指定优先级的printk语句默认使用DE
      FAULT_MESSAGE_LOGLEVEL优先级,它是一个在kernel/printk.c中定义的整数。默认记录
      级的具体数值在Linux的开发期间曾变化过若干次,所以我建议你最好总是指定一个合适
      的记录级。
       
      根据记录级,内核将消息打印到当前文本控制台上:如果优先级低于console_loglevel
      这个数值的话,该消息就显示在控制台上。如果系统同时运行了klogd和syslogd,无论c
      onsole_loglevel为何值,内核都将消息追加到/var/log/messages中。
       
      变量console_loglevel最初初始化为DEFAULT_CONSOLE_LOGLEVEL,但可以通过sys_syslo
      g系统调用修改。如klogd的手册所示,可以在启动klogd时指定-c开关来修改这个变量。

      g系统调用修改。如klogd的手册所示,可以在启动klogd时指定-c开关来修改这个变量。
      此外,你还可以写个程序来改变控制台记录级。你可以在O’Reilly站点上的源文件中找
      到我写的一个这种功能的程序,miscprogs/setlevel.c。新优先级是通过一个1到8之间
      的整数值指定的。
       
      你也许需要在内核失效后降低记录级(见“调试系统故障”),这是因为失效处理代码
      会将console_loglevel提升到15,之后所有的消息都会出现在控制台上。为看到你的调
      试信息,如果你运行的是内核2.0.x话,你需要提升记录级。内核2.0发行降低了MINIMUM
      _CONSOLE_LOGLEVEL,而旧版本的klogd默认情况下要打印很多控制消息。如果你碰巧使
      用了这个旧版本的守护进程,除非你提升记录级,内核2.0会比你预期的打印出更少的消
      息。这就是为什么hello.c中使用了<1>标记,这样可以保证消息显示在控制台上。
       
      从1.3.43一来的内核版本通过允许你向指定虚控制台发送消息,藉此提供一个灵活的记
      录策略。默认情况下,“控制台”是当前虚终端。也可以选择不同的虚终端接收消息,
      你只需向所选的虚终端调用ioctl(TIOCLINUX)。如下程序,setconsole,可以用来选择
      哪个虚终端接收内核消息;它必须以超级用户身份运行。如果你对ioctl还不有把握,你
      可以跳过这至下一节,等到读完第5章“字符设备驱动程序的扩展操作”的“ioctl”一
      节后,再回到这里读这段代码。
       
      (代码)
       
      setconsole使用了用于Linux专用功能的特殊的ioctl命令TIOCLINUX。为了使用TIOCLINU
      X,你要传递给它一个指向字节数组的指针。数组的第一个字节是所请求的子命令的编码

      X,你要传递给它一个指向字节数组的指针。数组的第一个字节是所请求的子命令的编码
      ,随后的字节依命令而不同。在setconsole中使用了子命令11,后一个字节(存放在byt
      es[1]中)标别虚拟控制台。TIOCLINUX的完成介绍可以在内核源码drivers/char/tty_io
      ..c中找到。
       
      消息是如何记录的
      printk函数将消息写到一个长度为LOG_BUF_LEN个字节的循环缓冲区中。然后唤醒任何等
      待消息的进程,即那些在调用syslog系统调用或读取/proc/kmesg过程中睡眠的进程。这
      两个访问记录引擎的接口是等价的。不过/proc/kmesg文件更象一个FIFO文件,从中读取
      数据更容易些。一跳简单的cat命令就可以读取消息。
       
      如果循环缓冲区填满了,printk就绕到缓冲区的开始处填写新数据,覆盖旧数据。于是
      记录进程就丢失了最旧的数据。这个问题与利用循环缓冲区所获得的好处相比可以忽略
      不计。例如,循环缓冲区可以使系统在没有记录进程的情况下照样运行,同时又不浪费
      内存。Linux处理消息的方法的另一个特点是,可以在任何地方调用printk,甚至在中断
      处理函数里也可以调用,而且对数据量的大小没有限制。这个方法的唯一缺点就是可能
      丢失某些数据。
       
      如果klogd正在运行,它读取内核消息并将它们分派到syslogd,它随后检查/etc/syslog
      ..conf找到处理这些数据的方式。syslogd根据一个“设施”和“优先级”切分消息;可
      以使用的值定义在<sys/syslog.h>中。内核消息根据相应printk中指定的优先级记录到L
      OG_KERN设施中。如果klogd没有运行,数据将保存在循环缓冲区中直到有进程来读取数
      据或数据溢出。

      据或数据溢出。
       
      如果你不希望因监视你的驱动程序的消息而把你的系统记录搞乱,你给klogd指定-f(文
      件)选项或修改/etc/syslog.conf将记录写到另一个文件中。另一种方法是一种强硬方
      法:杀掉klogd,将消息打印到不用的虚终端上*,或者在一个不用的xterm上执行cat
      /proc/kmesg显示消息。
       
      使用预处理方便监视处理
      在驱动程序开发早期,printk可以对调试和测试新代码都非常有帮助。然而当你正式发
      行驱动程序时,你应该去掉,或者至少关闭,这些打印语句。很不幸,你可能很快就发
      现,随着你想不再需要那些消息并去掉它们时,你可能又要加新功能,你又需要这些消
      息了。解决这些问题有几种方法――如何从全局打开和关闭消息以及如何打开和关闭个
      别消息。
       
      下面给出了我处理消息所用的大部分代码,它有如下一些功能:
       
      l        可以通过在宏名字加一个字母或去掉一个字母打开或关闭每一条语句。
       
      l        通过在编译前修改CFLAGS变量,可以一次关闭所有消息。
       
      l        同样的打印语句既可以用在内核态(驱动程序)也可以用在用户态(演示或测
      试程序)。
       

       
      下面这些直接来自scull.h的代码片断实现了这些功能。
       
      (代码)
       
      符合PDEBUG和PDEBUGG依赖于是否定义了SCULL_DEBUG,它们都和printf调用很类似。
       
      为了进一步方便这个过程,在你的Makefile加上如下几行。
       
      (代码)
       
      本节所给出的代码依赖于gcc对ANSI C预编译器的扩展,gcc可以支持带可变数目参数的
      宏。这种对gcc的依赖并不是什么问题,因为内核对gcc特性的依赖更强。此外,Makefil
      e依赖于GNU的gmake;基于同样的道理,这也不是什么问题。
       
      如果你很熟悉C预编译器,你可以将上面的定义扩展为可以支持“调试级”概念的,可以
      为每级赋一个整数(或位图),说明这一级打印多么琐碎的消息。
       
      但是每一个驱动程序都有它自己的功能和监视需求。好的编程技巧会在灵活性和高效之
      间找到一个权衡点,这个我就不能说哪个对你最好了。记住,预编译器条件(还有代码
      中的常量表达式)只到编译时运行,你必须重新编译程序来打开或关闭消息。另一种方
      法就是使用C条件语句,它在运行时运行,因此可以让你在程序执行期间打开或关闭消息
      。这个功能很好,但每次代码执行系统都要进行额外的处理,甚至在消息关闭后仍然会

      。这个功能很好,但每次代码执行系统都要进行额外的处理,甚至在消息关闭后仍然会
      影响性能。有时这种性能损失是无法接受的。
       
      个人观点,尽管上面给出的宏迫使你每次要增加或去掉消息时都要重新编译,重新加载
      模块,但我觉得用这些宏已经很好了。
       
      通过查询调试
      上一节谈到了printk是如何工作的以及如何使用它。但没有谈及它的缺点。
       
      由于syslogd会一直保持刷新它的输出文件,每打印一行都会引起一次磁盘操作,因此过
      量使用printk会严重降低系统性能。至少从syslogd的角度看是这样的。它会将所有的数
      据都一股脑地写到磁盘上,以防在打印消息后系统崩溃;然而,你不想因为调试信息的
      缘故而降低系统性能。这个问题可以通过在/etc/syslogd.conf中记录文件的名字前加一
      个波折号解决,但有时你不想修改你的配置文件。如果不这样,你还可以运行一个非klo
      gd的程序(如前面介绍的cat /proc/kmesg),但这样并不能为正常操作提供一个合适的
      环境。
       
      与这相比,最好的方法就是在你需要信息的时候,通过查询系统获得相关信息,而不是
      持续不断地产生数据。事实上,每一个Unix系统都提供了很多工具用来获得系统信息:p
      s,netstat,vmstat等等。
       
      有许多技术适合与驱动程序开发人员查询系统,简而言之就是,在/proc下创建文件和使
      用ioctl驱动程序方法。

      用ioctl驱动程序方法。
       
      使用/proc文件系统
      Linux中的/proc文件系统与任何设备都没有关系――/proc中的文件都在被读取时有核心
      创建的。这些文件都是普通的文本文件,它们基本上可由普通人理解,也可被工具程序
      理解。例如,对于大多数Linux的ps实现而言,它都通过读取/proc文件系统获得进程表
      信息的。/proc虚拟文件的创意已由若干现代操作系统使用,且非常成功。
       
      /proc的当前实现可以动态创建i节点,允许用户模块为方便信息检索创建如何入口点。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -