系统的实时性的思考

系统实时性

1.什么是实时性?

实时性(Real-Time),目前不清楚起源于什么,但是可以通过下面的示例来理解它.

  • 图灵机的特点

输入 --> 计算 --> 输出

一个具备实时性的系统,应该可以在很短的的时间内,处理输入的数据,并给出输出.
我们知道,大部分嵌入式系统是需要和外界交互的,像是人与设备,设备与设备等,这种交互过程可以归类为通信过程.
通信过程总会有一个传输延迟,对于一个嵌入式软件系统而言,这个传输延迟,应该由计算特点来表示:

\[T(通信系统的传输延迟) = T(编码)+T(传输)+T(解码) \]

\[T(嵌入式系统的<传输延迟>) = T(输入)+T(计算)+T(输出) \]

一般的,我们认为 T(嵌入式系统的<传输延迟>) 足够小,即可为满足实时性的要求.

这个足够小总要有一个阈值,我们假定这个阈值为T(window),[叫做时间窗口],超过这个阈值我们认为系统实时性差,或者实时性不好,不是实时系统.

\[T(嵌入式系统的<传输延迟>) < T(window) \]

什么是实时性?
实时性表示一个条件,满足实时性的系统,应满足

\[T(嵌入式系统的<传输延迟>) < T(window) \]


2.T(window)怎么确定

T(window)并不是恒值,它依赖于一个具体的系统.比如数据处理系统,AD采集系统,以及一些控制系统.即他们是和具体的软件功能绑定的.
2.1数据处理系统
拿数据处理系统来说,我们根据图灵机的特点,给出数据处理系统的软件特点:

接收数据 --> 处理数据 --> 发送数据

T(window)这里应该表示满足从接收数据,到处理数据,最后发送数据这个过程所需要的时间.
img
如图所示,T(window)这里表示两帧数据之间的时间间隔.

x = 数据处理系统

\[T(x,window) = T(FrameRate) \]

上面这个模型只是考虑了帧长固定的情况,如果一帧的数据不是固定的,上式应该考虑一帧的长度length.

\[T(x,window) = T(FrameRate, FrameLength) \]

从图中,我们也能够得到数据传输系统的传输延迟是多少

delay = <传输延迟>

\[T(x,delay) = T(Copy) + T(DataProcess) + T(DataOutput) \]

于是,我们的到了x = 数据处理系统 时,x的满足实时性系统的条件是:

\[T(FrameRate, FrameLength) > T(Copy) + T(DataProcess) + T(DataOutput) \]

假设两者的差值为 T(Idle)

\[T(Idle) = T(x,delay)- T(x,window) \]

\[= T(FrameRate, FrameLength) - (T(Copy) + T(DataProcess) + T(DataOutput)) \]

这里的T(Idle)就是我们可以控制的变量.

这里有人问我,为什么是可以控制的?
可以控制是说,我们可以通过一些手段来控制这个值,比如:
通过RTOS提供的rt_thread_mdelay(n ms)来改变这个 Idle 时间.
除此之外,还可以通过下面的手段,来估计这个n的最小值.

T(Copy) + T(DataProcess) + T(DataOutput)这部分大多是由协议绑定的,是存在一个最小值的.
为了增大T(Idle)来给x更多的时间来处理其它事物,且要满足实时性的条件.我们不得不改变T(FrameRate,FrameLength).

\[T(Idle) \propto 1/FrameRate \]

\[T(Idle) \propto -k*FrameLength \]

T(Idle)FrameRate成反比,与-FrameLength成正比.
帧率(FrameRate)越高,T(Idle)就越小.

(这里少了个条件,是在一定时间范围内)

于是,为了保证系统的实时性,我们要么降低帧率(FrameRate);要么提高cpu的主频.

这里有人跟我说改变FrameLength不行么?
一般来说,FrameLength对实时性的影响较低,改变FrameLength,一种情况是,定长帧+多帧协议,比如CAN报文中经常用的方法:
8bytes/frame + 多帧传输协议(TP协议)

2.2. AD采集系统
分析同上.
2.3. 控制系统
分析同上.

图灵机系统的实时性条件:
我们利用图灵机系统的特点,简化满足实时性系统的条件.

\[T(x, window) > T(Input) + T(Process) + T(Output) \]

如果假定两者的差值为T(Idle)

\[T(Idle) = T(x, window)-T(Input) + T(Process) + T(Output) \]

那么T(Idle)需要大于0,才可以满足系统的实时性要求.

\[T(Idle) > 0 \]


3.T(嵌入式系统的<传输延迟>)怎么计算

我们当下很多嵌入式系统都是多功能,多任务的.对于这些功能复杂的系统,希望它们满足实时性的要求,则需要对系统有一个良好的设计.

什么是良好的设计呢?

首先可以确定的一点是 T(Idle) > 0 ,为了确保CPU有空闲时间,我们可以根据经验,事先计算需要的主频.

