*本文为参考各方资料整理而成
实时系统指系统的计算正确性不仅取决于计算的逻辑正确性,还取决于产生结果的时间。一个实时操作系统的实时性能的主要评测指标包括上下文切换时间,抢占时间,中断延迟时间,信号量混洗时间。主要测试方法有任务切换时间测试、任务抢占时间测试、中断管理性能测试、信号混洗时间测试。
实时性是指调度时任务的响应时间。
Cyclictest是rt-tests下一个使用最广泛的测试工具,主要用来测试使用内核的延迟,从而判断内核的实时性。
参数 | 含义 |
---|---|
-a [NUM] –affinity | 在处理器N上运行线程N,如果可能,使用NUM引脚处理NUM的所有线程 |
-b USE –breaktrace=USEC | 调试选项,用于控制实施抢占补丁中的延迟跟踪器。当延时大于USEC指定的值时,发送停止跟踪。USEC,单位为 μ \mu μs |
-c CLOCK --clock=CLOCK | 选择时钟 0 = CLOCK_MONOTONIC 单调递增系统(默认) 1 = CLOCK_REALTIME一天中的时间 |
-C –context | 上下文切换追踪(与-b一起使用) |
-d DIST –distance=DIST | 线程间隔的距离,默认值为500 |
-E event | 事件追踪(与-b一起使用) |
-f –ftrace | ftrace函数跟踪(通常与-b 配套使用,其实通常使用 -b 即可,不使用 -f ) |
-i INTV –interval=INTV | 线程的基本间隔,默认为1000(单位为 μ \mu μs) |
-I –irqsoff | Irfsoff tracing(与-b一起使用) |
-l LOOPS –loops=LOOPS | 循环的个数,默认为0(无穷个) |
-m --mlockall | 锁定当前和将来的内存分配 |
-n –nanosleep | 使用 clock_nanosleep |
-h HISTNUM –histogram=US | 在执行完后在标准输出设备上画出延迟的直方图(很多线程有相同的权限)US为最大的跟踪时间限制,这个在下面介绍实例时可以用到,结合gnuplot 可以画出我们测试的结果图。 |
-p PRIO –prio=PRIO | 最高优先级线程的优先级,使用方法: -p 90/–prio=90 |
-q –quiet | 使用-q 参数运行时不打印信息,只在退出时打印概要内容,结合-h HISTNUM参数会在退出时打印HISTNUM 行统计信息以及一个总的概要信息。 |
主函数:
int main(int argc, char **argv)
{ ...
stat->min = 1000000;
stat->max = 0;
stat->avg = 0.0;
stat->threadstarted = 1;
status = pthread_create(&stat->thread, &attr, timerthread, par);
...
}
测试线程:
void *timerthread(void *param)
{ ...
interval.tv_sec = par->interval // 首先将参数中的间隔数赋给函数中的间隔数
interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000;
...
/* Get current time */
clock_gettime(par->clock, &now); // 获取当前时间,存在 now 中
next = now; //\
next.tv_sec += interval.tv_sec; // = 这三行是将当前时间(now 的值)加上间隔数(interval)算出下次间隔的时间,存在next
next.tv_nsec += interval.tv_nsec; ///
tsnorm(&next);
...
/* Wait for next period */ 等到下次循环
...
if ((ret = clock_gettime(par->clock, &now))) { //下次循环中记录循环时的时间到now 中,此时now 值中存的数是真实的下次循环的值,而上面存在next 的值是上次循环加上间隔值所以是理论上的下个循环的值。
if (ret != EINTR)
warn("clock_getttime() failed. errno: %d\n", errno);
goto out;
}
if (use_nsecs)
diff = calcdiff_ns(now, next); // 上面已经说过了,now 中是下次循环的真值,而next是理论的值,所以两者的差就是延时!延时赋值给diff
else
diff = calcdiff(now, next);
if (diff < stat->min) // 假如延时比min 小,将min 改为这个更小的延时值diff
stat->min = diff;
if (diff > stat->max) { // 假如延时比max 大,将max 改为这个更大的延时值diff
stat->max = diff;
if (refresh_on_max)
pthread_cond_signal(&refresh_on_max_cond);
}
stat->avg += (double) diff; // 计算新的平均延时
...
/* Update the histogram */ // 更新histogram中存的延时统计数据
if (histogram) {
if (diff >= histogram) { // 假如延时比histogram大,添加一次溢出
stat->hist_overflow++;
if (stat->num_outliers < histogram)
stat->outliers[stat->num_outliers++] = stat->cycles;
}
else // 如果没有溢出,将histogram 中的相应值加1
stat->hist_array[diff]++;
}
stat->cycles++; // 循环加1
next.tv_sec += interval.tv_sec; // 继续计算下次循环的值 ...
next.tv_nsec += interval.tv_nsec;
...
}
具体测试时通过创建线程,在线程中进行测试以及记录.刚开始记录开始时间为 t 1 t_1 t1,间隔时间为 l,理论值即为 t 1 t_1 t1+l,在执行完之后再次记录时间 t 2 t_2 t2,延时即为 t 2 t_2 t2-( t 1 t_1 t1+l).
Debian/Ubuntu系统下可以直接使用apt-get install rt-tests来安装cyclictest。
#sudo yum install numactl-devel #CentOS安装
#apt-get install libnuma-dev #Ubuntu安装
从Git存储库获取最新的源代码,执行命令git clone git://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git或从存档中获取已发布的tarball,解压缩在预订的目录下,并在源目录下运行make,
(1)拷贝Cyclictest的git仓库
git clone git://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git
(2)进入git仓库
cd rt-tests
(3)创建一个分支,命名为testing
git branch testing
(4)切换到testing分支
git checkout testing
*(5)查看最新的稳定分支
git checkout origin/stable/v1.0
*(6)查看当前分支
git branch
(7)编译
make
*(8)交叉编译,修改Makefile中的编译器
make CROSS_COMPILE=arm-v4t-linux-gnueabi
确保是root或使用sudo运行cyclictest,如果没有参数,cyclictest会创建一个带有1ms间隔计时器的线程。
#运行5个线程,线程优先级80,无限循环
sudo cyclictest -t 5 -p 80 -n
#clock_nanosleep 线程优先级80,间隔10000微秒,10000次循环,无负载
cyclictest -t1 -p 80 -n -i 10000 -l 10000
#POSIX间隔计时器 线程优先级80,间隔10000微秒,10000次循环,无负载
cyclictest -t1 -p 80 -i 10000 -l 10000
sudo cyclictest -t1 -p 80 -n -i 10000 -l 10000
结果如下:
T: 0 (17769) P:80 I:10000 C: 10000 Min: 3 Act: 13 Avg: 14 Max: 36
结果分析:
T: 0 序号为0的线程
P: 0 线程优先级为0
C: 9397 计数器。线程的时间间隔每达到一次,计数器加1
I: 1000 时间间隔为1000微秒(us)
Min: 最小延时(us)
Act: 最近一次的延时(us)
Avg:平均延时(us)
Max: 最大延时(us)
计算机接收到中断信号到操作系统作出响应,并完成切换转入中断服务程序的时间。对于抢先式内核,要先调用一个特定的函数,该函数通知内核即将进行中断服务,使得内核可以跟踪中断的嵌套。抢先式内核的中断响应时间由下式给出:
中断响应时间=关中断的最长时间+保护CPU内部寄存器的时间+进入中断服务函数的执行时间+开始执行中断服务例程(ISR)的第一条指令时间中断响应时间是系统在最坏情况下响应中断的时间,某系统100次中有99次在50ms之内响应中断,只有一次响应中断的时间是250ms,只能认为中断响应时间是250ms。
除为中断处理提供确定性外,实时处理也需要支持周期性间隔的任务调度。大量控制系统要求周期性采样与处理。某个特定任务必须按照固定的周期(p)执行,从而确保系统的稳定性。考虑一下汽车的防抱死系统(ABS)。控制系统对车辆的每个车轮的转速进行采样(每秒最多20次)并控制每个制动器的压力(防止它锁死)。为了保持控制系统的正常工作,传感器的采样与控制必须按照一定的周期间隔。这意味着必须抢占其他处理,以便ABS任务能按照期望的周期执行。
当多任务内核决定运行另外的任务时,它把正在运行任务的当前状态(即CPU寄存器中的全部内容)保存到任务自己的栈区之中。然后把下一个将要运行的任务的当前状态从该任务的栈中重新装入CPU 的寄存器,并开始下一个任务的运行。这个过程就称为任务切换。做任务切换所需要的时间取决于CPU有多少寄存器要入栈。CPU 的寄存器越多,额外负荷就越重。
编译RT-tests进行Linux实时性测试
Ubuntu的rt-tests包
Git 源代码
实时操作系统
cyclictest 测试以及原理浅谈
cyclictest 的使用
cyclictest–(zc7045)实时性能测试及原理
Linux 实时性能测试工具——Cyclictest 的使用与分析
实时Linux内核的编译安装(PREEMPT_RT)以及测试
实时系统性能测试指标及方法