cyclictest源码分析

  • 前言

最近研究了下rt-linux,在ubuntu上跑了3.4.72-rt89的实时内核,并且运行了cyclictest检验实时内核与普通内核的区别,详见:

http://blog.csdn.net/sakaue/article/details/17629917

http://blog.csdn.net/sakaue/article/details/17715287

  • 正文

那么,cyclictest到底运行了些什么去检验实时性的效果呢?我们首先分析下cyclictest.c文件(~/rt-tests/src/cyclictest/cyclictest.c)。假设,我们运行

sudo cyclictest -t1 -n -l 10000

这里的参数含义:-t 指定要在cyclictest 这个进程中要运行的线程个数,-n 代表我们使用clock_nanosleep函数进行休眠(稍后会详细介绍),-l代表每个线程中需要迭代的次数。这里指定cyclictest跑一个迭代10000次的线程。

在process_options() Line:1175中:

case 't':
num_threads = atoi(optarg); break; //指定线程数
...
case 'n':
use_nanosleep = MODE_CLOCK_NANOSLEEP;break //指定使用clock_nanosleep休眠
...
case 'l':
max_cycles = atoi(optarg);break; //指定线程迭代次数

main中有个for循环num_threads次(Line:1888 , 代表创建num_threads个线程),每个循环中创建一个struct thread_param结构体par(Line:1925),其结构如下

struct thread_param {
	int prio;
    int policy;
    int mode;
    int timermode;
    int signal;
    int clock;
	unsigned long max_cycles;
	struct thread_stat *stats;
    int bufmsk;
	unsigned long interval;
    int cpu;
    int node;
    int tnum;
};
然后对其各项字段进行赋值(Line:1928 – 1993),其中赋予par->interval=DEFAULT_INTERVAL(代表线程中每次迭代的休眠时间,默认1000,可以通过-i参数修改),随后pthread_create创建一个新线程(Line:1994),并将par作为参数传递进去。这个线程执行timerthread函数(Line:728):

void *timerthread(void *param);

在这个函数中,我们首先需要获取当前的事件,这里使用clock_gettime,它提供了纳秒的精确度:

clock_gettime(par->clock, &now); //Line : 790

然后我们把这个当前时间now值赋给next,并将next和之前的interval值相加,确定结束休眠的时间(这个时间是期望的休眠结束时间):
next = now;
next.tv_sec += interval.tv_sec;
next.tv_nsec += interval.tv_nsec;
tsnorm(&next);

随后程序就进入了max_cycles次迭代,我们之前指定为10000。这里,我们使用clock_nanosleep等待至next(Line:835),然后再计算睡眠结束时的时间,获取新的当前时间now(Line:874),这个时间也是实际的休眠的结束时间

if ((ret = clock_gettime(par->clock, &now))) {
	if (ret != EINTR)
		warn("clock_getttime() failed. errno: %d\n", errno);
	goto out;
}
然后,计算<strong>实际休眠结束时间now</strong>与<strong>期望休眠结束时间</strong>next之间的差值(Line:883):
diff = calcdiff(now, next);
如果这个差值小于或者大于我们设置的最小误差min和最大误差max,则用这个新值修改它们,并将这轮迭代的误差值累加,方便计算平均值(Line:884 – 891):

if (diff < stat->min)
	    stat->min = diff;
if (diff > stat->max) {
	    stat->max = diff;
            if (refresh_on_max)
	           pthread_cond_signal(&refresh_on_max_cond);
}
stat->avg += (double) diff;
记入这次迭代的误差,方便显示最近一次的误差ACT(Line:907):

stat->act = diff;

一个完整的线程迭代如下:

