当前位置: 首页 > 工具软件 > Cyclictest > 使用案例 >

cyclictest源码分析

舒永嘉
2023-12-01

sudo cyclictest -t1 -n -l 10000  

这里的参数含义:-t 指定要在cyclictest 这个进程中要运行的线程个数,-n 代表我们使用clock_nanosleep函数进行休眠(稍后会详细介绍),-l代表每个线程中需要迭代的次数。这里指定cyclictest跑一个迭代10000次的线程。
在process_options() Line:1175中:
[cpp] view plain copy
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),其结构如下
[cpp] view plain copy
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):
[cpp] view plain copy
void *timerthread(void *param);  


在这个函数中,我们首先需要获取当前的事件,这里使用clock_gettime,它提供了纳秒的精确度:
[cpp] view plain copy
clock_gettime(par->clock, &now); //Line : 790  


然后我们把这个当前时间now值赋给next,并将next和之前的interval值相加,确定结束休眠的时间(这个时间是期望的休眠结束时间):
[cpp] view plain copy
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),这个时间也是实际的休眠的结束时间:
[cpp] view plain copy
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):
[cpp] view plain copy
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):
[cpp] view plain copy
stat->act = diff;  


一个完整的线程迭代如下:
[cpp] view plain copy
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):
[cpp] view plain copy
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的运作。
 类似资料: