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

📄 ch04s02.html

📁 介绍Linux内核驱动编程的一本书 最主要的是有源代码,都是可用的 学习操作系统很好
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<div class="titlepage"><div><div><h3 class="title"><a name="TurningtheMessagesOnandOff"></a>4.2.4.&#160;打开和关闭消息</h3></div></div></div><p>在驱动开发的早期, printk 非常有助于调试和测试新代码. 当你正式发行驱动时, 换句话说, 你应当去掉, 或者至少关闭, 这些打印语句. 不幸的是, 你很可能会发现, 就在你认为你不再需要这些消息并去掉它们时, 你要在驱动中实现一个新特性(或者有人发现了一个 bug), 你想要至少再打开一个消息. 有几个方法来解决这 2 个问题, 全局性地打开或关闭你地调试消息和打开或关闭单个消息.</p><p>这里我们展示一种编码 printk 调用的方法, 你可以单独或全局地打开或关闭它们; 这个技术依靠定义一个宏, 在你想使用它时就转变成一个 printk (或者 printf)调用.</p><div class="itemizedlist"><ul type="disc"><li><p>每个 printk 语句可以打开或关闭, 通过去除或添加单个字符到宏定义的名子.</p></li><li><p>所有消息可以马上关闭, 通过在编译前改变 CFLAGS 变量的值.</p></li><li><p>同一个 print 语句可以在内核代码和用户级代码中使用, 因此对于格外的消息, 驱动和测试程序能以同样的方式被管理.</p></li></ul></div><p>下面的代码片断实现了这些特性, 直接来自头文件 scull.h:</p><pre class="programlisting">#undef PDEBUG /* undef it, just in case */#ifdef SCULL_DEBUG# ifdef __KERNEL__/* This one if debugging is on, and kernel space */# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)# else/* This one for user space */# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)# endif#else# define PDEBUG(fmt, args...) /* not debugging: nothing */#endif#undef PDEBUGG #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */</pre><p>符号 PDEBUG 定义和去定义, 取决于 SCULL_DEBUG 是否定义, 和以何种方式显示消息适合代码运行的环境: 当它在内核中就使用内核调用 printk, 在用户空间运行就使用 libc 调用 fprintf 到标准错误输出. PDEBUGG 符号, 换句话说, 什么不作; 他可用来轻易地"注释" print 语句, 而不用完全去掉它们.</p><p>为进一步简化过程, 添加下面的行到你的 makfile 里:</p><pre class="screen"># Comment/uncomment the following line to disable/enable debuggingDEBUG = y# Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlineselse DEBFLAGS = -O2endifCFLAGS += $(DEBFLAGS) </pre><p>本节中出现的宏定义依赖 gcc 对 ANSI C 预处理器的扩展, 支持带可变个数参数的宏定义. 这个 gcc 依赖不应该是个问题, 因为无论如何内核固有的非常依赖于 gcc 特性. 另外, makefile 依赖 GNU 版本的 make; 再一次, 内核也依赖 GNU make, 所以这个依赖不是问题.</p><p>如果你熟悉 C 预处理器, 你可以扩展给定的定义来实现一个"调试级别"的概念, 定义不同的级别, 安排一个整数(或者位掩码)值给每个级别, 以便决定它应当多么详细.</p><p>但是每个驱动有它自己的特性和监视需求. 好的编程技巧是在灵活性和效率之间选择最好的平衡, 我们无法告诉你什么是最好的. 记住, 预处理器条件(连同代码中的常数表达式)在编译时执行, 因此你必须重新编译来打开或改变消息. 一个可能的选择是使用 C 条件句, 它在运行时执行, 因而, 能允许你在出现执行时打开或改变消息机制. 这是一个好的特性, 但是它在每次代码执行时需要额外的处理, 这样即便消息给关闭了也会影响效率. 有时这个效率损失无法接受.</p><p>本节出现的宏定义已经证明在多种情况下是有用的, 唯一的缺点是要求在任何对它的消息改变后重新编译.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RateLimiting"></a>4.2.5.&#160;速率限制</h3></div></div></div><p>如果你不小心, 你会发现自己用 printk 产生了上千条消息, 压倒了控制台并且, 可能地, 使系统日志文件溢出. 当使用一个慢速控制台设备(例如, 一个串口), 过量的消息速率也能拖慢系统或者只是使它不反应了. 非常难于着手于系统出错的地方, 当控制台不停地输出数据. 因此, 你应当非常注意你打印什么, 特别在驱动的产品版本以及特别在初始化完成后. 通常, 产品代码在正常操作时不应当打印任何东西; 打印的输出应当是指示需要注意的异常情况.</p><p>另一方面, 你可能想发出一个日志消息, 如果你驱动的设备停止工作. 但是你应当小心不要做过了头. 一个面对失败永远继续的傻瓜进程能产生每秒上千次的尝试; 如果你的驱动每次都打印"my device is broken", 它可能产生大量的输出, 如果控制台设备慢就有可能霸占 CPU -- 没有中断用来驱动控制台, 就算是一个串口或者一个行打印机.</p><p>在很多情况下, 最好的做法是设置一个标志说, "我已经抱怨过这个了", 并不打印任何后来的消息只要这个标志设置着. 然而, 有几个理由偶尔发出一个"设备还是坏的"的提示. 内核已经提供了一个函数帮助这个情况:</p><pre class="programlisting">int printk_ratelimit(void); </pre><p>这个函数应当在你认为打印一个可能会常常重复的消息之前调用. 如果这个函数返回非零值, 继续打印你的消息, 否则跳过它. 这样, 典型的调用如这样:</p><pre class="programlisting">if (printk_ratelimit())    printk(KERN_NOTICE "The printer is still on fire\n");</pre><p>printk_ratelimit 通过跟踪多少消息发向控制台而工作. 当输出级别超过一个限度, printk_ratelimit 开始返回 0 并使消息被扔掉.</p><p>printk_ratelimit 的行为可以通过修改 /proc/sys/kern/printk_ratelimit( 在重新使能消息前等待的秒数 ) 和 /proc/sys/kernel/printk_ratelimit_burst(限速前可接收的消息数)来定制.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="PrintingDeviceNumbers"></a>4.2.6.&#160;打印设备编号</h3></div></div></div><p>偶尔地, 当从一个驱动打印消息, 你会想打印与感兴趣的硬件相关联的设备号. 打印主次编号不是特别难, 但是, 为一致性考虑, 内核提供了一些实用的宏定义( 在 &lt;linux/kdev_t.h&gt; 中定义)用于这个目的:</p><pre class="programlisting">int print_dev_t(char *buffer, dev_t dev); char *format_dev_t(char *buffer, dev_t dev);</pre><p>两个宏定义都将设备号编码进给定的缓冲区; 唯一的区别是 print_dev_t 返回打印的字符数, 而 format_dev_t 返回缓存区; 因此, 它可以直接用作 printk 调用的参数, 但是必须记住 printk 只有提供一个结尾的新行才会刷行. 缓冲区应当足够大以存放一个设备号; 如果 64 位编号在以后的内核发行中明显可能, 这个缓冲区应当可能至少是 20 字节长.</p></div><div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id420973" href="#id420973">13</a>] </sup>* 例如, 使用 setlevel 8; setconsole 10 来配置终端 10 来显示消息.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04.html">上一页</a>&#160;</td><td width="20%" align="center"><a accesskey="u" href="ch04.html">上一级</a></td><td width="40%" align="right">&#160;<a accesskey="n" href="ch04s03.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">第&#160;4&#160;章&#160;调试技术&#160;</td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top">&#160;4.3.&#160;用查询来调试</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>

⌨️ 快捷键说明

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