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

📄 (ldd) ch04-调试技术(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 4 页
字号:
       
      Control-PrScr(Show_State)
       
      针对系统里的每一个处理器打印一行信息,同时还打印内部进程树。对当前进程进行标
      记。
       
      RightAlt-PrScr(Show_Registers)
       
      由于它可以打印按键时的处理器寄存器内容,它是系统挂起时最重要的一个键了。如果
      有当前内核的系统表的话,查看指令计数器以及它如何随时间变化,对了解代码在何处

      有当前内核的系统表的话,查看指令计数器以及它如何随时间变化,对了解代码在何处
      循环非常有帮助。
       
      如果想将这些函数映射到不同的键上,每一个函数名都可以做为参数传递给loadkeys。
      键盘映射表可以任意修改(这是“策略无关的”)。
       
      如果console_loglevel足够到的话,这些函数打印的消息会出现在控制台上。如果不是
      你运行了一个旧klogd和一个新内核的话,默认记录级应该足够了。如果没有出现消息,
      你可以象以前说的那样提升记录级。“足够高”的具体值与你使用的内核版本有关。对
      于Linux 2.0或更新的版本来说是5。
       
      即便当系统挂起时,消息也会打印到控制台上,确认记录级足够高是非常重要的。消息
      是在产生中断时生成的,因此即便有错的进程不释放CPU也可以运行――当然,除非中断
      被屏蔽了,不过如果发生这种情况既不太可能也非常不幸。
       
      有时系统看起来象是挂起了,但其实不是。例如,如果键盘因某种奇怪的原因被锁住了
      就会发生这种情况。这种假挂起可以通过查看你为探明此种情况而运行的程序输出来判
      断。我有一个程序会不断地更新LED显示器上的时钟,我发现这个对于验证调度器尚在运
      行非常有用。你可以不必依赖外部设备就可以检查调度器,你可以实现一个程序让键盘L
      ED闪烁,或是不断地打开关闭软盘马达,或是不断触动扬声器――不过我个人认为,通
      常的蜂鸣声很烦人,应该尽量避免。看看ioctl命令KDMKTONE。O’Reilly FTP站点上的
      例子程序(misc-progs/heartbeat.c)中有一个是让键盘LED不断闪烁的。
       

       
      如果键盘不接收输入了,最佳的处理手段是从网络登录在系统中,杀掉任何违例的进程
      ,或是重新设置键盘(用kdb_mode -a)。然而,如果你没有网络可用来恢复的话,发现
      系统挂起是由键盘锁死造成的一点儿用也没有。如果情况确实是这样,你应该配置一种
      替代输入设备,至少可以保证正常地重启系统。对于你的计算机来说,关闭系统或重启
      比起所谓的按“大红钮”要更方便一些,至少它可以免去长时间地fsck扫描磁盘。
       
      这种替代输入设备可以是游戏杆或是鼠标。在sunsite.edu.cn上有一个游戏杆重启守护
      进程,gpm-1.10或更新的鼠标服务器可以通过命令行选项支持类似的功能。如果键盘没
      有锁死,但是却误入“原始”模式,你可以看看kdb包中文档介绍的一些小技巧。我建议
      最好在问题出现以前就看看这些文档,否则就太晚了。另一种可能是配置gpm-root菜单
      ,增添一个“reboot”或“reset keyboard”菜单项;gpm-root一个响应控制鼠标事件
      的守护进程,它用来在屏幕上显示菜单和执行所配置的动作。
       
      最好,你会可以按“留意安全键”(SAK),一个用于将系统恢复为可用状态的特殊键。
      由于不是所有的实现都能用,当前Linux版本的默认键盘表中没有为此键特设一项。不过
      你还是可以用loadkeys将你的键盘上的一个键映射为SAK。你应该看看drivers/char目录
      中的SAK实现。代码中的注释解释了为什么这个键在Linux 2.0中不是总能工作,这里我
      就不多说了。
       
      不过,如果你运行版本2.1.9或是更新的版本,你就可以使用非常可靠地留意安全键了。
      此外,2.1.43及后续版本内核还有一个编译选项选择是否打开“SysRq魔法键”;我建议
      你看一看drivers/char/sysrq.c中的代码并使用这项新技术。

      你看一看drivers/char/sysrq.c中的代码并使用这项新技术。
       
      如果你的驱动程序真的将系统挂起了,而且你有不知道在哪插入schedule调用,最佳的
      处理方法就是加一些打印消息,并将它们打印到控制台上(通过修改console_loglevel
      变量值)。在重演挂起过程时,最好将所有的磁盘都以只读方式安装在系统上。如果磁
      盘是只读的或没有安装,就不会存在破坏文件系统或使其进入不一致状态的危险。至少
      你可以避免在复位系统后运行fsck。另一中方法就是使用NFS根计算机来测试模块。在这
      种情况下,由于NFS服务器管理文件系统的一致性,而它又不会受你的驱动程序的影响,
      你可以避免任何的文件系统崩溃。
       
      使用调试器
      最后一种调试模块的方法就是使用调试器来一步步地跟踪代码,查看变量和机器寄存器
      的值。这种方法非常耗时,应该尽可能地避免。不过,某些情况下通过调试器对代码进
      行细粒度的分析是非常有益的。在这里,我们所说的被调试的代码运行在内核空间――
      除非你远程控制内核,否则不可能一步步跟踪内核,这会使很多事情变得更加困难。由
      于远程控制很少用到,我们最后介绍这项技术。所幸的是,在当前版本的内核中可以查
      看和修改变量。
       
      在这一级上熟练地使用调试器需要精通gdb命令,对汇编码有一定了解,并且有能够将源
      码与优化后的汇编码对应起来的能力。
       
      不幸的是,gdb更适合与调试核心而不是模块,调试模块化的代码需要更多的技术。这更
      多的技术就是kdebug包,它利用gdb的“远程调试”接口控制本地内核。我将在介绍普通

      多的技术就是kdebug包,它利用gdb的“远程调试”接口控制本地内核。我将在介绍普通
      调试器后介绍kdebug。
       
      使用gdb
      gdb在探究系统内部行为时非常有用。启动调试器时必须假想内核就是一个应用程序。除
      了指定内核文件名外,你还应该在命令行中提供内存镜象文件的名字。典型的gdb调用如
      下所示:
       
      (代码)
       
      第一个参数是未经压缩的内核可执行文件(在你编译完内核后,这个文件在/usr/src/li
      nux目录中)的名字。只有x86体系结构有zImage文件(有时称为vmlinuz),它是一种解
      决Intel处理器实模式下只有640KB限制的一种技巧;而无论在哪个平台上,vmlinux都是
      你所编译的未经压缩的内核。
       
      gdb命令行的第二个参数是是内存镜象文件的名字。与其他在/proc下的文件类似,/proc
      /kcore也是在被读取时产生的。当read系统调用在/proc文件系统执行时,它映射到一个
      用于数据生成而不是数据读取的函数上;我们已在“使用/proc文件系统”一节中介绍了
      这个功能。系统用kcore来表示按内存镜象文件格式存储的内核“可执行文件”;由于它
      要表示整个内核地址空间,它是一个非常巨大的文件,对应所有的物理内存。利用gdb,
      你可以通过标准gdb命令查看内核标量。例如,p jiffies可以打印从系统启动到当前时
      刻的时钟滴答数。
       

       
      当你从gdb打印数据时,内核还在运行,不同数据项会在不同时刻有不同的数值;然而,
      gdb为了优化对内存镜象文件的访问会将已经读到的数据缓存起来。如果你再次查看jiff
      ies变量,你会得到和以前相同的值。缓存变量值防止额外的磁盘操作对普通内存镜象文
      件来说是对的,但对“动态”内存镜象文件来说就不是很方便了。解决方法是在你想刷
      新gdb缓存的时候执行core-file /proc/kcore命令;调试器将使用新的内存镜象文件并
      废弃旧信息。但是,读新数据时你并不总是需要执行core-file命令;gdb以1KB的尺度读
      取内存镜象文件,仅仅缓存它所引用的若干块。
       
      你不能用普通gdb做的是修改内核数据;由于调试器需要在访问内存镜象前运行被调试程
      序,它是不会去修改内存镜象文件的。当调试内核镜象时,执行run命令会导致在执行若
      干指令后导致段违例。出于这个原因,/proc/kcore都没有实现write方法。
       
      如果你用调试选项(-g)编译了内核,结果产生的vmlinux比没有用-g选项的更适合于gd
      b。不过要注意,用-g选项编译内核需要大量的磁盘空间――支持网络和很少几个设备和
      文件系统的2.0内核在PC上需要11KB。不过不管怎样,你都可以生成zImage文件并用它来
      其他系统:在生成可启动镜象时由于选项-g而加入的调试信息最终都被去掉了。如果我
      有足够的磁盘空间,我会一致打开-g选项的。
       
      在非PC计算机上则有不同的方法。在Alpha上,make boot会在生成可启动镜象前将调试
      信息去掉,所以你最终会获得vmlinux和vmlinux.gz两个文件。gdb可以使用前者,但你
      只能用后者启动。在Sparc上,默认情况下内核(至少是2.0内核)不会被去掉调试信息
      ,所以你需要在将其传递给silo(Sparc的内核加载器)前将调试信息去掉,这样才能启

      ,所以你需要在将其传递给silo(Sparc的内核加载器)前将调试信息去掉,这样才能启
      动。由于尺寸的问题,无论milo(Alpha的内核加载器)还是silo都不能启动未去掉调试
      信息的内核。
       
      当你用-g选项编译内核并且用vmlinux和/proc/kcore一起使用调试器,gdb可以返回很多
      有关内核内部结构的信息。例如,你可以使用类似于这样的命令,p *module_list,p
      *module_list->next和p *chrdevs[4]->fops等显示这些结构的内容。如果你手头有内核
      映射表和源码的话,这些探测命令是非常有用的。
       
      另一个gdb可以在当前内核上执行的有用任务是,通过disassemble命令(它可以缩写)
      或是“检查指令”(x/i)命令反汇编函数。disassemble命令的参数可以是函数名或是
      内存区范围,而x/i则使用一个内存地址做为参数,也可以用符号名。例如,你可以用x/
      20i反汇编20条指令。注意,你不能反汇编一个模块的函数,这是因为调试器处理vmlinu
      x,它并不知道你的模块的信息。如果你试图用模块的地址反汇编代码,gdb很有可能会
      报告“不能访问xxxx处的内存(Cannot access memory at xxxx)”。基于同样的原因
      ,你不查看属于模块的数据项。如果你知道你的变量的地址,你可以从/dev/mem中读出
      它的值,但很难弄明白从系统内存中分解出的数据是什么含义。
       
      如果你需要反汇编模块函数,你最好对用objdump工具处理你的模块文件。很不幸,该工
      具只能对磁盘上的文件进行处理,而不能对运行中的模块进行处理;因此,objdump中给
      出的地址都是未经重定位的地
      --
       

      或是“检查指令”(x/i)命令反汇编函数。disassemble命令的参数可以是函数名或是
      内存区范围,而x/i则使用一个内存地址做为参数,也可以用符号名。例如,你可以用x/
      20i反汇编20条指令。注意,你不能反汇编一个模块的函数,这是因为调试器处理vmlinu
      x,它并不知道你的模块的信息。如果你试图用模块的地址反汇编代码,gdb很有可能会
      报告“不能访问xxxx处的内存(Cannot access memory at xxxx)”。基于同样的原因
      ,你不查看属于模块的数据项。如果你知道你的变量的地址,你可以从/dev/mem中读出
      它的值,但很难弄明白从系统内存中分解出的数据是什么含义。
       
      如果你需要反汇编模块函数,你最好对用objdump工具处理你的模块文件。很不幸,该工
      具只能对磁盘上的文件进行处理,而不能对运行中的模块进行处理;因此,objdump中给
      出的地址都是未经重定位的地
      --
      

⌨️ 快捷键说明

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