while (!shutdown) {
	 
	    uint64_t diff;
	    int sigs, ret;
	 
	    /* Wait for next period */
	    // par->mode = MODE_CLOCK_NANOSLEEP = 1
	    switch (par->mode) {
	        case MODE_CYCLIC:  // 0
	        case MODE_SYS_ITIMER: // 2
	            if (sigwait(&sigset, &sigs) < 0)
	                goto out;
	            break;
	 
	        case MODE_CLOCK_NANOSLEEP: // 1
	            if (par->timermode == TIMER_ABSTIME) { //par->timermode = 1 , TIMER_ABSTIME = 0x01
	                if ((ret = clock_nanosleep(par->clock, TIMER_ABSTIME, &next, NULL))) {
	                    if (ret != EINTR)
	                        warn("clock_nanosleep failed. errno: %d\n", errno);
	                    goto out;
	                }
	            } else {
	                if ((ret = clock_gettime(par->clock, &now))) {
	                    if (ret != EINTR)
	                        warn("clock_gettime() failed: %s", strerror(errno));
	                    goto out;
	                }
	                if ((ret = clock_nanosleep(par->clock, TIMER_RELTIME, &interval, NULL))) {
	                    if (ret != EINTR)
	                        warn("clock_nanosleep() failed. errno: %d\n", errno);
	                    goto out;
	                }
	                next.tv_sec = now.tv_sec + interval.tv_sec;
	                next.tv_nsec = now.tv_nsec + interval.tv_nsec;
	                tsnorm(&next);
	            }
	            break;
	 
	        case MODE_SYS_NANOSLEEP: //3
	            if ((ret = clock_gettime(par->clock, &now))) {
	                if (ret != EINTR)
	                    warn("clock_gettime() failed: errno %d\n", errno);
	                goto out;
	            }
	            if (nanosleep(&interval, NULL)) {
	                if (errno != EINTR)
	                    warn("nanosleep failed. errno: %d\n", errno);
	                goto out;
	            }
	            next.tv_sec = now.tv_sec + interval.tv_sec;
	            next.tv_nsec = now.tv_nsec + interval.tv_nsec;
	            tsnorm(&next);
	            break;
	    }
	 
	    if ((ret = clock_gettime(par->clock, &now))) { //获取当前时间,存在now中
	        if (ret != EINTR)
	            warn("clock_getttime() failed. errno: %d\n", errno);
	        goto out;
	    }
	 
	    if (use_nsecs)  //use_nsecs = 0
	        diff = calcdiff_ns(now, next); //now 中是下次循环的真值,而next是理论的值,所以两者的差就是延时!延时赋值给diff 
	    else
	        diff = calcdiff(now, next);//计算now - next的差值
	 
	    /*
	        stat->min = 1000000;
	        stat->max = 0;
	        stat->avg = 0.0;
	     */
	    if (diff < stat->min)// 假如延时比min 小,将min 改为这个更小的延时值diff 
	        stat->min = diff;
	    if (diff > stat->max) {// 假如延时比max 大,将max 改为这个更大的延时值diff 
	        stat->max = diff;
	        if (refresh_on_max) //refresh_on_max = 0
	            pthread_cond_signal(&refresh_on_max_cond);
	    }
	    stat->avg += (double) diff;// 计算新的平均延时 
	 
	    if (duration && (calcdiff(now, stop) >= 0))
	        shutdown++;
	 
	    if (!stopped && tracelimit && (diff > tracelimit)) {
	        stopped++;
	        tracemark("hit latency threshold (%d > %d)", diff, tracelimit);
	        tracing(0);
	        shutdown++;
	        pthread_mutex_lock(&break_thread_id_lock);
	        if (break_thread_id == 0)
	            break_thread_id = stat->tid;
	        break_thread_value = diff;
	        pthread_mutex_unlock(&break_thread_id_lock);
	    }
	    stat->act = diff;  //保存最近一次的时延
	 
	    if (par->bufmsk) //par->bufmsk = 0
	        stat->values[stat->cycles & par->bufmsk] = diff;
	 
	    /* Update the histogram */   // 更新histogram中存的延时统计数据 
	    if (histogram) {  //histogram = 0
	        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;
	 
	    //par->mode = MODE_CLOCK_NANOSLEEP = 1
	    if (par->mode == MODE_CYCLIC) { //define MODE_CYCLIC 0
	        int overrun_count = timer_getoverrun(timer);
	        next.tv_sec += overrun_count * interval.tv_sec;
	        next.tv_nsec += overrun_count * interval.tv_nsec;
	    }
	 
	    tsnorm(&next);
	 
	    if (par->max_cycles && par->max_cycles == stat->cycles)
	        printf("Break from cycle , max_cycles = %d\n",par->max_cycles);
	        break;
}

随后,程序会在print_stat(Line:1685)中,将上述的误差数据打印出来(Line:2030):

if (use_nsecs)
	    fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu "
	        "Min:%7ld Act:%8ld Avg:%8ld Max:%8ld\n";
else
	    fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu "
	        "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n";
	 
fprintf(fp, fmt, index, stat->tid, par->prio,
par->interval, stat->cycles, stat->min, stat->act,
stat->cycles ?
	(long)(stat->avg/stat->cycles) : 0, stat->max);

  •  总结

通过分析cyclictest源码,可知它是通过计算期望与实际时延的误差来检测rt系统的性能。这里只是抛砖引玉,研究了下单线程模式下的运行,接下来我会继续研究多线程模式下cyclictest的运作。




展开阅读全文

没有更多推荐了,返回首页