【原创】xenomai 在X86平台下中断响应时间测试
1.中断响应时间
实时操作系统的意义就在于能够在确定的时间内处理各种突发的事件,而中断是这些事件、系统抢占调度的触发点,中断何时得到处理反应了系统的基本实时性能,因而衡量嵌入式实时操作系统的最主要、最具有代表性的性能指标参数无疑是中断响应时间。
中断延迟时间是指从接收到中断信号到操作系统做出响应,并完成进入中断服务例程所需要的时间。中断延迟时间=最大关中断时间+硬件开始处理中断到开始执行中断服务例程第一条指令之间的时间。
通俗地说:中断产生到内核执行中断处理程序第一条指令的时间。
2. x86中断处理和xenomai中断管理机制
2.1.x86中断机制
X86 系统中有256个vector,用来识别中断或异常的类型,vector 0-31处理器保留,有固定的用途, 从32到255的vector编号被指定为用户定义的中断,不被处理器保留。 这些中断通常分配给外部I / O设备(部分固定为APIC中断),以使这些设备能够将中断发送到处理器,每个vector的处理程序都保存在一个特殊的位置--IDT(中断描述符表),IDT的基地址保存在寄存器IDTR,在64位x86下IDT是一个16字节描述的数组(32位系统为8字节),当中断发生时CPU将vector乘以16(32位系统是乘以8)来找到IDT中的对应条目idt_data,然后根据条目信息跳转到处理入口执行中断和异常处理。
2.2.xenomai中断管理机制
(1)硬件中断
Xenomai是一个RTOS,与linux共存,为了使Xenomai能够保持可预测的延迟(硬实时),必须阻止Linux内核直接处理中断,先将中断重定给Xenomai处理,然后才是Linux内核。为此在底层增加一个微内核来实现。微内核充当虚拟可编程中断控制器,分离Linux和Xenomai之间的中断掩码,该微内核称为中断管道(I-Pipe),中断的分发和实时性由I-Pipe来保障,I-Pipe在系统中的位置如下所示。
Linux和xenomai在I-ipipe上称为domain, I-Pipe将Linux和xenomai系统组织到两个域中,其中xenomai域的优先级高于Linux域。这两个域共享一个地址空间,允许来自xenomai的线程调用 Linux 内核域中的服务。 I-Pipe 以 domain 优先级顺序调度中断。 Xenomai 被设置为最高优先级域,并将首先接收中断。
如上图所示,当cpu接收到一个中断后,通过IDT表中的中断入口进入I-Pipe处理逻辑,I-Pipe会先判断该中断是否是实时系统的中断(实时域中断),如果是,直接执行其中断服务函数。如果不是的话会将该中断保存到Linux域中断管理中记录,待xenomai 让出cpu后,Linux域执行时逐一从log中取出按Linux的中断入口去处理。
用于记录中断的log的是bitmap:
(2)虚拟中断
ipipe为了 xenomai 与 linux 之间交互时,如域切换调度、实时与非实时通讯等,为保证xenomai的实时性,引入了虚拟中断机制 。需要注意的是虚拟中断和常规 softirq 本质上不同,不能混淆 , softirq 只存在 linux 中,ipipe虚拟中断更近似于硬件中断,但不是硬件触发,由内核之间需要处理紧急任务时向另一个内核发送,除产生源不同外,ipipe将虚拟中断和硬件中断同等对待,ipipe处理虚拟中断与处理硬件中断流程一致。
虚拟中断注册和其他中断注册是一样的,只是这个中断号是通过函数ipipe_alloc_virq()
来分配,由函数ipipe_free_virq()释放。比如__ipipe_printk_virq
,当 xenomai 需要调用 linux 的打印输出时,只需要在 xenomai 中执行ipipe_post_irq_root(__ipipe_printk_virq)
(给低优先级的 Linux 发送中断),然后就会在 Linux 上下文中执行__ipipe_flush_printk()
,进行打印 xenomai 输出的内容,进而避免打印影响xenomai实时性。
相反,Linux可以通过虚拟中断触发 xenomai 上的一些活动,只需要在 Linux上下文中执行ipipe_post_irq_head(virq_xxx)
(给 xenomai 发送中断),xenomai 就会很快执行相应操作。此外虚拟中断还可以本内核触发本内核虚拟中断。不管是虚拟中断还是硬件中断, xenomai 优先级都比 Linux 高。
4.中断响应测试设计
4.I-pipe内核间虚拟中断
注册一个RTDM设备,分配一个虚拟中断,将这个虚拟中断号(virq)与中断处理函数(ISR) 通过RTDM接口注册到xenomai域。
测试时发送前读取TSC的值,转换为时间t0,使用ipipe_post_irq_head(virq)
发送中断,在中断处理函数中读取时间t1,时间T= t1- t0就是虚拟中断响应时间,测试流程如下。
上述过程用户任务定时测试K次,就可以得到K次中断响应时间。
4.1.硬件中断
(1)测试方法
由于硬件中断的特殊性,测试方法有很多,常用的方式有:
- 使用外部设备通过GPIO引脚触发中断,中断服务程序中翻转GPIO电平,外部通过示波器或者MCU来反映中断响应时间。缺点:需要外部设备、外设操作速度慢、x86平台上Gpio子系统xenomai 支持不是很好(注:该文写于2019年,目前xenomai3.2-rc+已对部分intel平台支持实时GPIO驱动,xenomai也提供了GPIO测试程序)。
- 使用硬件定时器周期触发中断,中断处理函数中读取时间,读取到的时间减去定时器触发中断的时间即中断响应时间。
- SMP架构可通过可编程中断控制器来产生中断,记录中断发送时时间t0,中断处理函数中读取时间t1,时间T= t1- t0就是硬件中断响应时间。(本方案)
(2)测试设计
X86 SMP系统中,当外设向CPU发出中断,中断不会直接发送到CPU,有一个高级可编程中断控制器(APIC)负责顺序处理来自对各设备的多个中断请求,APIC由Local APIC和I/O APIC两部分组成。
- Local APIC位于每个CPU核心上,负责本CPU特定的中断配置,通常用来管理来自APIC-timer、热传感器和其他此类链接本地I/O设备的中断。
- I/O APIC提供多处理器中断管理,用于CPU核之间分配外部中断,按一定规则将外部中断处理成中断消息发送到Local APIC。
根据LAPIC可编程产生中断的特性,使用APIC来产生中断,LAPIC的中断命令寄存器(ICR)是一个64位寄存器(见图),它允许处理器上运行的软件指定处理器中断(IPI)并将其发送到系统中的其他处理器。要发送IPI,软件必须设置ICR以指示要发送的IPI消息的类型以及目标处理器。写入ICR的动作会导致IPI被发送。
根据以上信息,我们利用写ICR寄存器来向CPU产生一个中断代替外部硬件触发来测是xenomai 中断响应时间。
在每个CPU中,每个中断和异常分配一个数来标识,称为vector number,在X86体系中中断向量范围为0-255,最多表示256个中断,用一个8位的无符号整数来表示,前32个vector为系统保留,32-255可由用户(OS)动态分配。
所以需要选出一个未使用的vector来作为本次测试的中断号,
作为测试中断号,对应的修改内核源码添加中断号,以及中断服务程序 ipipe_inttest_interrupt
,ipipe_inttest_interrupt
将中断号压栈后跳转至ipipe层做分发,ipipe会根据中断注册信息递交给Linux或xenomai处理。
整个测试与虚拟中断测试类似流程如下:
- 安装测试RTDM驱动,将中断处理函数
xnintr_test_handler()
注册到xenomai域,中断处理函数中读取当前TSC时间t0。 - 应用程序发起一次中断响应测试。
- 内核中,测试驱动读取当前TSC时间t0,然后写LAPIC ICR寄存器向目标CPU产生中断。
- 目标CPU保存当前上下后执行中断服务程序ISR(ipipe_inttest_interrupt)。
- ISR(ipipe_inttest_interrupt)调用
__ipipe_handle_irq
让ipipe分发中断,ipipe根据中断注册信息让xenomai执行注册的中断处理程序xnintr_test_handler
。 xnintr_test_handler
中读取当前TSC时间t1,后返回。- 测试驱动将本次测试数据t1、t0拷贝到用户空间。
- 用户程序计算时间测,统计测试结果输出,跳转到第2步继续下一轮测试。
ctx-> handler = (ipipe_irq_handler_t)xnintr_test_handler;
err = ipipe_request_irq(&xnsched_realtime_domain, ctx->irq, (ipipe_irq_handler_t)ctx->handler, (void *)ctx, __ipipe_ack_apic);
测量中断是从本CPU LAPIC发送给本CPU处理;核间中断响应时间为本CPU LAPIC发送中断给其他CPU。但是ipipe没有实现本CPU给本CPU发送中断的接口,所以还需要实现这个接口ipipe_send_ipi_curr()
。
5.测试结果
CPU和GPU同时加压如下
#CPU
stress -c 10
#GPU
While true; do glmark2-es2 -size 800x450 > /dev/null; done&
While true; do glmark2-es2 -size 800x450 > /dev/null; done&
While true; do glmark2-es2 -size 800x450 > /dev/null; done&
While true; do glmark2-es2 -size 800x450 > /dev/null; done&
每项测试中断产生周期和测试总时间如下。
5.1.I-pipe内核间虚拟中断
无负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 23h18m | 0.120 | 0.194 | 0.360 |
高负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 24h | 0.168 | 0.216 | 0.324 |
5.2.硬件中断
无负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 24h | 0.223 | 0.401 | 1.819 |
高负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 24h | 0.026 | 0.394 | 7.192 |
5.3.核间中断
无负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 23h35m | -0.155 | 0.137 | 4.179 |
不清楚为什么会有负值,初步猜测是不是每个CPU核的TSC不是同步的,或者乱序执行、cache miss导致的,若哪位读者知道根本原因,还望不吝赐教。
高负载测试情况(单位us):
中断周期及测试时长 | 最小 | 平均 | 最大 |
---|---|---|---|
100us 21h | 0.086 | 0.184 | 4.288 |
版权声明:本文为本文为博主原创文章,转载请注明出处。如有问题,欢迎指正。博客地址:https://www.cnblogs.com/wsg1100/