精华内容
下载资源
问答
  • 十 使用SkyEye构建Linux内核调试环境 1 SkyEye的安装和μcLinux内核编译 2 使用SkyEye调试 3 使用SkyEye调试内核的特点和不足 十一 KDB 1 入门 2 初始化并设置环境变量 3 激活 KDB 4 KDB 命令 5 技巧和诀窍 6 结束语...
  • Linux 内核调试

    2020-01-31 10:05:32
    肺炎疫情憋在家里,总结一下很久之前的学习笔记,先说说系统开发工程都需要的内核调试方面。主要是结合以前的项目调试经验和笔记,欢迎大家补充,还记得以前从0 开始写一个bootloader,把一个linux 系统在三星s3c...

    肺炎疫情憋在家里,总结一下很久之前的学习笔记,先说说系统开发工程都需要的内核调试方面。主要是结合以前的项目调试经验和笔记,欢迎大家补充,还记得以前从0 开始写一个bootloader,把一个linux 系统在三星s3c2410上跑起来,基本就是使用printk 来调试的。

     

    上面这一段就是一个典型的IC 原厂的工程师在最开始把linux移植到芯片上的bootargs,是bootloader 启动时传给kenerl的参数,因为一个芯片从来没有跑过linux,基本从0开始,根据经验,在没有仿真器的情况下,是完全可以把linux跑起来。源码级调试的需求在linux工程上是非常小的,linux主要是靠经验及printk去推测代码路径,只有极少数情况才会用到仿真器,比如硬件有什么故障,总线hang死了,CPU模式切换,内存错了等非常底层问题才可能用到仿真器。当然这些小概率的问题没仿真器是没法调试,因为代码跟不进去,不过这种情况通常是IC原厂的工程师会做的事情。

    1. early printk(early console) 实现及initcall

        在我们820a 平台上,这些文件是非常lowlevel的打印语句(uart driver驱动子系统起来之前),是通过汇编语言实现的非常简单的打印语句,这些语句可以在linux非常早期就可以使用,比如刚把linux 解压完,“uncompress linux”就可以打印出来了。

    Kenel config如下:

    所以在调试linux早期阶段,会利用这种lowlevel的手段调用打印跟踪启动过程,之后linux就会调用到很多初始化init function。 初始化也有不同的level,其中很多也是驱动的初始化,Linux使用一个for循环把所有函数调用一段,他不是手工的去掉,他是把每个初始化函数的指针放在一个特殊的数据段里面,再利用一个for循环取出这个数据段的第一个函数取出来,一直调用到最后一个指针。这个过程可能有几百个initcall,这些initcall有些是linux自带的,有些是我们自己写的,所以这个过程中initclal可能会直接挂掉,需要有一个办法在开机时就打印出来,linux有一个方式是initccall debug,通常我们再bootarges 中传入一个initcall_debug,linux就会把每个初始化函数什么时候调用的,及返回是什么全部打印出来,如果某个calling打印之后就没有打印了,说明这个calling就卡住了。

    printk虽然非常简单,但是作为一个linux工程师不能一上来就直接一个printk,显得非常不专业,专业的linux工程师一般调用printk的两个变体,一个是dev_xxx,pr_xxx, 我们一般在驱动中调用dev_xxx(dev_info, dev_err 等),这个会自动给你打印增加设备的打印前缀。这样就一目了然,不会混乱,比如你是spi,就会打印spi***之类的。有时候我们仅仅增加一个模块(非设备驱动中),这时候我们可以使用pr_XXX(pr_info, pr_err...).这个时候我们可以在模块最前面定义一个pr_fmt, 这个整个模块的pr打印自动会添加签名pr_fmt定义的前缀

     

    还有一种情况在工作中遇到比较多,就是在打印中加上自己的名字,尽量针对需要的打印的特点,功能,模块添加打印信息,加个名字显得很怪,搞不好还要遗臭万年,嘿嘿。

    Linux printk 比我们想象的要好用,printk 这个函数不像printf,因为printk不会引起睡眠,在软中断,中断,spinlock里面等,基本所有的上下文都可以调用。

    当然,调得太多,也会让系统变慢,所以我们有时候需要基于linux本身的理解,防止影响系统,所以一个牛x的工程能够在正确的位置加printk。

    Linux内部函数调用有时候是非常深的,我们有时候直接在最底层函数加一个dump_stack()或者使用warn_on(1) ,内核调到这里就会把调用栈打印出来。

    1. 源码级调试

       工程上通常去淘宝买个jtag 仿真器就可以了,插根网线或者usb就可以gdb 调试了。Arm官方的是DS-5,这个东西功能很强大可以结合eclipse 调试,但是对于我们来说,图形界面再强大,还是基于对linux代码的理解去调试。

    通常可以利用qemu 或者仿真器连接板子,再使用gdb vmlinux 启动,使用target remote ip:port 连接目标板,这样就可以进行源码调试了。

    按下ctr+x 抬起来按下a,就分裂两个窗口,一个源码,一个命令,这样就进行源码级的调试了,

    1. 内核模块源码级调试

       模块调试与kernel 有点不一样,因为模块是insmod之后才知道哪些代码段,数据段在哪里。 所以先进入模块的sections 获取代码段,数据段,再利用add-symbol-file 传递数据、代码段地址,就可以调试了。

    1. 待机调试

    一个真实的故事,一个工程师调手机的待机,一个电路板上有很多个硬件,这个硬件的驱动里面都有一个suspend,之前suspend的时候系统会hang住,这个工程师之前花了一个星期在所有的suspend函数加上printk打印调试,其实这样如此通用的需求,很多公司很多人都有,最后其实只要在bootargs,加入no_console suspend ,以及pm相关的debug打开,这样suspend的时候,linux自动把suspend的信息打开, 从这个例子之后,我明白了一个linux常识,linux是一个如此强大生态,怎么轮到我发明一份新的需求,也不会已如此野蛮,呆板的实现方式,很多牛人早就想到了,并实现了一套牛x的方式。

     

     

    1. OOPS & Panic

        其实oops已经完整告诉了内核出错信息,出错的原因,比如下面例子是访问了一个NULL指针,出错的PC指针位置是globalfifo_read + 0x50/0x200(flobalfifo_read 最开始一条指令偏移0x50的位置,flobalfifo_read 整个代码段加起来是0x200) ,之后会列出完整的backtrace。

     

    这时候就需要反汇编来调试,一般反汇编ko,或者对应的.o 文件就可以了,没必要反汇编整个内核,那样时间就非常久了。

    工程上objdump比用addr2ine 要更全面和有助于调试

    打开反汇编结果,找到出错的globalfifo_read ,再找到偏移 0x394 +0x50 =0x3e4, 不仅找到了出错的c代码,也可以精确到汇编。

     

     

     

     

    Oops 与 Panic不是一回事,他们打印是差不多的,Panic 之后内核一定是挂了,oops 不会崩溃。Oops 一般是在进程上下文,所以内核通常只是把进程挂掉,中断上下文的oops 会导致panic,当然如果我们设置了panic_on_oops 为1 ,就都会panic。工程上通常会把panic_on_oops 上打开,因为不确定oops到底改了哪些东西,后面搞不好会有莫名其妙的错误,使得调试更加复杂。

    1. grabserial

       这个工具会打印两个时间戳,第一个是接收到打印的时间,第二个是与上一个打印的时间差,这样对我们优化开机时间比较有帮助。

     

    1. 内核的debug选项

       内核中有很多的debug选项,默认大部分是不打开的一般在调试某个功能的时候把相关选项打开,比如调试电源管理,需要把no console suspend打开。Linux是一个如此强大的生态,基本上你想到的所有的需求,linux都为你准备好了,不要自己去摸索创造。

    比如在spinlock 或者中断上下文调用了sleep,崩溃的位置不一定在中断或者spinlock里面。所以很难去调试,内核里面有个DEBUG_ATOMIC_SLEEP 打开,内核中spinlock中只要调用了sleep相关的函数(mutex malloc),内核就会打印出来,很多错误都可以去google 查找到相关的问题。通常我们费尽脑汁想的事情,别人早就想过了。

      

    对于内存的践踏,或者多次free,或者free之后访问等问题。

    举一个kmalloc 内存泄漏的例子。曾经有个研究所有一个内存泄漏的问题,他们的平台是芯片厂商直接提供的BSP,他们在上面跑一个qt程序,发现跑个把月之后内存就耗尽了,一直查,各种怀疑人生,怀疑自我,不断替换qt版本,去掉应用程序的等,搞了一年没有搞定,最后要交货了没办法,找了咨询机构解决。咨询的工程师只花了3天解决。

    1. 首先看是不是内存泄漏,发现Free命令确实是随着时间推移,内存在减少
    2. 接着看看所有应用程序的uss 追踪一段时间,没有变化
    3. 再看看proc/meminfo 里面的slab区域,发现随着时间的推移,slab区域在变化,再进一步查看一下proc/slabinfo ,slabinfo会显示内核每个slab object分配情况,发现里面有个很小的kmalloc区域在慢慢变大,从而确定是内核的内存泄漏
    4. 把内核的kmemleak 去监控内存的泄漏,最后发现是芯片公司给他们的BSP 有个bug,里面有个指针是kmalloc 出来,指针指向的内容也是kmalloc出来的,芯片BSP工程师范了个错误,他只释放了指针指向的内容的内存,指针没有释放,所以导致每次就泄漏几个字节

    所以linux工程师 懂linux是一个基础,否则就会话大量的时间解决不了问题,最后怀疑人生。比如这个,懂内存管理的话,很容易查到问题所在。

    Double free:

     

     

     

    Kmalloc内存越界:

     

    Kmalloc内存泄漏(注意在启动参数中使能kmemleak=on,内核congfig只配置没使能)

     

    这个文件会把怀疑的内存泄漏点记录下来

     

    1. Lockup 检测

       

      对于softlockup ,比如某个线程调用了spinlock,这时候cpu抢占就关闭了,linux会调用一个高优先级的实时线程,线程里面有个计数器每次加一,并且有个定时器判断计数器是否有没有加1 ,计数器来了之后中断中检测计数器一直没更新,就证明系统已经softlockup了,此时系统会打印出来,定位到backtrace。

     对于hardlockup,比如某个cpu上连中断都锁住了,中断都无法进入。Linux里面必须使用NMI 非可屏蔽的中断,(内核编译的时候使能)。

    所以调试的时候发现内核没有响应了,这时候就可以打开lockup调试

     

    再多的工具和手段也代替不了解决问题的思维,只有不断的去实践,才能找到解决问题的“感觉”

    展开全文
  • 这里说明下,本人调试内核版本是2.6.11.12,为什么去调试这么“古老”的版本?原因不多说了,你手头也许正拿着ULK3,而它针对的内核版本正是2.6.11,有比这更好的理由吗?而且这个版本不算旧,已不算新,我认为还...
  • 消费电子异军突起,嵌入式与互联网成为最热门的技术。在所有操作系统中,Linux是发展很快、应用很广泛的一种操作系统。Linux的开
  •  在内核调试技术之中,简单的是printk的使用了,它的用法和C语言应用程序中的printf使用类似,在应用程序中依靠的是stdio.h中的库,而在linux内核中没有这个库,所以在linux内核中,使用这个printk要对内核的实现...
  • Linux内核调试的方式以及工具集锦

    万次阅读 多人点赞 2017-04-01 21:31:55
    GitHub Linux内核调试的方式以及工具集锦 LDD-LinuxDeviceDrivers/study/debug 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 因本人技术水平和知识面...
    CSDNGitHub
    Linux内核调试的方式以及工具集锦LDD-LinuxDeviceDrivers/study/debug


    知识共享许可协议
    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作
    因本人技术水平和知识面有限, 内容如有纰漏或者需要修正的地方, 欢迎大家指正, 也欢迎大家提供一些其他好的调试工具以供收录, 鄙人在此谢谢啦

    "调试难度本来就是写代码的两倍. 因此, 如果你写代码的时候聪明用尽, 根据定义, 你就没有能耐去调试它了."
            --Brian Kernighan

    1 内核调试以及工具总结


    内核总是那么捉摸不透, 内核也会犯错, 但是调试却不能像用户空间程序那样, 为此内核开发者为我们提供了一系列的工具和系统来支持内核的调试.

    内核的调试, 其本质是内核空间与用户空间的数据交换, 内核开发者们提供了多样的形式来完成这一功能.

    工具描述
    debugfs等文件系统提供了 procfs, sysfs, debugfs以及 relayfs 来与用户空间进行数据交互, 尤其是 debugfs, 这是内核开发者们实现的专门用来调试的文件系统接口. 其他的工具或者接口, 多数都依赖于 debugfs.
    printk强大的输出系统, 没有什么逻辑上的bug是用PRINT解决不了的
    ftrace以及其前端工具trace-cmd等内核提供了 ftrace 工具来实现检查点, 事件等的检测, 这一框架依赖于 debugfs, 他在 debugfs 中的 tracing 子系统中为用户提供了丰富的操作接口, 我们可以通过该系统对内核实现检测和分析. 功能虽然强大, 但是其操作并不是很简单, 因此使用者们为实现了 trace-cmd 等前端工具, 简化了 ftrace 的使用.
    kprobe以及更强大的systemtap内核中实现的 krpobe 通过类似与代码劫持一样的技巧, 在内核的代码或者函数执行前后, 强制加上某些调试信息, 可以很巧妙的完成调试工作, 这是一项先进的调试技术, 但是仍然有觉得它不够好, 劫持代码需要用驱动的方式编译并加载, 能不能通过脚本的方式自动生成劫持代码并自动加载和收集数据, 于是systemtap 出现了. 通过 systemtap 用户只需要编写脚本, 就可以完成调试并动态分析内核
    kgdb && kgtpKGDB 是大名鼎鼎的内核调试工具, KGTP则通过驱动的方式强化了 gdb的功能, 诸如tracepoint, 打印内核变量等.
    perferf Event是一款随 inux内核代码一同发布和维护的性能诊断工具, 核社区维护和发展. Perf 不仅可以用于应用程序的性能统计分析, 也可以应用于内核代码的性能统计和分析. 得益于其优秀的体系结构设计, 越来越多的新功能被加入 Perf, 使其已经成为一个多功能的性能统计工具集
    LTTngLTTng 是一个 Linux 平台开源的跟踪工具, 是一套软件组件, 可允许跟踪 Linux 内核和用户程序, 并控制跟踪会话(开始/停止跟踪、启动/停止事件 等等).

    2 用户空间与内核空间数据交换的文件系统


    内核中有三个常用的伪文件系统: procfs, debugfs和sysfs.

    文件系统描述
    procfsThe proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
    sysfsThe filesystem for exporting kernel objects.
    debugfsDebugfs exists as a simple way for kernel developers to make information available to user space.
    relayfsA significantly streamlined version of relayfs was recently accepted into the -mm kernel tree.

    它们都用于Linux内核和用户空间的数据交换, 但是适用的场景有所差异:

    • procfs 历史最早, 最初就是用来跟内核交互的唯一方式, 用来获取处理器、内存、设备驱动、进程等各种信息.

    • sysfskobject 框架紧密联系, 而 kobject 是为设备驱动模型而存在的, 所以 sysfs 是为设备驱动服务的.

    • debugfs 从名字来看就是为 debug 而生, 所以更加灵活.

    • relayfs 是一个快速的转发 (relay) 数据的文件系统, 它以其功能而得名. 它为那些需要从内核空间转发大量数据到用户空间的工具和应用提供了快速有效的转发机制.

    在 Linux 下用户空间与内核空间数据交换的方式, 第 2 部分: procfs、seq_file、debugfs和relayfs

    Linux 文件系统:procfs, sysfs, debugfs 用法简介

    2.1 procfs文件系统


    • ProcFs 介绍`

    procfs 是比较老的一种用户态与内核态的数据交换方式, 内核的很多数据都是通过这种方式出口给用户的, 内核的很多参数也是通过这种方式来让用户方便设置的. 除了 sysctl 出口到 /proc 下的参数, procfs 提供的大部分内核参数是只读的. 实际上, 很多应用严重地依赖于procfs, 因此它几乎是必不可少的组件. 前面部分的几个例子实际上已经使用它来出口内核数据, 但是并没有讲解如何使用, 本节将讲解如何使用procfs.

    • 参考资料

    用户空间与内核空间数据交换的方式(2)——procfs

    2.2 sysfs文件系统


    内核子系统或设备驱动可以直接编译到内核, 也可以编译成模块, 编译到内核, 使用前一节介绍的方法通过内核启动参数来向它们传递参数, 如果编译成模块, 则可以通过命令行在插入模块时传递参数, 或者在运行时, 通过 sysfs 来设置或读取模块数据.

    Sysfs 是一个基于内存的文件系统, 实际上它基于ramfs, sysfs 提供了一种把内核数据结构, 它们的属性以及属性与数据结构的联系开放给用户态的方式, 它与 kobject 子系统紧密地结合在一起, 因此内核开发者不需要直接使用它, 而是内核的各个子系统使用它. 用户要想使用 sysfs 读取和设置内核参数, 仅需装载 sysfs 就可以通过文件操作应用来读取和设置内核通过 sysfs 开放给用户的各个参数:

    mkdir -p /sysfs
    mount -t sysfs sysfs /sysfs

    注意, 不要把 sysfssysctl 混淆, sysctl 是内核的一些控制参数, 其目的是方便用户对内核的行为进行控制, 而 sysfs 仅仅是把内核的 kobject 对象的层次关系与属性开放给用户查看, 因此 sysfs 的绝大部分是只读的, 模块作为一个 kobject 也被出口到 sysfs, 模块参数则是作为模块属性出口的, 内核实现者为模块的使用提供了更灵活的方式, 允许用户设置模块参数在 sysfs 的可见性并允许用户在编写模块时设置这些参数在 sysfs 下的访问权限, 然后用户就可以通过 sysfs 来查看和设置模块参数, 从而使得用户能在模块运行时控制模块行为.

    用户空间与内核空间数据交换的方式(6)——模块参数与sysfs

    2.3 debugfs文件系统


    内核开发者经常需要向用户空间应用输出一些调试信息, 在稳定的系统中可能根本不需要这些调试信息, 但是在开发过程中, 为了搞清楚内核的行为, 调试信息非常必要, printk可能是用的最多的, 但它并不是最好的, 调试信息只是在开发中用于调试, 而 printk 将一直输出, 因此开发完毕后需要清除不必要的 printk 语句, 另外如果开发者希望用户空间应用能够改变内核行为时, printk 就无法实现.

    因此, 需要一种新的机制, 那只有在需要的时候使用, 它在需要时通过在一个虚拟文件系统中创建一个或多个文件来向用户空间应用提供调试信息.

    有几种方式可以实现上述要求:

    • 使用 procfs, 在 /proc 创建文件输出调试信息, 但是 procfs 对于大于一个内存页(对于 x864K)的输出比较麻烦, 而且速度慢, 有时回出现一些意想不到的问题.

    • 使用 sysfs( 2.6 内核引入的新的虚拟文件系统), 在很多情况下, 调试信息可以存放在那里, 但是sysfs主要用于系统管理,它希望每一个文件对应内核的一个变量,如果使用它输出复杂的数据结构或调试信息是非常困难的.

    • 使用 libfs 创建一个新的文件系统, 该方法极其灵活, 开发者可以为新文件系统设置一些规则, 使用 libfs 使得创建新文件系统更加简单, 但是仍然超出了一个开发者的想象.

    为了使得开发者更加容易使用这样的机制, Greg Kroah-Hartman 开发了 debugfs(在 2.6.11 中第一次引入), 它是一个虚拟文件系统, 专门用于输出调试信息, 该文件系统非常小, 很容易使用, 可以在配置内核时选择是否构件到内核中, 在不选择它的情况下, 使用它提供的API的内核部分不需要做任何改动.

    用户空间与内核空间数据交换的方式(1)——debugfs

    Linux内核里的DebugFS

    Linux驱动调试的Debugfs的使用简介

    Linux Debugfs文件系统介绍及使用

    Linux内核里的DebugFS

    Debugging the Linux Kernel with debugfs

    debugfs-seq_file

    Linux Debugfs文件系统介绍及使用

    Linux 文件系统:procfs, sysfs, debugfs 用法简介

    用户空间与内核空间数据交换的方式(1)——debugfs

    Linux 运用debugfs调试方法

    2.4 relayfs文件系统


    relayfs 是一个快速的转发(relay)数据的文件系统, 它以其功能而得名. 它为那些需要从内核空间转发大量数据到用户空间的工具和应用提供了快速有效的转发机制.

    Channelrelayfs 文件系统定义的一个主要概念, 每一个 channel 由一组内核缓存组成, 每一个 CPU 有一个对应于该 channel 的内核缓存, 每一个内核缓存用一个在 relayfs 文件系统中的文件文件表示, 内核使用 relayfs 提供的写函数把需要转发给用户空间的数据快速地写入当前 CPU 上的 channel 内核缓存, 用户空间应用通过标准的文件 I/ O函数在对应的 channel 文件中可以快速地取得这些被转发出的数据 mmap 来. 写入到 channel 中的数据的格式完全取决于内核中创建channel 的模块或子系统.

    relayfs 的用户空间API :

    relayfs 实现了四个标准的文件 I/O 函数, open、mmap、poll和close

    函数描述
    open打开一个 channel 在某一个 CPU 上的缓存对应的文件.
    mmap把打开的 channel 缓存映射到调用者进程的内存空间.
    read读取 channel 缓存, 随后的读操作将看不到被该函数消耗的字节, 如果 channel 的操作模式为非覆盖写, 那么用户空间应用在有内核模块写时仍可以读取, 但是如 channel 的操作模式为覆盖式, 那么在读操作期间如果有内核模块进行写,结果将无法预知, 因此对于覆盖式写的 channel, 用户应当在确认在 channel 的写完全结束后再进行读.
    poll用于通知用户空间应用转发数据跨越了子缓存的边界, 支持的轮询标志有 POLLINPOLLRDNORMPOLLERR
    close关闭 open 函数返回的文件描述符, 如果没有进程或内核模块打开该 channel 缓存, close 函数将释放该channel 缓存

    注意 : 用户态应用在使用上述 API 时必须保证已经挂载了 relayfs 文件系统, 但内核在创建和使用 channel时不需要relayfs 已经挂载. 下面命令将把 relayfs 文件系统挂载到 /mnt/relay.

    用户空间与内核空间数据交换的方式(4)——relayfs

    Relay:一种内核到用户空间的高效数据传输技术

    2.5 seq_file


    一般地, 内核通过在 procfs 文件系统下建立文件来向用户空间提供输出信息, 用户空间可以通过任何文本阅读应用查看该文件信息, 但是 procfs 有一个缺陷, 如果输出内容大于1个内存页, 需要多次读, 因此处理起来很难, 另外, 如果输出太大, 速度比较慢, 有时会出现一些意想不到的情况, Alexander Viro 实现了一套新的功能, 使得内核输出大文件信息更容易, 该功能出现在 2.4.15(包括 2.4.15)以后的所有 2.4 内核以及 2.6 内核中, 尤其是在 2.6 内核中,已经大量地使用了该功能

    用户空间与内核空间数据交换的方式(3)——seq_file

    内核proc文件系统与seq接口(4)—seq_file接口编程浅析

    Linux内核中的seq操作

    seq_file源码分析

    用序列文件(seq_file)接口导出常用数据结构

    seq_file机制

    3 printk


    在内核调试技术之中, 最简单的就是 printk 的使用了, 它的用法和C语言应用程序中的 printf 使用类似, 在应用程序中依靠的是 stdio.h 中的库, 而在 linux 内核中没有这个库, 所以在 linux 内核中, 实现了自己的一套库函数, printk 就是标准的输出函数

    linux内核调试技术之printk

    调整内核printk的打印级别

    linux设备驱动学习笔记–内核调试方法之printk

    4 ftrace && trace-cmd


    4.1 trace && ftrace


    Linux当前版本中, 功能最强大的调试、跟踪手段. 其最基本的功能是提供了动态和静态探测点, 用于探测内核中指定位置上的相关信息.

    静态探测点, 是在内核代码中调用 ftrace 提供的相应接口实现, 称之为静态是因为, 是在内核代码中写死的, 静态编译到内核代码中的, 在内核编译后, 就不能再动态修改. 在开启 ftrace 相关的内核配置选项后, 内核中已经在一些关键的地方设置了静态探测点, 需要使用时, 即可查看到相应的信息.

    动态探测点, 基本原理为 : 利用 mcount 机制, 在内核编译时, 在每个函数入口保留数个字节, 然后在使用 ftrace时, 将保留的字节替换为需要的指令, 比如跳转到需要的执行探测操作的代码。

    ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为, 以便进行故障调试或性能分析.

    最早 ftrace 是一个 function tracer, 仅能够记录内核的函数调用流程. 如今 ftrace 已经成为一个 framework, 采用 plugin 的方式支持开发人员添加更多种类的 trace 功能.

    FtraceRedHatSteve Rostedt 负责维护. 到 2.6.30 为止, 已经支持的 tracer 包括 :

    Tracer描述
    Function tracer 和 Function graph tracer跟踪函数调用
    Schedule switch tracer跟踪进程调度情况
    Wakeup tracer跟踪进程的调度延迟, 即高优先级进程从进入 ready 状态到获得 CPU 的延迟时间. 该 tracer 只针对实时进程
    Irqsoff tracer当中断被禁止时, 系统无法相应外部事件, 比如键盘和鼠标, 时钟也无法产生 tick 中断. 这意味着系统响应延迟, irqsoff 这个 tracer 能够跟踪并记录内核中哪些函数禁止了中断, 对于其中中断禁止时间最长的, irqsoff 将在 log 文件的第一行标示出来, 从而使开发人员可以迅速定位造成响应延迟的罪魁祸首.
    Preemptoff tracer和前一个 tracer 类似, preemptoff tracer 跟踪并记录禁止内核抢占的函数, 并清晰地显示出禁止抢占时间最长的内核函数.
    Preemptirqsoff tracer同上, 跟踪和记录禁止中断或者禁止抢占的内核函数, 以及禁止时间最长的函数.
    Branch tracer跟踪内核程序中的 likely/unlikely 分支预测命中率情况. Branch tracer 能够记录这些分支语句有多少次预测成功. 从而为优化程序提供线索.
    Hardware branch tracer利用处理器的分支跟踪能力, 实现硬件级别的指令跳转记录. 在 x86 上, 主要利用了 BTS 这个特性.
    Initcall tracer记录系统在 boot 阶段所调用的 init call.
    Mmiotrace tracer记录 memory map IO 的相关信息.
    Power tracer记录系统电源管理相关的信息
    Sysprof tracer缺省情况下, sysprof tracer 每隔 1 msec 对内核进行一次采样,记录函数调用和堆栈信息.
    Kernel memory tracer内存 tracer 主要用来跟踪 slab allocator 的分配情况. 包括 kfree, kmem_cache_allocAPI 的调用情况, 用户程序可以根据 tracer 收集到的信息分析内部碎片情况, 找出内存分配最频繁的代码片断, 等等.
    Workqueue statistical tracer这是一个 statistic tracer, 统计系统中所有的 workqueue 的工作情况, 比如有多少个 work 被插入 workqueue, 多少个已经被执行等. 开发人员可以以此来决定具体的 workqueue 实现, 比如是使用 single threaded workqueue 还是 per cpu workqueue.
    Event tracer跟踪系统事件, 比如 timer, 系统调用, 中断等.

    这里还没有列出所有的 tracer, ftrace 是目前非常活跃的开发领域, 新的 tracer 将不断被加入内核。

    ftrace和它的前端工具trace-cmd(深入了解Linux系统的利器)

    ftrace 简介

    内核性能调试–ftrace

    使用 ftrace 调试 Linux 内核,第 1 部分

    ftrace的使用

    [转]Linux内核跟踪之trace框架分析

    Linux trace使用入门

    4.2 ftrace前端工具trace-cmd


    • trace-cmd 介绍

    trace-cmd 和 开源的 kernelshark 均是内核Ftrace 的前段工具, 用于分分析核性能.

    他们相当于是一个 /sys/kernel/debug/tracing 中文件系统接口的封装, 为用户提供了更加直接和方便的操作.

    • 使用
    #  收集信息
    sudo trace-cmd reord subsystem:tracing 
    
    #  解析结果
    #sudo trace-cmd report

    trace-cmd: A front-end for Ftrace

    其本质就是对/sys/kernel/debug/tracing/events 下各个模块进行操作, 收集数据并解析

    5 Kprobe && systemtap


    5.1 内核kprobe机制


    kprobelinux 内核的一个重要特性, 是一个轻量级的内核调试工具, 同时它又是其他一些更高级的内核调试工具(比如 perfsystemtap)的 “基础设施”, 4.0版本的内核中, 强大的 eBPF 特性也寄生于 kprobe 之上, 所以 kprobe 在内核中的地位就可见一斑了.

    Kprobes 提供了一个强行进入任何内核例程并从中断处理器无干扰地收集信息的接口. 使用 Kprobes 可以收集处理器寄存器和全局数据结构等调试信息。开发者甚至可以使用 Kprobes 来修改 寄存器值和全局数据结构的值.

    如何高效地调试内核?

    printk 是一种方法, 但是 printk 终归是毫无选择地全量输出, 某些场景下不实用, 于是你可以试一下tracepoint, 我使能 tracepoint 机制的时候才输出. 对于傻傻地放置 printk 来输出信息的方式, tracepoint 是个进步, 但是 tracepoint 只是内核在某些特定行为(比如进程切换)上部署的一些静态锚点, 这些锚点并不一定是你需要的, 所以你仍然需要自己部署tracepoint, 重新编译内核. 那么 kprobe 的出现就很有必要了, 它可以在运行的内核中动态插入探测点, 执行你预定义的操作.

    它的基本工作机制是 : 用户指定一个探测点, 并把一个用户定义的处理函数关联到该探测点, 当内核执行到该探测点时, 相应的关联函数被执行,然后继续执行正常的代码路径.

    kprobe 实现了三种类型的探测点 : kprobes, jprobeskretprobes(也叫返回探测点). kprobes 是可以被插入到内核的任何指令位置的探测点, jprobes 则只能被插入到一个内核函数的入口, 而 kretprobes 则是在指定的内核函数返回时才被执行.

    kprobe工作原理

    随想录(强大的kprobe)

    kprobe原理解析(一)

    5.2 前端工具systemtap


    SystemTap 是监控和跟踪运行中的 Linux 内核的操作的动态方法. 这句话的关键词是动态, 因为 SystemTap 没有使用工具构建一个特殊的内核, 而是允许您在运行时动态地安装该工具. 它通过一个 Kprobes 的应用编程接口 (API) 来实现该目的.

    SystemTap 与一种名为 DTrace 的老技术相似,该技术源于 Sun Solaris 操作系统. 在 DTrace 中, 开发人员可以用 D 编程语言(C 语言的子集, 但修改为支持跟踪行为)编写脚本. DTrace 脚本包含许多探针和相关联的操作, 这些操作在探针 “触发” 时发生. 例如, 探针可以表示简单的系统调用,也可以表示更加复杂的交互,比如执行特定的代码行

    DTraceSolaris 最引人注目的部分, 所以在其他操作系统中开发它并不奇怪. DTrace 是在 Common Development and Distribution License (CDDL) 之下发行的, 并且被移植到 FreeBSD 操作系统中.

    另一个非常有用的内核跟踪工具是 ProbeVue, 它是 IBMIBM® AIX® 操作系统 6.1 开发的. 您可以使用 ProbeVue 探查系统的行为和性能, 以及提供特定进程的详细信息. 这个工具使用一个标准的内核以动态的方式进行跟踪.

    考虑到 DTraceProbeVue 在各自的操作系统中的巨大作用, 为 Linux 操作系统策划一个实现该功能的开源项目是势不可挡的. SystemTap2005 年开始开发, 它提供与 DTraceProbeVue 类似的功能. 许多社区还进一步完善了它, 包括 Red HatIntelHitachiIBM 等.

    这些解决方案在功能上都是类似的, 在触发探针时使用探针和相关联的操作脚本.

    SystemTap 学习笔记 - 安装篇

    Linux 自检和 SystemTap 用于动态内核分析的接口和语言

    Brendan’s blog Using SystemTap

    内核调试神器SystemTap — 简介与使用(一)

    内核探测工具systemtap简介

    SystemTap Beginner

    使用systemtap调试linux内核

    Ubuntu Kernel Debuginfo

    Linux 下的一个全新的性能测量和调式诊断工具 Systemtap, 第 3 部分: Systemtap

    6 kgdb && kgtp


    6.1 kgdb


    • KDB 和 KGDB 合并, 并进入内核

    KGDB 是大名鼎鼎的内核调试工具, 他是由 KDBKGDB 项目合并而来.

    kdb 是一个Linux系统的内核调试器, 它是由SGI公司开发的遵循GPL许可证的开放源码调试工具. kdb 嵌入在Linux 内核中. 为内核&&驱动程序员提供调试手段. 它适合于调试内核空间的程序代码. 譬如进行设备驱动程序调试. 内核模块的调试等.

    kgdbkdb 现在已经合并了. 对于一个正在运行的kgdb 而言, 可以使用 gdbmonitor 命令来使用 kdb 命令. 比如

    (gdb)gdb monitor ps -A

    就可以运行 kdbps 命令了.

    分析一下 kdb 补丁和合入主线的 kdb 有啥不同

    kdbkgdb 合并之后, 也可以使用 kgdbIO 驱动(比如键盘), 但是同时也 kdb也丧失了一些功能. 合并之后的kdb不在支持汇编级的源码调试. 因此它现在也是平台独立的.

    1. kdump和kexec已经被移除。

    2. 从/proc/meninfo中获取的信息比以前少了。

    3. bt命令现在使用的是内核的backtracer,而不是kdb原来使用的反汇编。

    4. 合并之后的kdb不在具有原来的反汇编(id命令)

    总结一下 : kdbkgdb 合并之后,系统中对这两种调试方式几乎没有了明显的界限,比如通过串口进行远程访问的时候,可以使用 kgdb 命令, 也可以使用 kdb 命令(使用gdb monitor实现)

    6.2 KGTP


    KGTP 是一个 实时 轻量级 Linux 调试器 和 跟踪器. 使用 KGTP

    使用 KGTP 不需要在 Linux 内核上打 PATCH 或者重新编译, 只要编译KGTP模块并 insmod 就可以.

    其让 Linux 内核提供一个远程 GDB 调试接口, 于是在本地或者远程的主机上的GDB可以在不需要停止内核的情况下用 GDB tracepoint 和其他一些功能 调试 和 跟踪 Linux.

    即使板子上没有 GDB 而且其没有可用的远程接口, KGTP 也可以用离线调试的功能调试内核(见http://code.google.com/p/kgtp/wiki/HOWTOCN#/sys/kernel/debug/gtpframe和离线调试)。

    KGTP支持 X86-32 , X86-64 , MIPS 和 ARM 。
    KGTP在Linux内核 2.6.18到upstream 上都被测试过。
    而且还可以用在 Android 上(见 HowToUseKGTPinAndroid)

    github-KGTP

    KGTP内核调试使用

    KGTP中增加对GDB命令“set trace-buffer-size”的支持 - Week 5

    7 perf


    Perf 是用来进行软件性能分析的工具。
    通过它, 应用程序可以利用 PMU, tracepoint 和内核中的特殊计数器来进行性能统计. 它不但可以分析指定应用程序的性能问题 (per thread). 也可以用来分析内核的性能问题, 当然也可以同时分析应用代码和内核,从而全面理解应用程序中的性能瓶颈.

    最初的时候, 它叫做 Performance counter, 在 2.6.31 中第一次亮相. 此后他成为内核开发最为活跃的一个领域. 在 2.6.32 中它正式改名为 Performance Event, 因为 perf 已不再仅仅作为 PMU 的抽象, 而是能够处理所有的性能相关的事件.

    使用 perf, 您可以分析程序运行期间发生的硬件事件,比如 instructions retired , processor clock cycles 等; 您也可以分析软件事件, 比如 Page Fault 和进程切换。
    这使得 Perf 拥有了众多的性能分析能力, 举例来说,使用 Perf 可以计算每个时钟周期内的指令数, 称为 IPC, IPC 偏低表明代码没有很好地利用 CPU.

    Perf 还可以对程序进行函数级别的采样, 从而了解程序的性能瓶颈究竟在哪里等等. Perf 还可以替代 strace, 可以添加动态内核 probe 点. 还可以做 benchmark 衡量调度器的好坏.

    人们或许会称它为进行性能分析的”瑞士军刀”, 但我不喜欢这个比喻, 我觉得 perf 应该是一把世间少有的倚天剑.
    金庸笔下的很多人都有对宝刀的癖好, 即便本领低微不配拥有, 但是喜欢, 便无可奈何. 我恐怕正如这些人一样, 因此进了酒馆客栈, 见到相熟或者不相熟的人, 就要兴冲冲地要讲讲那倚天剑的故事.

    Perf – Linux下的系统性能调优工具,第 1 部分

    perf Examples

    改进版的perf, Performance analysis tools based on Linux perf_events (aka perf) and ftrace

    Perf使用教程

    linux下的内核测试工具——perf使用简介

    perf 移植

    8 其他Tracer工具


    8.1 LTTng


    LTTng 是一个 Linux 平台开源的跟踪工具, 是一套软件组件, 可允许跟踪 Linux 内核和用户程序, 并控制跟踪会话(开始/停止跟踪、启动/停止事件 等等). 这些组件被绑定如下三个包 :

    描述
    LTTng-tools库和用于跟踪会话的命令行接口
    LTTng-modules允许用 LTTng 跟踪 LinuxLinux 内核模块
    LTTng-UST用户空间跟踪库


    Linux 平台开源的跟踪工具:LTTng

    用 lttng 跟踪内核

    LTTng and LTTng project

    8.2 eBPF


    extended Berkeley Packet Filter(eBPF)是一个可以在事件上运行程序的高效内核虚拟机(JIT)。它可能最终会提供 ftrace 和 perf_events 的内核编程,并强化其他的 tracer。这是 Alexei Starovoitov 目前正在开发的,还没有完全集成,但是从4.1开始已经对一些优秀的工具有足够的内核支持了,如块设备I/O的延迟热图。可参考其主要作者 Alexei Starovoitov 的BPF slides和eBPF samples。

    8.3 Ktap


    ktap 在过去是一款前景很好的 tracer,它使用内核中的 lua 虚拟机处理,在没有调试信息的情况下在嵌入式设备上运行的很好。它分为几个步骤,并在有一段时间似乎超过了 Linux 上所有的追踪器。然后 eBPF 开始进行内核集成,而 ktap 的集成在它可以使用 eBPF 替代它自己的虚拟机后才开始。因为 eBPF 仍将持续集成几个月,ktap 开发者要继续等上一段时间。我希??今年晚些时候它能重新开发。

    8.4 dtrace4linux


    dtrace4linux 主要是 Paul Fox 一个人在业余时间完成的,它是 Sun DTrace 的 Linux 版本。它引入瞩目,还有一些 provider 可以运行,但是从某种程度上来说还不完整,更多的是一种实验性的工具(不安全)。我认为,顾忌到许可问题,人们会小心翼翼的为 dtrace4linux 贡献代码:由于当年 Sun 开源DTrace 使用的是 CDDL 协议,而 dtrace4linux 也不大可能最终进入 Linux kernel。Paul 的方法很可能会使其成为一个 add-on。我很乐意看到 Linux 平台上的 DTrace 和这个项目的完成,我认为当我加入 Netflix 后将会花些时间来协助完成这个项目。然而,我还是要继续使用内置的 tracers,如 ftrace 和 perf_events。

    8.5 OL DTrace


    Oracle Linux DTrace为了将 DTrace 引入 Linux,特别是 Oracle Linux,做出了很大的努力。这些年来发布的多个版本表明了它的稳定进展。开发者们以一种对这个项目的前景看好的态度谈论着改进 DTrace 测试套件。很多有用的 provider 已经完成了,如:syscall, profile, sdt, proc, sched 以及 USDT。我很期待 fbt(function boundary tracing, 用于内核动态跟踪)的完成,它是 Linux 内核上非常棒的 provider。OL DTrace 最终的成功将取决于人们对运行 Oracle Linux(为技术支持付费)有多大兴趣,另一方面取决于它是否完全开源:它的内核元件是开源的,而我没有看到它的用户级别代码。

    8.6 sysdig


    sysdig是一个使用类tcpdump语法来操作系统事件的新tracer,它使用lua提交进程。它很优秀,它见证了系统跟踪领域的变革。它的局限性在于它只在当前进行系统调用,在提交进行时将所有事件转储为用户级别。你可以使用系统调用做很多事情,然而我还是很希望它能支持跟踪点、kprobe和uprobe。我还期待它能支持eBPF做内核摘要。目前,sysdig开发者正在增加容器支持。留意这些内容。


    知识共享许可协议本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作.
    因本人技术水平和知识面有限, 内容如有纰漏或者需要修正的地方, 欢迎大家指正, 也欢迎大家提供一些其他好的调试工具以供收录, 鄙人在此谢谢啦

    展开全文
  • Linux 内核调试器内幕 KDB 入门指南 级别: 初级 Hariprasad Nellitheertha, 软件工程师, IBM 2003 年 9 月 01 日 调 试内核问题时能够跟踪内核执行情况并查看其内存和数据结构是非常有用的Linux 中的内置内核调试器 ...
  • linux内核调试

    千次阅读 2018-09-13 20:52:36
    1、early printk loglevel  printk的log输出是由console实现(会在其他文章中说明)。由于在kernel刚启动的过程中,还没有为串口...为此,linux提供了early console机制,用于实现为设备注册console之前的早期log...

    1、early printk   loglevel

           printk的log输出是由console实现(会在其他文章中说明)。由于在kernel刚启动的过程中,还没有为串口等设备等注册console(在device probe阶段实现),此时无法通过正常的console来输出log。
    为此,linux提供了early console机制,用于实现为设备注册console之前的早期log的输出。这个console在kernel启动的早期阶段就会被注册,主要通过输出设备(比如串口设备)的简单的write方法直接进行数据打印。

           CONFIG_DEBUG_LL
           ENTRY(printch)定义在arch/arm/debug.S中,需要用这个宏来打开。
           CONFIG_EARLY_PRINTK

           lichee\linux-3.10\arch\arm64\kernel\early_printk.c

                      

     

    2、initcall_debug

           initcall_debug参数定位初始化过程中的错误信息发生的位置。

                      

                      

     

     

    3、内核打印

           Linux内核用函数printk打印调试信息,该函数的用法与C库打印函数printf格式类似,但在内核使用。用户可在内核代码中的某位置加入函数printk,直接把所关心的信息打打印到屏幕上或日志文件中。

           函数printk根据日志级别(loglevel)对调试信息进行分类。日志级别用宏定义,展开为一个字符串,在编译时由预处理器将它和消息文本拼接成一个字符串。

           (1)、不直接调用printk,而是调用pr_xxx,如下

                      

           (2)、不直接调用printk,而是调用dev_xxx

    设备的名字将为打印前缀, dev_warn();     dev_info();    dev_emerg();

                      

           (3)、带函数与行数打印

                    printk("%s%d\n", __func__, __LINE__);

           (4)、printk

                    printk打印级别动态调整

                      

           printk有8个loglevel,定义在lichee\linux-3.10\include\linux\ kern_levels.h中,其中数值范围从0到7,数值越小,优先级越低。未指定优先级的默认级别定义在/kernel/printk.c中,当优先级的值小console_loglevel这个整数变量的值,信息才能显示出来

                        

                        

          printk在中断,软中断,spinlock里面都可以调用。printk的开销很大,尤其是串口打印,会让系统慢很多,所以产品里面减少不必要的打印,或者bootargs里面"quiet"。

           (5)、内核带时间戳打印

                        

     

           (6)、dumpstack()   warn_on(1)

           回溯函数调用过程。

     

    4、内核模块源码级调试

           比较少用,参考kdb,gdb调试文档(待续,本人只用gdb调试过上层,没有调过内核,视频里面看也只是泛泛而讲)。

     

    5、oops与panic

           Oops英文单词的中文含义是“哎呀”,表示“惊叹”;Panic英文单词的中文含义是“惊慌”。所以panic的程度显然是高于oops的,因为惊叹不一定会惊慌,而惊慌最容易失措,内核panic后,就死机了,俗称内核崩溃。但是内核报oops,这个时候不见得会panic,它可能只是报个oops,杀死进程而已。

    oops不一定panic,中断上下文的oops会panic,如果panic_on_oops设置为1,一律panic。

           oops 显示发生错误时处理器的状态,包括 CPU 寄存器的内容、页描述符表的位置,以及其他一些信息。

                              

     

     

                              

                             

    --->cd0+a0=d70

                            

     

    6、其他内核debug选项和功能

       (1)、 no_console_suspend

           在suspend的时候console 不进行suspend,否则console suspend之后其他driver在suspend 过程中印的log都显示不出来,因此加这个参数一般用于调试suspend 和 resume。在bootargs中添加"no_console_suspend"参数。

     

       (2)、SLUB_DEBUG   kmemleak  --->内存检测

      Linux常见的内存访问错误有:越界访问(out of bounds)、访问已经释放的内存(use after free)、重复释放、内存泄露(memory leak)、栈溢出(stack overflow)等。

           (a)、SLUB_DEBUG 内核中小块内存大量使用slab/slub分配器,slub_debug提供了:访问已经释放的内存、越界访问、重复释放内存等功能检测

    支持slub_debug内核配置

    重新配置kernel选项,打开如下选项即可。

    CONFIG_SLUB=y

    CONFIG_SLUB_DEBUG=y

    CONFIG_SLUB_DEBUG_ON=y

                          

                          

                          

     

       (1)、重复释放

                        

                         

     

                      

    2c4+10c -> 3D0

                      

          

           (2)、内存越界

                        

     

                            

           (b)、kmemleak是内核提供的一种检测内存泄露工具,启动一个内核线程扫描内存,并打印发现新的未引用对象数量。

            使用kmemlieak,需要打开如下内核选项。

    CONFIG_HAVE_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
    # CONFIG_DEBUG_KMEMLEAK_TEST is not set
    CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y---------
    或者关闭此选项,则不需要在命令行添加kmemleak=on。

                           

     

      (3)、DEBUG_ATOMIC_SLEEP  -->检测在spinlock,中断,软中断中的sleep

                             

                         

                             

       (4)、RCU Stall

           内核配置项:

                          

       (5)、Lockup detector

           kernel/watchdog.c

           NMI中断+定时器中断+高优先级RT线程

           用定时器中断,检测高优先级线程有无机会执行->softlockup

           用NMI,检测定时器中断有无机会执行->hard lockup

    内核选项:

                         

    示例:

                       

                        

    展开全文
  • 操作环境:x86-64位linux操作系统(特别注意是64位,如果你是32位则修改相应的安装参数就可以了,多少位是可选的,本文以Ubuntu14.04)、GCC、GDB、QEMU、./configure与make的依赖(有的系统自带了,如果没带那就坑...

    操作环境:x86-64位linux操作系统(特别注意是64位,如果你是32位则修改相应的安装参数就可以了,多少位是可选的,本文以Ubuntu14.04)、GCC、GDB、QEMU、./configure与make的依赖(有的系统自带了,如果没带那就坑了)

    注意事项:严格注意自己的操作系统环境,缺啥补啥,并且要对应好环境,别拿32位跟64的配置搞错,其它的也自己注意一下,理解每个步骤是干啥。

    修改记录:有的朋友反应说没有最小文件系统,所以装了一个busybox(1.25.0版本)

    目录

    目录

    一、环境配置

    二、编译linux内核

    2.1 下载与编译内核

    2.2 各种问题的解决方式

    三、最小文件系统busybox(1.25.0版本)

    3.1  源码编译

     3.2  生成initrd

    3.3  测试根文件系统

    四、qemu的使用

    参考资料:


     

    一、环境配置

    在虚拟机上搭建Ubuntu就不介绍了,比较简单,如果不会可以参考VMWare安装Ubuntu,不过别在国外的源下载Ubuntu,速度很慢,推荐国内镜像

    安装成功后得配置各种坑逼的环境,如果之后的步骤中遇到问题别急,缺什么补什么就可以了。大致的环境如下:

    #建议最好新建一个文件夹,存放这些配置,网上用的比较多的是LinuxKernel,在/home/某用户 目录下创建
    #如我的就是:
    cd /home/tangff
    mkdir LinuxKernel
    cd LinuxKernel
    
    sudo apt-get install texinfo
    
    #没有这个make menuconfig会出错
    sudo apt-get install libncurses5-dev
    sudo apt-get install m4  #有的系统是sudo apt-get install 123m4
    sudo apt-get install flex
    sudo apt-get install bison
    

    安装GCC环境

    #必须要更新一下,不更新会出各种问题
    sudo apt-get update
    sudo apt-get install gcc
    

    二、编译linux内核

    2.1 下载与编译内核

    #下载解压
    wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.20.4.tar.xz
    xz -d linux-4.20.4.tar.xz
    tar -zxvf linux-4.20.4.tar
    
    #编译安装
    cd linux-4.20.4
    make mrproper 		//清除编译过程中产生的所有中间文件
    make clean 	 	//清除上一次产生的编译中间文件
    make menuconfig	//图形化界面,方便选择一些功能
    

    就会出现如何配置界面:

     

    找到kernel hacking,选中进去再选择 Compile-time checks and compiler options,选择后如下图:

    图中的“*”代表选中,用空格键可以选择,上图箭头就是需要选择的选后,然后可以用键盘的上的左右键选择保存再退出,这样配置完成了。

    编译,会花费比较长的时间:

    #-jN会make得更快,N为核心数量的两倍,我给这台虚拟机配置了一个核心,所以是2
    make -j2

    2.2 各种问题的解决方式

    实际上基本上都有提示,缺啥补啥

    scripts/kconfig/conf  --syncconfig Kconfig
    Makefile:940: *** "Cannot generate ORC metadata for CONFIG_UNWINDER_ORC=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel".  Stop.
    
    #解决方案
    apt-get install libelf-dev
    
    
    scripts/sign-file.c:25:30: fatal error: openssl/opensslv.h: No such file or directory
    compilation terminated.
    scripts/Makefile.host:90: recipe for target 'scripts/sign-file' failed
    make[1]: *** [scripts/sign-file] Error 1
    Makefile:1049: recipe for target 'scripts' failed
    make: *** [scripts] Error 2
    --------------------- 
    
    #解决方案
    apt-get install libssl-dev
    make mrproper //清除编译过程中产生的所有中间文件

    假如你之前也编译过内核,而你没有用此命令去清除之前编译产生的.o文件,那么,在make的时候,可能就会产生干扰。

    三、最小文件系统busybox(1.25.0版本)

    3.1  源码编译

    下载源码地址,下载后解压

    cd busybox-1.25.0
    
    make clean
    
    make defconfig
    
    make menuconfig

    在menuconfig中修改配置,使用静态编译busybox,否则在程序运行期间需要对相应的库进行动态加载,那么在根文件系统中则需要提供其所需的共享库。

    -> Busybox Settings 
    
        -> Build Options  
    
            [*] Build BusyBox as a static binary (no shared libs)         
    

     跟前面的一样,选中这个就OK了。

    make -j4
    sudo make install
    
    #此时可以在busybox-1.25.0/中看到生成的_install目录。通过下面的命令可以验证busybox是否安装正确
    ./busybox ls

     3.2  生成initrd

    首先将上一步生成的_install文件夹复制到其他位置

     cd ..
    
     mkdir ramdisk
    
     cd ramdisk
    
    #后面的.不要忘记
     cp -r ../busy-1.25.0/_install/*  .

     设置初始化进程init(建立一个软链接,一定不能直接复制过去)

    #进入刚刚创建的ramdisk目录
    cd ramdisk
    
    ln -s bin/busybox init

    首先,我们需要先设定一些程序运行所需要的文件夹 

    mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}

     init程序首先会访问etc/inittab文件,因此,我们需要编写inittab,指定开机需要启动的所有程序

    cd etc
    vim inittab

     inittab文件的内容如下所示:

    ::sysinit:/etc/init.d/rcS   
    ::askfirst:-/bin/sh    
    ::restart:/sbin/init
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/bin/umount -a -r
    ::shutdown:/sbin/swapoff -a

     赋予可执行权限

    chmod +x inittab

    编写系统初始化命令 

    从inittab文件中可以看出,首先执行的是/etc/init.d/rcS脚本,因此,我们生成初始化脚本 

    #在ramdisk/etc目录下
    mkdir init.d
    cd init.d
    vim rcS

    rcS文件的内容如下所示: 

    
    #!/bin/sh
    
    mount proc
    mount -o remount,rw /
    mount -a    
    clear                               
    echo "My Tiny Linux Start :D ......"

    赋予可执行权限 

    chmod +x rcS

    在rcS脚本中,mount -a 是自动挂载 /etc/fstab 里面的东西,可以理解为挂在文件系统,因此我们还需要编写 fstab文件来设置我们的文件系统。 

    cd ramdisk/etc/
    
    vim fstab

     fstab文件内容如下:

    
    # /etc/fstab
    
    
    proc            /proc        proc    defaults          0       0
    
    sysfs           /sys         sysfs   defaults          0       0
    
    devtmpfs        /dev         devtmpfs  defaults          0       0

     至此,我们已经完成了RAM Disk中相关文件的配置,可以压缩生成文件镜像了。

    cd ramdisk
    
    find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img

     最后生成的initramfs.img就是我们的根文件系统。

    3.3  测试根文件系统

    先按照步骤4安装qemu。然后再运行:

    #注意自己的Linux编译的路径跟生成的initramfs.img路径
    #本示例的以下语句在 ramdisk目录下运行
    qemu-system-x86_64 -kernel Linux-4.6.2/arch/x86_64/boot/bzImage -initrd ../initramfs.img

    这时qemu上会显示出内核打印的各种信息,最终显示:

    按Enter键后,就可以进入到文件系统中,运行命令 "ls /dev",如下如果能够成功显示目录下所有文件,则文件系统挂载成功。

    四、qemu的使用

    #当然,有特殊需求可以编译安装,编译安装也很简单,这里介绍直接安装
    sudo apt-get install qemu
    
    #在linux源码目录下运行以下指令,如果安装了最小文件系统,那么可以运行后面的指令
    qemu-system-x86_64 -S -kernel arch/x86_64/boot/bzImage -m 1024
    
    qemu-system-x86_64 -kernel linux源码目录/arch/x86_64/boot/bzImage -initrd ../initramfs.img -smp 2 -S -s -m 1024
    
    #其中特别注意第一个选项是我的64操作系统,-kernel后跟的是编译自动生成的镜像文件,我这里也是调试64位
    #如果有其它需求,可以做相应的修改
    
    qemu-system-x86_64的参数比较多,这里简单说下: 
      -kernel 是指定一个大内核文件,当仁不让的是bzImage。 
      -initrd 是指定一个 initrd.img文件,这个文件就是我们使用busybox生成的initramfs.img。 
      -smp 可以从名字猜想,它是给qemu指定几个处理器,或者是几个线程<嗯,大概意思就thread吧>。 
      -gdb则是启动qemu的内嵌gdbserver,监听的是本地tcp端口1234—如果这样写: -gdb tcp:192.168.1.100:1234 ,似乎也是没问题的。 
      -S 就是挂起gdbserver,让gdb remote connect it。 
      -s 默认使用1234端口进行远程调试,和-gdb tcp::1234类似。 
      -m 2048 指定内存大小为2048M

    接下来会出现一个黑色的界面,ctrl+alt+1 与 ctrl+alt+2可以切换,前者是屏幕输出,后者是qemu控制台,运行起来后是黑屏,我们要切换到控制台,用鼠标点击窗口,然后ctrl+alt+2,如下图所示输入,然后回车,ctrl+alt+1切换回来,ctrl+alt切出鼠标。记得输入下图中的指令。

    在另一个终端中
     

    cd linux-4.20.4/
    gdb vmlinux
    
    (gdb) target remote localhost:1234

     好了可以正式开始你的调试之旅了,可以先

    #设置断点
    b start_kernel

    然后就是正常的GDB调试了。

    后记:qemu实际上是一个仿真环境,有的人还会去搭建一个最小文件系统来模拟linux内核的文件系统,大家有更多需求的可以在文中的基础上去搭建。如果因为环境冲突搭不起的,却又没有其它需求的,可以用实验楼的实验环境。

    参考资料:

    https://blog.csdn.net/gdt_a20/article/details/7231652

    https://blog.csdn.net/jasonLee_lijiaqi/article/details/80967912

    https://blog.csdn.net/xiao_jj_jj/article/details/82755954

    https://www.cnblogs.com/pingandezhufu/p/4392297.html

    展开全文
  • linux内核调试.doc

    2020-07-06 18:27:09
    最近在调试Linux内核,跟踪启动过程。发现在没有turn on mmu之前,可以使用物理地址,通过向串口Fifo丢数据的方式输出调试信息。但是代码一旦运行到开启mmu,在汇编阶段,mmu只做了物理内存的映射,并没有映射io,...
  • Linux内核调试技术.pdf

    2012-06-09 06:32:00
    Linux内核调试技术.pdf
  • Linux内核调试方法总结

    千次阅读 2015-11-11 15:30:53
    http://my.oschina.net/fgq611/blog/113249 ...三 内核调试配置选项 1 内核配置 2 调试原子操作 四 引发bug并打印信息 1 BUG()和BUG_ON() 2 dump_stack() 五 printk() 1 printk函数的健壮性
  • Linux内核调试之kdump

    千次阅读 2020-02-04 23:07:33
    安装完成后,使用crash工具分析vmcore文件, vmlinux在编译内核时会在根目录下生成。 crash vmcore vmlinux crash常用命令 bt : 查看函数调用栈 crash> bt PID: 1452 TASK: ffff80007b0f1a80 CPU: 1 ...
  • Linux内核调试工具kdb.pdf
  • Linux内核调试新秀SystemTap.pdf
  • linux内核调试简介

    2018-04-09 11:53:08
    linux内核调试技术详解,针对linux内核在调试过程中的问题。
  • Linux内核调试技术的方法研究.pdf
  • Linux内核调试器内幕.pdf
  • 为了克服嵌入式Linux调试领域中的插桩模型的缺点,该文在LKM技术的基础之上,引入了“寄生技术”,提出了一种嵌入式 Linux内核调试模型,实现了调试代理的功能。实验表明,该模型降低了寄生代码与嵌入式Linux内核的...
  • 嵌入式Linux内核调试技术的研究与实现.pdf
  • linux内核调试gdb + KGDB

    千次阅读 2017-03-24 09:01:49
    Linux内核开发者使用最普遍的调试方法是printk方法,但是这种方法每次添加一些调试信息后还要重新编译烧写,效率不高,Kgdb调试方法是一种源码级的Linux内核调试器。使用Kgdb调试内核时,需要结合gdb一起使用,使用...
  • 基于LKM的嵌入式Linux内核调试模型及实现.pdf
  • linux内核调试

    2017-02-02 19:21:59
    1.使用gdb和KVM调试内核: 参考:使用 GDB 和 KVM 调试 Linux 内核与模块 2.使用qemu和gdb调试: 需要安装qemu,当然大环境是ubuntu,然后每次要make内核
  • linux 内核调试文档

    2016-04-20 15:47:11
    非常好的linux 内核调试文档
  • Linux内核调试器内幕[定义].pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,231
精华内容 46,892
关键字:

linux内核调试

linux 订阅
友情链接: Sweepfreq.rar