📄 4.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://bbs.tsinghua.edu.cn"><font face="黑体"><big><big>水木清华★</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> 内核源代码分析 (BM: suzhe) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="index.htm">上一层</a>][<a href="5.htm">下一篇</a>]
<hr><p align="left"><small>发信人: axp33a (无聊中...), 信区: Linux <br>
标 题: Linux内核源代码分析2-2-1 <br>
发信站: BBS 水木清华站 (Thu Aug 3 11:20:29 2000) WWW-POST <br>
<br>
2.2 代码样例 <br>
了解Linux代码风格最好的方法就是实际研究一下它的部分代码。即使你不完全理解本节 <br>
所讨论代码的细节也无关紧要,毕竟本节的主要目的不是理解代码,一些读者可以只对本 <br>
节进行浏览。本节的主要目的是让读者对Linux代码进行初步了解,为今后的工作提供必 <br>
要基础。该讨论将涉及部分广泛使用的内核代码。 <br>
2.2.1 printk <br>
printk(25836行)是内核内部消息日志记录函数。在出现诸如内核检测到其数据结构出 <br>
现不一致的事件时,内核会使用printk把相关信息打印到系统控制台上。对于printk的调 <br>
用一般分为如下几类: <br>
* 紧急事件(emergency)—例如,panic函数(25563行)多次使用了printk。当内核检 <br>
测到发生不可恢复的内部错误时就会调用panic函数,然后尽其所能地安全关闭计算机。 <br>
这个函数中调用printk以提示用户系统将要关闭。 <br>
* 调试—从3816行开始的#ifdef块使用printk来打印SMP逻辑单元(box)中每一个处理器 <br>
的相关配置信息,但是此过程只有在使用SMP_DEBUG标志编译代码的情况下才能够被执行 <br>
。 <br>
* 普通信息—例如,当机器启动时,内核必须估计系统速度以确保设备驱动程序能够忙等 <br>
待(busy-wait)一个精确的极短周期。计算这种估计值的函数名为calibrate_delay( <br>
19654行),它既在19661行使用printk声明马上开始计算,又在19693行报告计算结果。 <br>
另外,在第4章将详细的介绍calibrate_delay函数。 <br>
如果你已经浏览过这些参照行,你可能已经注意到printk和printf的参数十分类似:一个 <br>
格式化字符串,后跟零个或者多个参数加入字符串中。格式化字符串可能是以一组“<N> <br>
”开始,这里的N是从0到7的数字,包括0和7在内。数字区分了消息的日志等级(log <br>
level),只有当日志等级高于当前控制台定义的日志等级(console_loglevel,25650行 <br>
)时,才会打印消息。root可以通过适当减小控制台的日志等级来过滤不是很紧急的消息 <br>
。如果内核在格式化字符串中检测不到日志等级序列,那么就会一直打印消息(实际上, <br>
日志等级序列并不一定要在格式化字符串中出现,可以在格式化文本中查找到它的代码) <br>
。 <br>
从14946行开始的#define块说明了这些特殊序列,这些定义可以帮助调用者正确区分对 <br>
printk的调用。简单地说,我称日志等级0到4为“紧急事件”,等级5到等级6为“普通信 <br>
息”,等级7自然就是我所说的“调试”(这种分类方法并不意味着其他更好的分类方法 <br>
没有用处,而只是目前我们还不关心它而已)。 <br>
在上面讨论的基础上,我们研究一下代码本身。 <br>
printk <br>
25836:参数fmt是printf类型的格式化字符串。如果你对“...”部分的内容不熟悉,那 <br>
就 需要参阅一本好的C语言参考书(在其索引中查找“变参函数, <br>
variadic function”)。另外,在安装的GNU/Linux中的stdarg帮助里也包含了一个有关 <br>
变参函数的简明描述,在这儿只需要敲入“man stdarg”就可以看到。 <br>
简单地说,“...”部分提示编译器fmt后面可能紧跟着数量不定的任何类型的参数。由于 <br>
这些参数在编译的时候还没有类型和名字,内核使用由三个宏va_start、va_arg和va_end <br>
组成的特殊组及一个特殊类型—va_list对它们进行处理。 <br>
25842:msg_level记录了当前消息的日志等级。它是静态的,这看起来可能会有些奇怪— <br>
为什么下一次对printk的调用需要记录日志等级呢?问题的答案是只有打印出新行(\n) <br>
或者赋给一个新的日志等级序列以后,当前消息才会结束。这样,通过在包含消息结束的 <br>
新行里调用printk,就保证了在多个短期冲突的情况下,调用者只打印唯一一个长消息。 <br>
<br>
25845:在SMP逻辑单元中,内核可能试图从不同的CPU向控制台同时打印信息(有时在单 <br>
处理机(UP)逻辑单元中也会发生同样问题,但由于中断还未被覆盖掉,所以问题也并不 <br>
十分明显)。如果不进行任何协同的话,结果就将处于完全无法让人了解的杂乱无章的状 <br>
态,每个消息的各个部分都和其他消息的各个部分混杂交织在一起。 <br>
相反,内核使用旋转锁(spin-lock)来控制对控制台的访问。旋转锁将在第10章进行深 <br>
入介绍。 <br>
如果你对flags 在传送给spin_lock_irqsave之前为什么不对它初始化感到疑惑,请不要 <br>
担心:spin_lock_irqsave(对于不同的版本请分别参看12614行,12637行,12716行和 <br>
12837行)是一个宏,而不是一个函数。该宏实际上是将值写入flags中,而不是从flags <br>
中读出值(在25895行中,存储在flags中的信息被spin_unlock_irqrestore回读,请参看 <br>
12616行,12639行,12728行和12841行)。 <br>
25846:初始化变量args,该变量代表printk参数中的“...”部分。 <br>
25848:调用内核自身的vsprintf(为节省空间而省略)实现。该函数的功能与标准 <br>
vsprintf函数非常相似,向buf中写入格式化文本(25634行)并返回写入字符串的长度( <br>
长度不包括最后一位终止字符0字节)。很快,你将可以看到为什么这种机制会忽略buf的 <br>
前三个字符。 <br>
(正如25847行的注释中所述)我们应该注意到在这里并没有采取严格的措施来保证缓冲 <br>
器不会过载。这里系统假定1024个字符长度的buf已经足够使用(参阅25634行)。如果内 <br>
核在这里能够使用vsnprintf函数的话,情况就会好许多。然而,vsnprintf还有另外一个 <br>
参数限制了它能够写入缓冲器的字符长度。 <br>
25849:计算buf中最近使用的元素,调用va_end终止对“...”参数的处理。 <br>
25851:开始格式化消息的循环。其中存在一个内部循环能够处理更多内容(这一点随后 <br>
就能看到),因此,每次内循环开始,都开始一个新的打印行。由于通常情况下printk只 <br>
用于打印单行,所以在每次调用中,这种循环通常只执行一次。 <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -