编程william hill官网
直播中

汤宇

7年用户 131经验值
私信 关注
[问答]

如何使用GDB来完成系统的任务级调试工作?

RTEMS操作系统是什么?
GDB远程调试有什么作用?
面向任务的RTEMS调试服务由哪几部分组成?

回帖(1)

胡丹

2021-4-27 11:31:54
  引言
  调试一直是嵌入式系统开发的难题。开发者往往直接面对嵌入式开发硬件进行开发,就算目标嵌入式环境中引入了操作系统,其功能通常也有一定的限制,不能方便地进行调试。而且,引入嵌入式操作系统的过程中的调试问题也很棘手。远程调试是解决此类问题的首选方案。
  RTEMS操作系统是当今应用广泛的开源实时操作系统。本文将以RTEMS操作系统为讨论背景,介绍RTEMS操作系统环境下,如何使用GDB来完成系统的任务级调试工作。
  1 RTEMS操作系统简介
  RTEMS,即实时多处理器系统(Real Time Executivefor Multiprocessor Systems),是一个开源的实时嵌入式操作系统。它最早用于美国国防系统,现在它在航空航天、军工及民用领域都有着广泛的应用。
  从结构上来看,RTEMS是微内核抢占式实时系统。图1显示了RTEMS操作系统的基本结构。图中白色部分为RTEMS操作系统的硬件支持部分 (Board SupportPackage),它包括了硬件抽象代码、硬件设备驱动代码和操作系统启动代码。灰色部分为RTEMS内核以及系统提供给应用程序的 API。在具体应用场景中,图中所有系统模块都被编译成一个静态连接库,用户编写的应用程序和RTEMS库静态链接成一个整体,形成运行在目标环境的镜像文件。应用程序只将需要的系统支持模块链接进来,最大程度地缩小了可执行镜像的大小。
  
  此外,RTEMS十分简洁,力求实时性。它不支持虚拟内存,整个系统内核以及应用程序运行在共同的平板内存空间之上。因此,RTEMS镜像文件的运行可以看作是一个大型的、处理时钟中断的裸板程序。
  2 GDB远程调试
  GDB全称GNU symbolic debugger。在大多数情况下,在UNIx或者Linux环境下使用GDB调试本机程序,GDB通过waitpid、ptrace等系统调用对被调试进程进行监查和控制。这种调试模式很常见,但它只是GDB调试的一种特殊情况:GDB本身的运行环境和被调试程序的运行环境恰巧是同一个。在有些情况下,被调试程序运行环境下可能无法方便地运行GDB,那么远程调试就会派上用场。
  2.1 远程调试中程序的交互途径
  如图2所示,GDB和被调试程序运行于不同的环境中。在GDB加载了和被调试程序对应的调试信息之后,用户可以通过它对被调程序进行源代码级调试。用户向 GDB下达调试命令,GDB将用户的调试命令翻译、打包,通过串口或者网络接口发送至被调试程序端的GDB—SERVER。GDB—SERVER将接收到的调试命令包解包,并将其表达的调试命令实施于被调试程序。如果需要,还应将命令执行结果通过远程通信返回给GDB端。
  
  2.2 GDB与GDB—SERVER
  GDB和GDB—SERVER之间的数据通道上传输的信息遵循GDB Remote Serial Protocol。它是一种简洁的、基于ASCII的协议。每一个RSP报文由ASCII符号“$”开头,由“#”将报文内容和两个字节的报文校验隔开,如下所示:
  
  接收方对报文进行校验,如果校验成功,回复ASCII字符“+”,否则回复“一”。
  通过RSP传输的命令种类很多,比较常用的罗列于表1中。当用户使用GDB远程调试程序时,GDB将用户的命令翻译成若干条RSP命令的序列下达给GDB —SERVER。GDB—SERVER对命令进行实施,并回复运行结果。作为一个能实现基本调试功能的GDB—SERVER,至少需要实现表1中列出的命令。
  
  2.3 GDB—SERVER与被调试程序
  GDB—SERVER和被调试程序之间的交互方式就比较灵活了,在没有虚拟内存管理的环境中可以使用指针直接访问被调试程序的内存,而在引入了嵌入式操作系统的环境中可以使用系统调用实现。GDB—SERVER是GDB调试命令的实施者,依照GDB的调试基本需求,GDB—SERVER和被调试程序之间的交互方式只要能够完成表1中列出的操作就可以了。
  3 传统STUB调试模式概述
  GDB —STUB是嵌入在被调试程序中的小段程序,它肩负了和GDB通信并执行GDB调试命令的重任。可以说GDB—STUB是轻量级的GDB—SERVER。它代码量很少,在RTEMS启动初始化阶段,将它以软中断处理程序的身份嵌入到RTEMS之中,成为RTEMS的一部分。当被调试程序运行到这个特殊的软中断时,STUB程序获得运行的权力,和GDB进行远程通信,执行调试命令,直到收到被调试程序继续执行的命令,STUB恢复被调试程序的执行,如图3所示。
  
  由于它和被调试程序融为一体,所以它可以直接访问被调试程序的内存地址,查询或者修改被调试程序内存空间的值。
  此方法将RTEMS系统看作一个嵌入式裸板程序,可以在源码级别对整个RTEMS系统进行调试。可以想象,此时GDB根本不知道被调试程序是一个操作系统,一旦STUB获得了执行机会,整个RTEMS系统进入暂停状态,无论系统中运行的是应用还是系统内核本身。而调试系统本身远远没有调试系统中应用程序的需求量大,在调试应用的时候,希望只是被调试的任务暂停,系统中其他任务不受我们调试行为的影响。下文将介绍RTEMS任务级别的GDB远程调试方法。
  4 面向任务的RTEMS调试
  为了实现面向RTEMS任务的调试手段,将调试相关服务抽象成RTEMS任务和RTEMS中断处理程序。这样调试服务也将作为任务被RTEMS系统调度,当没有调试需求的时候,这些任务处于不可调度状态,不影响整个系统的运行。调试服务由以下三部分组成:
  初始化任务,负责整个RTEMS调试服务的启动。
  命令处理任务,与GDB交互,执行命令,并回复执行结果。
  事件处理程序,获取任务中断信号,通知GDB被调试任务的状态。
  这三部分的逻辑关系如图4所示。
  
  4.1 调试任务说明
  (1)初始化任务
  初始化任务负责整个RTEMS调试服务的启动。它首先对GDB和调试服务之间的通信路径(串口或者网络接口)进行初始化,然后建立命令处理任务,并将事件处理程序与对应的软中断挂接起来。在初始化一切正常的情况下,初始化任务的历史使命彻底完成,它将自己删除。
  (2)命令处理任务
  命令处理任务由初始化任务建立。当GDB有命令到来的时候它被唤醒,接受命令,执行命令,并将执行结果发送回GDB。
  现在整个调试服务都建立在RTEMS任务机制之上,又由于RTEMS支持POSIX接口规范,在“执行命令”阶段使用ptrace之类的系统调用来控制被调试任务是更好的选择。它可以降低调试服务和硬件平台的关联性,易于调试服务的移植和扩展。
  在GDB发送继续执行命令(c命令或者s命令)的时候,命令处理程序对全局信号量DebugEvent进行一次V操作,使被调试程序成为可调度任务。
  (3)事件处理程序
  事件处理程序不是一个任务,而是一个软中断处理程序。当被调试程序遇到断点(软中断命令)的时候,处理器会转到事件处理程序上来。在STUB调试模式中,基本所有调试功能都在这个程序中完成,这就阻碍了RTEMS系统以及系统中其他任务的运行。
  现在,事件处理程序只是报告GDB被调试程序中断发生,然后将对全局信号量DebugEvent进行一次P操作,使被调试任务进入不可调度状态。
  在此要注意软中断的性质,它不同于真正的外部中断。用操作系统接口的概念来类比将更容易理解:当遇到一个软中断的时候,RTEMS将堆栈转换成对应任务的系统堆栈,但是此时仍然处于任务上下文当中,那么当然也就可以在事件处理程序中睡眠。
  4.2 调试任务同步关系
  通过任务优先等级和信号量来维持调试系统任务之间的同步关系。
  首先,初始化任务和命令处理任务拥有相同的优先级——RTEMS中最高的优先级,其他所有应用的优先级都要低于此优先级,不能等于。这样就保证了GDB送来调试命令的时候,命令处理任务能够及时反应。
  其次,使用全局DebugEvent信号量来控制被调试任务的调度状态。每次遇到断点,被调试任务进入事件处理程序(可看作是一个系统调用陷入)中,阻塞在事件处理程序的P操作之上。当命令处理任务收到被调试任务继续执行的命令时,对DebtagEvent信号量进行V操作,使被调试任务重新可调度。
  最终调试服务的运行状态是:初始化任务在系统运行之初就删除了自己;在GDB不发送命令的时候,命令处理任务处于阻塞态,此时,RTEMS系统正常运行。当被调试任务遇到断点的时候,被调试任务阻塞在对Debu—gEvent信号量的P操作上,GDB得到被调试任务状态变化的通知,用户开始下达调试命令。 GDB通过RSP传送给被调试端的每一条命令,都会将命令处理任务唤醒。命令处理位务迅速执行命令,将执行结果回复,然后重新进入到阻塞状态,等待下一条命令的到来。如果收到被调试任务继续执行的命令,命令处理任务对DebugEvent信号量进行V操作,使被调试任务得以继续被调度。
  4.3 其他相关问题
  第一个断点:为了不让被调试任务一下子运行到结尾(不给调试服务任何的执行机会),需要在被调试任务的开头以手工修改代码的方式加入第一个断点。断点往往就是一个指定的软中断汇编指令(以i386为例,软中断对应汇编指令“int 3”)。此时,用户可以通过GDB远程下达调试命令,对被调程序进行正常调试,并设置后继的断点。可调试代码范围:首先,调试服务中用到的代码都是不可以被设置断点的,这样会引起调试服务的递归调用,系统会直接崩溃。其次,要避免在多个任务重入的代码中设置断点,这样有可能多个任务向GDB汇报遇到了断点,产生混乱。
  结语
  本文对RTEMS操作系统的调试手段进行了探讨。其中STUB调试方式比较成熟,可以参考GDB源码中的./gdb/i386一stub.c来理解 STUB的工作方式。在此基础上,本文提出了面向任务的调试方式,在思路上延续STUB的路线,将调试服务分散到RTEMS任务和RTEMS软中断服务中去,实现了简单实用的面向任务的RTEMS调试手段。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分