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

📄 4.htm

📁 Linux内核源代码分析(水木清华
💻 HTM
📖 第 1 页 / 共 2 页
字号:
25853:如果预先不知道消息的日志等级,printk会检查当前行是否以日志等级序列开头 <br>

。 <br>

25860:如果不是,buf中开始未使用的三个字符就能够起作用了(第一次以后的每次循环 <br>

,都会覆盖部分消息文本,但是这样并不会引起问题,因为这里的文本只是前面行中的一 <br>

部分,它们已经被打印过,而且以后也不再需要了)。这样,就可以将日志等级插入buf <br>

中。 <br>

25866:此处有如下属性:p指向日志等级序列(消息文本紧随其后),msg指向消息文本 <br>

—请注意25852行和25865行中对msg的赋值。 <br>

由于已知p用来指示日志等级序列的开头—该日志等级序列可能是由函数自身所创建的, <br>

日志等级可以从p中抽出并存到msg_level中。 <br>

25868:没有检测到新行,清空line_feed标志。 <br>

25869:这是前面谈到过的内循环,循环将运行到本行结束(也就是检测到新行标志)或 <br>

者缓冲器的末尾为止。 <br>

25870:除了将消息打印到控制台之外,printk还能够记录最近打印的长度为LOG_ <br>

BUF_LEN的字符组(LOG_BUF_LEN为16K,请参看25632行)。如果在控制台打开之前,内核 <br>

就已经调用printk,则显然不能在控制台上正确打印消息,但是这些消息将被尽可能地存 <br>



储到log_buf中(25656行)。当控制台打开以后,缓存在log_buf中的数据就可以转储并 <br>

在控制台上打印出来,请参看25988行。 <br>

log_buf是一个循环缓冲器,log_start和log_size变量(25657行和25646行)分别记录当 <br>

前缓冲器的开始位置和长度。本行中的按位与(AND)操作实际上是快速求模(%)运算 <br>

,它的正确性依赖于LOG_BUF_LEN的值是2的幂。 <br>

25872:保存变量跟踪记录循环日志的值。显然,日志大小会不断增长,直至达到 <br>

LOG_BUF_LEN的值为止。此后,log_size将保持不变,而插入新字符将导致log_start的增 <br>

长。 <br>

25878:请注意logged_chars(25658行)记录从机器启动之后由printk写入的所有字符的 <br>

长度,它在每次循环中都会被更新,而不是在循环结束后才改变一次。基于同样的道理, <br>

log_start和log_size的处理方式也是一样。这实际上是一种优化的时机,本书将在结束 <br>

对函数的介绍之后再对它进行详细讨论。 <br>

25879:消息被分为若干行,这当然要使用新行标志符来进行分割。一旦内核检测到新行 <br>

标志符,就写入一个完整行,从而内循环的执行也可以提前终止。 <br>

25884:在这里我们先不考虑内部循环是否会提前退出,从msg到p的字符序列是专门提供 <br>

给控制台使用的(这种字符序列我称之为行,但是不要忘了,这里的行可能并不意味着新 <br>

行终止,因为buf也许还没有终止)。如果该行的日志等级高于系统控制台定义的日志等 <br>

级,而且当前又有控制台可供打印,那么就能够正确打印该行。(记住,printk可能在所 <br>

有控制台打开之前就已经被调用过了。) <br>

如果在该消息块中没有发现日志等级序列,并且在前面的printk调用中也没有对 <br>

msg_level赋值,那么本行中的msg_level就是-1。由于console_loglevel总不小于1(除 <br>

非root通过sysctl接口锁定),于是总是可以打印这些行。 <br>



25886:本行应该能够被打印。printk通过遍历打开的控制台驱动链表告知每一个控制台 <br>

驱动去打印当前行设备驱动在本书的讨论范围之外,因此,控制台驱动代码则并不包含在 <br>

内)。 <br>

25888:请注意这里消息文本的开头使用的是msg而不是p,这样就在没有日志等级序列的 <br>

情况下写入消息了。然而,日志等级序列已经被存储到log_buf缓冲器中了。这样就使后 <br>

来能够访问log_buf以获取消息日志等级的代码(请参看25998行),不会再产生显示混乱 <br>

信息序列的现象。 <br>

25892:如果内层for循环发现一新行,那么buf中的剩余字符(如果有的话)将被认为是 <br>

新的消息,因此msg_level会被重置。但是无论怎样,外层循环都会持续到buf清空为止。 <br>

  <br>

25895:释放在25845行获取的控制台锁(console lock)。 <br>

25896:唤醒等待被写入控制台日志的所有进程。注意即使没有文本被实际写入任何控制 <br>

台,这个过程也仍然会发生。这样处理是正确的,因为无论是否要往控制台中写入文本, <br>

等待进程实际上都是在等待从log_buf中读出信息。在25748行,进程被转入休眠状态以等 <br>

待log_buf的活动。在休眠、唤醒和等待队列中所使用的机制将在下一节中进行讨论。 <br>

25897:返回日志中写入的字符长度。 <br>

如果对于每个字符的处理工作都能减少一点,那么从25869行开始的for循环就执行得更快 <br>

一点。当循环存在时,我们可以通过只在循环退出时将logged_chars更新一次来稍微提高 <br>

运行速度。然而我们还可以通过其他努力来提高速度。由于我们可以预知消息的长度,因 <br>

此log_size和log_start可以到最后再增长。让我们来实验一下这样能否提高速度,下面 <br>

是一段经过理想优化的代码: <br>

请注意循环通常只需要执行一次,只有在log_buf末尾写入信息需要折行时才会多次执行 <br>



。因而log_size和log_buf只需要更新一次(或者当写入需要换行时是两次)。 <br>

这时速度的确提高了,但是有两个原因使我们并不能这样做。首先,内核可能有自己特有 <br>

的memcpy函数,我们必须确保对memcpy的调用不会再次进入对printk的调用(有一部分内 <br>

核移植版定义了自己特有的速度较快的memcpy函数版本,因此所有的移植都要在这一点上 <br>

保持一致)。如果memecpy调用printk来报告失败,那么就有可能触发无限循环。 <br>

然而在这一点上也并不是真的无药可救。使用这种解决方案的最大问题在于该内核循环的 <br>

形式中也要留意新行标志符,因此使用memcpy将整个消息拷贝到log_buf中是不正确的: <br>

如果此处存在新行,我们将无法对其进行处理。 <br>

我们可以试验一个一箭双雕的办法。下面这种替代的尝试虽然可能比前面那种初步解决方 <br>

法速度要慢,但是它保持了内核版本的语意: <br>

(请注意gcc的优化器十分灵敏,它足以能检测到循环内部的表达式log_buf+LOG_BUF_LEN <br>

并没有改变,因此在上面的循环中试图手工加速计算是没有任何效果的。) <br>

不幸的是,这种方法并不能比现在的内核版本在速度上快许多,而且那样会使得代码晦涩 <br>

难懂(如果你编写过更新log_size和log_start的代码,你就能清楚地了解这一点)。你 <br>

可以自己决定这种折衷是否值得。然而无论怎样,我们学到了一些东西,通常,不管成功 <br>

与否,改进内核代码都可以加深你对内核工作原理的理解。 <br>

  <br>

  <br>

  <br>

-- <br>

※ 来源:·BBS 水木清华站 smth.org·[FROM: 166.111.196.22] <br>

</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="index.htm">上一层</a>][<a href="5.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>

⌨️ 快捷键说明

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