cyclictest是一个高精度的测试程序,Cyclictest 是 rt-tests 下的一个测试工具,也是rt-tests 下使用最广泛的测试工具,一般主要用来测试使用内核的延迟,从而判断内核的实时性。
将cyclictest二进制文件放到NFS共享文件夹,客户机连接主机NFS共享文件夹。执行如下命令
./cyclictest -t 5 -p 80 -n
tips:
cyclictest 的参数介绍
关于cyclictest 的各个参数具体含义建议大家还是用时间具体看看 cyclictest --help 的信息,我这只介绍几个常用的。
-p PRIO --prio=PRIO 最高优先级线程的优先级 使用时方法为: -p 90 / --prio=90
-m --mlockall 锁定当前和将来的内存分配
-c CLOCK --clock=CLOCK 选择时钟 cyclictest -c 1
0 = CLOCK_MONOTONIC (默认)
1 = CLOCK_REALTIME
-i INTV --interval=INTV 基本线程间隔,默认为1000(单位为us),下面介绍原理的时候会提到
-l LOOPS --loops=LOOPS 循环的个数,默认为0(无穷个),与 -i 间隔数结合可大致算出整个测试的时间,比如 -i 1000 -l 1000000 ,总的循环时间为1000*1000000=1000000000 us =1000s ,所以大致为16分钟多。
-n --nanosleep 使用 clock_nanosleep
-h HISTNUM --histogram=US 在执行完后在标准输出设备上画出延迟的直方图(很多线程有相同的权限)US为最大的跟踪时间限制,这个在下面介绍实例时可以用到,结合gnuplot 可以画出我们测试的结果图。
-q --quiet 使用-q 参数运行时不打印信息,只在退出时打印概要内容,结合-h HISTNUM参数会在退出时打印HISTNUM 行统计信息以及一个总的概要信息。
-f --ftrace f trace函数跟踪(通常与-b 配套使用,其实通常使用 -b 即可,不使用 -f )
-b USEC --breaktrace=USEC 当延时大于USEC指定的值时,发送停止跟踪。USEC,单位为谬秒(us)。
下面来个对比:
加实时补丁
不加实时补丁
cyclictest运行结果详解
T: 0 序号为0的线程
P: 0 线程优先级为0
C: 9397 计数器。线程的时间间隔每达到一次,计数器加1
I: 1000 时间间隔为1000微秒(us)
Min: 最小延时(us)
Act: 最近一次的延时(us)
Avg:平均延时(us)
Max: 最大延时(us)
结合详解对结果进行简单的比对,效果还是比较明显的。
从rt-tests/src/cyclictest/cyclictest.c 的 main函数入手:
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);
...
}
所以我们看到在main函数中只是定义了相关的变量之类的,而具体测试是通过创建线程,在线程中进行测试以及记录,下面我们打开测试线程:
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;
...
}
从代码中可以看出,实现计算延时的原理还是挺简单的。
总结一下,就是刚开始记录开始时间为t1,间隔时间为 l,理论值即为 t1+l,在执行完之后再次记录时间t2,延时即为 t2-(t1+l).