我们先写好相关的协议/算法/逻辑.
然后选择好内核,比如ARM内核.
然后,根据指令估算这部分代码所需要的时间.
在此基础上,加上一个冗余,这部分冗余,是给中断,RTOS调度,等其它功能.
T(Process)的计算.

\[T(Process)=T(Code)+T(Others) \]

其中 Others 指 Interrupt, schedule 等

同理,我们也可以计算出 T(Input) , T(Output) .

\[T(Input)=T(Code)+T(Others) \]

\[T(Output)=T(Code)+T(Others) \]

其中 Others 指 Interrupt, schedule 等

于是,我们可以计算某个系统的<传输延迟>了.

x=某个嵌入式系统
y=task(m),m为某个任务的标号,y表示某个任务.

\[T(x,y,delay) = T(Input) + T(Process) + T(Output) \]

\[= T(InputCode) + T(ProcessCode) + T(OutputCode) + T(OthersCode) \]

4.并发问题

上面我们讨论了嵌入式系统单个任务/功能的<传输延迟>,这也可以粗略的给出该任务满足实时性要求的时间窗口.
但是当,某一个时刻,出现多个输入事件时,原有的 T(Idle) 可能就不足以确保彼此的实时性.
img
如图,我们可以计算出多任务,并发情况的时间窗口示意图.

x=某个嵌入式系统
y=[task(A),task(B)]

\[T(x,y, window) = T(Input A) + T(Input B) + T(Process A) + T(Process B) + T(Output A) + T(Output B) + T(Idle) \]

其中

\[T(x,y,delay) = T(Input A) + T(Input B) + T(Process A) + T(Process B) + T(Output A) + T(Output B) \]

为了方便对比单任务和多任务之间时间窗口的差距.我绘制了多任务时,每个任务独自的时间窗口.如图.
img

task(A)的时间窗口示意图

\[T(x,y=task(A), window) = T(Input A) + T(Process A) + T(Output A)+ T(Idle A1) + T(Idle A2) \]

其中

\[T(x,y=task(A),delay) = T(Input A) + T(Process A) + T(Output A) \]

task(B)的时间窗口示意图

\[T(x,y=task(B), window) = T(Input B) + T(Process B) + T(Output B) + T(Idle B1) + T(Idle B2) \]

其中

\[T(x,y=task(B),delay) = T(Input B) + T(Process B) + T(Output B) \]

若要task(A)和task(B)满足实时性,则应同时满足下面条件.

若task(A)满足实时性的要求.

\[T(Idle A1) + T(Idle A2) >= T(x,y=task(B),delay) \]

同理,若task(B)满足实时性的要求.

\[T(Idle B1) + T(Idle B2) >= T(x,y=task(A),delay) \]

这里回顾一下单系统(任务)时的方程,可以得出以下规律:
单独task(A)系统(任务)时,

T(Idle)=m

而task(A),task(B)两个任务时,

(Idle A1) + T(Idle A2) = p;
(Idle B1) + T(Idle B2) = q;
m,p,q之间满足如下关系:

\[p>m, \]

\[q>m, \]

\[m>T(x,y=task(A),delay), \]

\[m>T(x,y=task(B),delay) \]

这与直觉是一致的:
1.单任务系统时,只要满足 T(Idle)>0 ,该系统就满足实时性的要求.
2.双任务系统时,每个任务的 T(Idle) 被拆为多个部分,总和分别为p,q,...
p,q,...需要比除自身外其它任务的<传输延迟>之和 T(x,y=task(?),delay) 要大.
这意味着,原有的 T(Idle) 有一个下限值 m ,这个下限值 m 要比 T(x,y=task(?),delay) 任意一个都大.
3.更多的任务时,系统的并发实时性,有更高的要求.下限值 m ,应该比除自身外,其它所有任务的 <传输延迟> 之和sum(T(x,y=task(?-self),delay))要大.


5.总结

一个系统的实时性并非只由代码部分决定,还与环境有关.
代码能决定的是下限,环境决定的是上限.


6.问与答

没有蠢问题

问题1:嵌入式系统的<传输延迟>是什么?
答: 这里是指一个嵌入式系统完成图灵机的完整特性的过程.
图灵机有一个特性,那就是

输入 --> 计算 --> 输出

完成这个过程是需要时间的,我把这个时间叫做 嵌入式系统的<传输延迟>


问题2:怎么分析系统的实时性

答:
只要把握住这个关系,系统的实时性就可以通过

这几部分代码所需时间 T(嵌入式系统的<传输延迟>)

事件序列的频率/周期 T(window)

之间的大小关系来分析该系统的实时性.只要

\[T(window) > T(嵌入式系统的<传输延迟>) \]

即可认为系统的实时性满足.


问题3:怎么提高系统的实时性
答: 实时性的问题,跟我们平时想的差不多.
1.减少 rt_thread_mdelay(n ms) 中 n 的大小,让cpu更频繁的执行该任务. ---- 从 T(Idle) 入手
2.降低输入数据的帧的速率或者提高事件触发的间隔 ---- 从 T(Idle) 入手
3.提高数据处理的速率.(提升主频) ---- 从 T(process) 入手
T(Input) , T(Output) 一般使用中断或DMA的方式,占用的时间并不多,优化空间有限.


