LUPA首页
|
资讯
|
教程
|
下载
|
求职
|
方案
|
博客
|
交易
|
英文版
游客:
注册
|
登录
|
会员
|
统计
|
帮助
LUPA论坛
»
内核问题答疑
» 内核调试(一)
‹‹ 上一主题
|
下一主题 ››
投票
交易
悬赏
活动
打印
|
推荐
|
订阅
|
收藏
|
开通个人空间
|
加入资讯
标题: 内核调试(一)
本帖已经被作者加入个人空间
陈莉君
版主
UID 26540
精华
1
积分 2168
帖子 145
LUPA币 2093 点
阅读权限 100
注册 2006-11-9
#1
发表于 2008-5-11 23:56
资料
个人空间
短消息
内核调试(一)
艰苦的调试工作是内核级的开发区别于用户级开发的一个显著特点。相对于用户级开发,内核开发确实要艰苦得多。更要命的是,内核的一个错误往往马上就能让系统崩溃——一点情面都不会留。
越来越轻车熟路地驾驭内核调试的能力——当然,最终是为了能够成功的开发内核——很大一部分取决于你的经验和对整个操作系统的把握。没错,虽然你玉树临风会对别的事情有帮助,但是调试内核的关键还是在于你对内核的深刻理解。然而我们必须有个可以开始着手的地方,所以,在这里我们从调试内核的一种可能步骤开始。
开始前你需要准备什么?
那么,你确定要开始了吗?这可能会是一个漫长而又困难重重的苦旅。不少
bug
已经让整个开发社区几个月都食不甘味了。幸运的是,虽然确实有许多这样难以对付的
bug
,但也由一些比较简单,而且容易消灭的小
bug
。所以,你现在要面对的是些简单琐碎的小
bug
。但不用怕,当然,除非你开始做一些调查,否则你不会清楚到底面对的是什么。现在,你需要的只是:
¨
一个
bug
。听起来很可笑,但你确实需要一个定义精确的
bug
。如果错误总是能够再现的话,那对我们会很有帮助,而且有一部分错误确实如此。然而不幸的是,大部分
bug
通常并非是行为可靠而又定义清楚的。
¨
藏匿
bug
的内核的版本(一般可以假设是在最新版本的内核里,否则谁又会去理睬它呢?)如果你能搞清楚这个
bug
最早出现在哪个版本中就再理想不过了。如果你不知道,我们待会儿会解释该怎么办。
¨
一点好运气。
如果你没办法让
bug
重现出来,下面要讲的这些步骤就毫无意义。问题的关键在于你是否能够让这些错误重复发生。如果你不能,消灭
bug
就只能通过抽象出问题,再从代码中搜索蛛丝马迹来进行了。虽然有时也得这么做(了解了吧,内核开发者其实也就是这么棒了),但如果你能够让错误重复出现,成功的机会要大许多。
有一个
bug
存在而有人没办法让它重现,这听起来可能感觉挺奇怪。在用户级的程序里,
bug
常常表现得很直截了当——比如,
执行
foo
就会让程序立即产生核心信息转储(
core dump
)
。但内核全然不是这样。内核、用户程序和硬件之间的交互非常微妙。一个竞争条件往往把它狰狞的面目隐藏在某个算法百万次的迭代之中。设计不佳的甚至是包含错误的代码可能在某些系统上表现得相当不错,而在其它系统上却让人无法忍受。在跟踪
bug
的时候,对于一些特定的配置、一些特定的机器,付出额外的努力是司空见惯的,要不然的话它可能压根就不会显露原型。在跟踪
bug
的时候,你掌握的信息越多越好。许多时候,当你可以精确的重现一个
bug
的时候,你就已经成功了一大半了。
内核中的
bug
内核中的
bug
和用户空间应用程序中的
bug
一样多变。它们的产生可以有无数的原因,同时它们的表象也变化多端。从明白无误的错误代码(比如,没有把正确的值存放在正确的位置)到同步时发生的错误(比如,共享变量锁定不当),都是
bug
的温床。从降低所有东西的运行性能到毁坏数据,都可能是
bug
发作时的症状。
从隐藏在源代码中的错误到展现在目击者面前的
bug
,其发作往往是一系列连锁反应的事件才可能触发的。举个例子,一个被共享的结构体,如果它没有引用计数,那么它就有可能会引发竞争条件。因为没有引用计数的话,一个进程可以在另外一个进程仍然需要使用该结构的时候就释放掉它。这样做可能导致引用一个空指针,也可能会读出一些垃圾数据,还可能并不产生什么恶果(如果该数据并没有被其它什么覆盖的话)。引用空指针会导致产生一个
oops
,而垃圾数据可能会导致系统崩溃(这种情形比
oops
还坏)。用户报告了
oops
或系统的错误现象之后,开发者回过头来观察错误情形,发现在释放数据之后还会对它进行读写,存在着一个竞争条件,于是就会进行修正,给这个共享的结构加上适当的引用计数。多说一句,对它的访问大多还需要由锁来保证。
调试内核听起来很难,但事实上
Linux
内核与其它大型的软件项目也没有什么太大的不同。内核确实有一些独特的问题需要考虑,像定时限制和竞争条件等,它们都是允许多个线程在内核中同时运行产生的结果。我相信你有一定的理解能力也能够付出些努力,所以你应该可以调试内核中的存在的问题(说不定你还会喜欢上这种挑战呢)。
pintk()
内核提供的打印函数
printk()
,和
C
库提供的
printf()
函数功能几乎相同。实际上,在整个这本书中我们都没有用到不同的那些部分。从它实现的大部分意图来说,这个名字很不错;
printk()
就是内核的格式化打印函数。但是,差异确实也还是存在的。
printk()
函数的健壮性
健壮性是
printk()
函数最容易让人们接受的一个特质。
任何时候,任何地方
都能调用它,内核中的
printk()
比比皆是。它可以在中断上下文和进程上下中调用;它可以在持有锁时调用;它可以在多处理器上同时调用,而且调用者连锁都不必使用。
它是一个弹性极佳的函数。这一点相当重要,
printk()
之所以这么有用,就在于它随时都在那里而且随时都能用。
printk()
的脆弱之处
printk()
函数的健壮性也有漏洞。在系统启动过程中,终端还没有初始化之前,在某些地方不能使用它。不过说实在的,如果终端没有初始化,你又能输出到什么地方去呢?
这一般不是一个什么问题,除非你要调试的是启动过程最开始的那些步骤(比如说在负责执行硬件体系结构相关的初始化动作的
setup_arch()
函数中)。着手进行这样的调试挑战性很强——没有任何打印函数能用确实让问题更加棘手。
不过还是有一些指望的,虽然不多。核心硬件部分的黑客依靠此时能够工作的硬件设备(比如说一个串口)与外界通信。相信我,绝大部分人对此都不会感兴趣。一些被支持的体系结构确实靠这种方式实现了健全的解决方案,但是——其它系统(包括
i386
)都通过提供可用的补丁来扭转局面。
解决的办法是提供一个
printk()
的变体函数,在启动过程的初期就具有在终端上打印的能力:
early_printk()
。它的功能与
prink()
完全相同,区别仅仅在于名字和它能够更早地工作。不过,由于该函数在一些内核支持的硬件体系结构上无法实现,所以这种这种办法缺少可移植性。可是,如果它能够工作的话,它就是你最好的助手。
除非你在启动过程的初期就要在终端上输出,你可以认为
printk()
什么情况下都能工作。
Loglevels
printk()
和
printf()
一个最主要的区别就是前者可以指定一个
记录级别
。内核根据这个级别来判断是否在终端上打印消息。内核把级别比某个特定值低的所有消息显示在终端上。
可以通过下面这种方式指定一个记录级别:
printk(KERN_WARNING “This is a warning!\n”);
printk(KERN_DEBUG “This is a debug notice!\n”);
printk(“ I did not specify a loglevel!\n”);
KERN_WARING
和
KERN_DEBUG
都是
<linux/kernel.h>
中的简单宏定义。它们扩展开是像“
<4>
”或“
<7>
”这样的字符串,加进
printk()
函数要打印的消息的开头。内核用这个指定的记录等级和当前终端的记录等级
console_loglevel
来决定是不是向终端上打印。表
1
列举了所有可供使用的记录等级。
表
1
可供使用的记录等级
记录等级
描述
KERN_EMERG
一个紧急情况
KERN_ALERT
一个需要立即被注意到的错误
KERN_CRIT
一个临界情况
KERN_ERR
一个错误
KERN_WARNING
一个警告
KERN_NOTICE
一个普通的,不过也有可能需要注意的情况
KERN_INFO
一条非正式的消息
KERN_DEBUG
一条调试信息——一般是冗余信息
如果你没有特别指定一个记录等级,函数会选用默认的
DEFAULT_MESSAGE_LOGLEVEL
,现在默认等级是
KERN_WARNING
。由于这个默认值将来存在变化的可能性,所以还是应该给你自己的消息指定一个记录等级。
内核将最重要的记录等级
KERN_EMERG
定为“
<0>
”,将无关紧要的记录等级“
KERN_DEBUG”
定为“
<7>
”。举例来说,当编译预处理完成之后,前例中的代码实际被编译成如下格式:
printk(“<4> This is a warning!\n”);
printk(“<7>This is a debug notice!\n”);
printk(“<4>I did not specify a loglevel!\n”);
怎样给你调用的
printk()
赋记录等级完全取决于你自己。那些正式的、需要你保持的消息应该有合适的记录等级。但是那些当你试图解决一个问题时加得到处都是的调试信息——必须承认,我们都这么干而且也确实行得通——可以按照你的想法赋给记录等级。一种选择是保持你的终端的默认记录等级不变,给你的所有调试信息
KERN_CRIT
或更低的等级。相反,你也可以给你的所有调试信息
KERN_DEBUG
等级,而调整你终端的默认记录等级。两种方法各有利弊,自己拿主意吧。
BTW:我的毕业设计的学生在调试内核代码时,遇到不少问题,在此把《LDK》中调试一章的内容专门贴出来,给大家参考。
[
本帖最后由 陈莉君 于 2008-5-11 23:59 编辑
]
透析真谛,手中满握春风;共享智慧,如春风沐浴
http://www.kerneltravel.net
[广告]
推荐个超酷的web2.0相册
xdd123www
关注开源
UID 161580
精华 0
积分 42
帖子 3
LUPA币 40 点
阅读权限 20
注册 2008-3-20
#2
发表于 2008-5-13 18:22
资料
短消息
有用!
谢谢老师~
[广告]
推荐个超酷的web2.0相册
投票
交易
悬赏
活动
LUPA论坛
专题指导
> 内核问题答疑
> FreeBSD专版
LUPA论坛
> 开源思想交流
> 人物专栏
> LUPA足迹
> 推进员之家
> 网页标准化
> 投稿区
> 技术交流
> 初级问答[新手区]
> 有奖评书专区
> 社区茶馆
> 美景美图
> 创业就业
> 游戏专版
> IT界评论
> 技术文档
> Linux基础
> 跨平台应用
> ErLang专区
> 软件应用
> LAMP专区
> Shell编程
> JAVA
> 高级应用
> PHP
> 邮件服务器
> 嵌入式开发
> 数据库
> FTP技术
> 网络安全
> Solaris专区
> 其他Unix系列
> windows平台开源软件介绍
> 其他编程语言
> 高校教学认证专版
> 认证公告和教学指导
> 技术支持
> 操作员认证专题
> 网管员认证专题
> LAMP工程师认证专题
> 社区管理
> 社区活动
> LUPA基金会
> 开源社区广告同盟
> 人员调整公告
> 回收垃圾
Linux平台开发专版
> C/C++语言基础
> 开发工具使用
> GTK/QT图形库
> 开发包调用
> 软件包制作
合作专区
> 开源项目合作建设
> X-Vake威客系统
> Serious Game底层引擎
> 蓝迪游戏
> ExtMail
> WiseReal教育软件
> Works.lt信息化平台
> LGsearch桌面搜索
> FireFox插件开发
> LUPA考试系统
> Linuxer电子杂志
> Easyjf专版(简易JAVA框架)
> 恩信ERP
> 希瑞CRM
> Zen Cart购物车
> Klinux 发行版定制
当前时区 GMT+8, 现在时间是 2008-7-24 15:59
浙ICP备06002895号
Powered by
Discuz!
5.0.0
© 2001-2006
Comsenz Inc.
Processed in 0.075776 second(s), 6 queries , Gzip enabled
TOP
清除 Cookies
-
联系我们
-
LUPA开源社区
-
Archiver
-
WAP
控制面板首页
编辑个人资料
积分交易
公众用户组
好友列表
基本概况
流量统计
客户软件
发帖量记录
论坛排行
主题排行
发帖排行
积分排行
在线时间
管理团队
管理统计