1. 须要的只是:css
2. 在跟踪bug的时候,掌握的信息越多越好。linux
1. 内核bug多种多样,产生的缘由有不少:从错误代码(没有把正确的值存放在恰当的位置);到同步时发生的错误(共享变量锁定不当);再到错误的管理硬件(给错误的控制寄存器发送错误的指令)。nginx
2. 从下降全部程序的运行性能到毁坏数据再到使得系统处于死锁状态均可能是bug发做时的症状。git
3. 从隐藏在源代码中的错误到展示在目睹者面前的bug,每每是经历一系列连锁反应的事件才可能触发的。算法
printk()是内核提供的格式化打印函数,除了和C库提供的printf()函数功能相同外还有一些自身特殊的功能。sass
1.健壮性是printk()函数最容易让人们接受的一个特质,在任什么时候候内核的任何地方都能调用它。安全
除非在启动过程的初期就要在终端输出,不然能够认为printk()在什么状况下都能工做。app
2.printk()函数也会有漏洞,解决办法是提供一个变体函数early _ printk(),但这种办法在某些硬件体系结构上没法实现,缺乏可移植性。ide
1.printk()能够指定一个日志级别,内核根据这个级别来判断是否在终端上打印消息。内核把级别比某个特定值低的全部消息显示在终端上。函数
2. KERN_ WARNING和KERN_ DEBUG都是简单的宏定义,加进printk()函数要打印的消息的开头。内核用这个指定的记录等级和当前终端的记录等级console_loglevel来决定是否是向终端上打印。
3.若是没有指定一个等级记录,函数会选用默认的DEFAULT _ MESSAGE _ LOGLEVEL,如今的默认等级为KERN _ WARNING。但默认值未来存在变化性,因此仍是应该指定一个记录等级。
4.内核将最重要的记录等级KERN _ EMERG定为<0>,可有可无的记录等级KERN _ DEBUG定为<7>
5.有两种赋予记录等级的方法:
内核消息都被保存在一个环形队列中,该缓冲区的大小能够在编译时经过设置CONFIG _ LOG _ BUF _ SHIFT进行调整,在单处理器的系统上默认值是16kb,也就是说内核在同一时间只能保存16kb的内核消息,再多的话新消息就会覆盖老消息。读写都是按照环形队列方式操做的。
1.在标准的Linux系统上,用户空间的守护进程klogd从记录缓冲区中获取内核消息,再经过syslogd守护进程将他们保存在系统日志文件中。
2.klogd:既能够从/proc/kmsg文件中,也能够经过syslog()系统调用读取得到的内核信息,默认状况下选择读取/proc方式实现。两种状况klogd都会阻塞,直到有新的内核消息可供读出,被唤醒以后,默认处理是将消息传给syslogd。在启动时,能够经过-c标志来改变终端的记录等级。
3.syslogd:把它接收到的全部消息添加到一个文件中,默认是/var/log/messages,也可经过/etc/syslog.conf配置文件从新指定。
1.oops是内核告知用户有不幸发生的最经常使用的方式。
2.由于内核是整个系统的管理者,不能采起像在用户空间出现运行错误时使用的那些简单手段,由于他很难自我修复,也不能将本身杀死,只能发布oops,过程为:向终端上输出错误消息、输出寄存器中保存的信息、输出可供跟踪的回溯线索。一般发布oops以后,内核会处于一种不稳定状态。
寄存器上下文和回溯线索
回溯线索中的地址须要转化成有意义的符号名称才能够方便使用,须要调用ksymoops命令,还必须提供编译内核时产生的System.map。若是用的是模块,还须要一些模块信息。调用方法:
kysmoop saved_oops.txt
如今的版本中不须要使用sysmoops这个工具,由于可能会发生不少问题,新版本中引入了kallsyms疼,能够经过定义CONFIG _ KALLSYMS配置选项启用。
在编译时,为了方便调试和测试内核代码,内核提供了许多配置选项。这些选项都在内核配置编译器的内核开发菜单中,都依赖于CONFIG_ DEBUG_ KERNEL。
1.一些内调用能够用来方便标记bug,提供断言并输出信息。最经常使用的两个是BUG()和BUG_ON()。当被调用时会引起oops,致使栈的回溯和错误信息的打印。
2.大部分体系把BUG()和BUG_ON()定义成某种非法操做,这样天然会产生须要的oops。能够把这些调用当作断言使用,想要断言某种状况不应发生:
if (bad_thing) BUG();
或使用更好的形式:
BUG_ON(bad_thing);
2.BUILD _ BUG_ ON()与BUG_ON()做用相同,仅在编译时调用。
3.引用panic()能够引起更严重的错误,不但会打印错误信息,还会挂起整个系统。
4.dump_stack()只在终端上打印寄存器上下文和函数的跟踪线索。
1.该功能能够经过定义CONFIG _ MAGIC _ SYSRQ配置选项来启用。SysRq(系统请求)键在大多数键盘上都是标准键。
2.当该功能被启用时,不管内核出于什么状态,均可以经过特殊的组合键和内核进行通讯。
3.除了配置选项之外,还要经过一个sysctl用来标记该特性的开或关,须要启动它时使用以下命令:
echo 1 > /proc/sys/kernel/sysrq
1.可使用标准的GNU调试器对正在运行的内核进行查看。针对内核启动调试器的方法与针对进程的方法大体相同:
gdb vmlinux /proc/kcore
2.可使用gdb的全部命令来获取信息。
打印一个变量的值:
p global_variable
反汇编一个函数:
disassemble function
3. 若是编译内核的时候使用了-g参数(在内核的Makefile文件的CFLAGS变量中加入-g)gdb还能够提供更多的信息。
4.局限性:
不能单步执行内核代码
1.kgdb是一个补丁 ,可让咱们在远端主机上经过串口利用gdb的全部功能对内核进行调试。
这须要两台计算机:第一台运行带有kgdb补丁的内核,第二台经过串行线使用gdb对第一台进行调试。
2.经过kgdb,gdb的全部功能都能使用:
1.通常状况下,只要保留原有的算法而把你的新算法加入到其余位置上,基本就能保证安全。
能够把用户id(UID)做为选择条件来实现这种功能,经过某种选择条件,安排到底执行哪一种算法:
if (current-> uid !=7777) { /* 老算法…… */ } else { /* 新算法…… */ }
2.除了uid为7777的用户之外,其余全部的用户都是用的老算法,能够建立一个UID为7777用户,专门用来测试新算法。
若是代码与进程无关,或者但愿有一个针对全部状况都能使用的机制来控制某个特性,可使用条件变量。这比使用UID更简单,只须要建立一个全局变量做为一个条件选择开关。若是该变量为0,就使用某一个分支上的代码;不然,选择另一个分支。能够经过某种接口提供对这个变量的操控,也能够直接经过调试器进行操控。
1.这种方法经常使用于使用者须要掌握某个特定事件的发生规律的时候。
2.方法是建立统计量,并提供某种机制访问其统计结果。
3.这种实现并不是是SMP安全的,更好的方式是用原子操做。
当系统的调试信息过多的时候,有两种方式能够防止这类问题发生:
限制调试信息,最多几秒打印一次,能够根据本身的须要调节频率。
发生次数限制
这种方法是要调试信息至多输出几回,超过次数限制后就不能再输出。
这种方法能够用来确认在特定状况下某段代码的确被执行了。
注:无论哪一种方法用到的变量须要是静态的、局部的。而且限制在函数的局部范围之内,这样才能保证变量的值在经历屡次函数调用后仍然可以保留下来。不是SMP安全或抢占安全的,更好的方式是用原子操做。
1.Git源码管理工具提供了一个有用的二分搜索机制,若是使用Git来控制Linux源码树的副本,则Git将自动运行二分搜索进程。此外,Git会在修订版本中进行二分搜索,能够具体找到哪次提交的代码引起了bug。
告知git要进行二分搜索:
git bisect start
提供一个出现问题的最先内核版本:
git bisect bad <revision>
当前版本就是引起bug的最第一版本的状况下使用这条命令:
git bisect bad
提供一个最新的可正常运行的内核版本:
git bisect good <revision>
2.这以后,git就会利用二分搜索法在Linux源码树中,自动检测正常的版本内核和有bug的内核版本之间那个版本有隐患,而后再编译、运行以及测试正被检测的版本。
若是这个版本正常:
git bisect good
若是这个版本运行有异常:
git bisect bad
3.对于每个命令,Git将在每个版本的基础上反复二分搜索源码树,而且返回所查的下一个内核版本,直到不能再进行二分搜索位置,最终Git会打印出有问题的版本号。
4,指定Git仅仅在与错误相关的目录列表中去二分搜索提交的补丁: git bisect start - arch/x86