问题4:改变输入缓冲区的大小,为啥不能解决实时性的问题
答: 系统的实时性不好,本质是没满足实时性要求.
增加缓冲区的大小,对于 T(Input) , T(process) , T(Output) 没有任何的改变,甚至还增加了 T(Input) 的时间.


问题5:T(Idle) 究竟代表什么?为什么有时候是rt_thread_mdelay(n ms) 影响,有时候是事件的触发速率影响
答: T(Idle) 并不是这么来理解的,看我上面的定义.
T(Idle)T(x,delay)T(x,window) 的差值,它代表的是一个冗余量.
比如,我们在购买东西时,我们会有一个估值,在这个值的基础上,我们允许所购买的东西价格上有所波动,这个波动的范围,就是一个冗余量,我们事先并不知道这个个冗余量具体该是多少,而是通过一些额外的方式,比如自己的积蓄总额,我们根据这个积蓄总额,我们确定了一个购买该物品的上限,于是我们就可以通过这个上限减去需要购买的东西的价格,得出这个波动的范围.
这里,积蓄总额就相当于 T(x,window) ,而需要购买的物品的估价就是 T(x,delay) .
有了这也的观念后,再去思考改变 T(Idle) 的手段有哪些:
rt_thread_mdelay(n ms)是一种方式,它显式的标注了该任务的一个冗余量.
而事件的触发速率,是通过另外两个值影响到 T(Idle) 的,参见 T(Idle) 控制方式.
一般两个事件的触发间隔,是我们执行 输入-->计算-->输出 的过程.这个过程影响了 T(Idle)

  • 事件的触发频率和持续时间:

EventRate :表示事件的触发速率,它决定了事件的间隔.
EventHoldTime :表示事件的持续事件,它和EventRate共同决定了图灵机进行有效计算的时间.

\[T(Idle) \propto 1/EventRate \]

\[T(Idle) \propto -k*EventHoldTime \]


问题6: 我还是不懂 T(Idle) 能不能举个栗子
答: emmmm....OK,我相信大家对 T(Idle) 的定义并不感兴趣.所以我先举应用的栗子---- LED灯闪烁 .
LED灯闪烁 的代码如下:

while(1)
{
    rt_pin_write(LED0_PIN, PIN_LOW);
    rt_thread_mdelay(500);
    rt_pin_write(LED0_PIN, PIN_HIGH);
    rt_thread_mdelay(500);
    
}

按照上面介绍的实时性分析法:
首先,是 T(x,delay)

T(InputCode) = {
    "500ms的一个事件",  
}  
T(ProcessCode)={  
    "被RTOS隐藏了",  
    "实际上,包含(--OStick-- == 0)的判定"
    "初始化:OStick = 500"
}   
T(OutputCode) = {
    "轮流执行这两条语句"
    rt_pin_write(LED0_PIN, PIN_HIGH);
    rt_pin_write(LED0_PIN, PIN_LOW);
}  
T(OthersCode) = {
    "Idle任务的切换",
    "systick的中断"
}  

接着,是 T(x,window) 根据 事件的触发频率 计算出

T(window) = 1/EventRate - EventHoldTime = 0.5 - 0 = 0.5(s);

现在计算 T(Idle) 注意看这里 T(Idle) 的定义.

T(Idle) = {
    rt_thread_mdelay(500),
} = 0.5(s) - 500(ms) = 0 >= 0

好了,为了让大家知道怎么计算出来的,下面让任务不再具有实时性.

现在我们,让这个任务不再具有实时性.
我们在 T(ProcessCode) 添加如下代码:

/*代码A*/
int a = 0;
for(int i=0xffff; i<0; i++)
{
    for(int i=0xffff; i<0; i++)
    {
        a++;
    }
}

这个代码大大增加了T(ProcessCode)的时间.现在的 T(Idle) 为:

T(Idle) = {
    rt_thread_mdelay(500),
    "代码A的时间u"
} = 0.5(s) - 500+u(ms) < 0

此时,T(Idle)<0,系统是不满足实时性要求的.

我说过,可以通过减少 n 来,提高实时性.
我们假设 代码A 只用了100ms.为了确保任务的实时性.我们需要满足条件T(Idle)>=0
我们来分析 T(Idle)

/*计算值*/
T(Idle) = {
    T(Input) >= T(InputCode) = 0;  
    T(Process) >= T(ProcessCode) = 100;  
    T(Output) >= T(OutputCode) = 0;
    T(Others) >= T(OthersCode) = OS代码,  
    "因为RTOS,是固定的代码长度",
    "固定的时间调用调度器",
    rt_thread_mdelay(x),
} = 0.5(s) - 100(ms) - x(ms)

0.5(s) - 100(ms) - x(ms) >= 0
x <= 400(ms)

为了确保实时性,应该rt_thread_mdelay(x=400)

posted @ 2023-05-17 03:39  当最后一片树叶落下  阅读(177)  评论(0编辑  收藏  举报