前面三节中的线程都是单次线程,即只执行一次便注销。在实时系统中,通常包括周期线程(在文章中用任务表述)和非周期线程(单次线程)。为了实现周期线程,RTLinux采用while循环的方式实现,即while一次表示该线程的一次执行。
从表面意义上来看,周期线程应该包括一个周期属性,表示该线程多长时间执行一次(从程序角度上来说也叫循环一次)。现在,我们从一个实际的例子当中来看周期线程是如何创建和工作的。
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("der.herr@hofr.at");
MODULE_DESCRIPTION("pure posix periodic thread");
#define US_PER_SECOND 1000 * 1000
#define NS_PER_SECOND 1000 * 1000 * 1000
pthread_t thread1;
void *start_routine(void *arg)
{
struct sched_param p;
int i=0;
p . sched_priority = 1;
pthread_setschedparam
(pthread_self(), SCHED_FIFO, &p);
pthread_make_periodic_np
(pthread_self(), gethrtime(), (hrtime_t)5 * NS_PER_SECOND);
while(1)
{
i++;
rtl_printf("I
am the period thread, I have executed %d times\n", i);
mdelay(1000);
pthread_wait_np();
}
}
int init_module(void) {
int ret;
ret = pthread_create
(&thread1, NULL, start_routine,(void *) 1);
if (ret == EAGAIN)
{
rtl_printf("Create
thread1 failed.\n");
return -1;
}
return 0;
}
void cleanup_module(void) {
pthread_delete_np(thread1);
printk("thread
terminated\n");
}
该程序中,init_module创建一个线程thread1,在thread1的执行函数start_routine中,调用pthread_setschedparam()函数设置该线程的优先级。该函数具有三个参数:1.线程号,2.调度策略3.调度参数(只有优先级)。其中调度策略在实时程序中是无用的。Pthread_self()返回当前执行线程的线程号。我想rtlinux的开发者之所以这样设计,是为了那些习惯于编写linux程序的人。我们通过该函数设置thread1的优先级为1,然后调用函数pthread_make_periodic_np。该函数也同样包括三个参数:1.线程号;2.周期开始时间(绝对开始时间);3.周期大小。后两个参数的数值类型为hrtime_t,该类型等价于long long。程序中,第二个参数gethrtime()返回当前时间,时间格式为hrtime_t。值得注意的第三个参数,如果我们将第三个参数改为5 * NS_PER_SECOND,编译过程中会出现如下警告:
warning: integer overflow in expression。因为5会默认为int类型,当乘以NS_PER_SECOND后,该值超出了int的范畴,为此,很有必要对5进行强制类型转换。在这细心的读者会发现NS_PER_SECOND在定义的时候采用#define NS_PER_SECOND 1000 * 1000 * 1000,而不是#define NS_PER_SECOND 1000000000。其原因是前者能够让人很清楚的知道NS_PER_SECOND是1亿,而后者却要麻烦人去耐心的查0的个数,稍微不细心,就会少写或者多写1个0。
经过pthread_make_periodic_np函数后,rtlinux内核已经把thread1标记为周期线程了,其标志方法就是设置thread1的resume_time值.,该值的具体作用在后面我们会做具体介绍。该函数执行完毕后,thread1会继续执行。这跟某些资料的阐述是不一样的。有些资料说该函数执行完毕后,线程会挂起,直到到达其开始时间,也即是第二个参数,这种说法是不对的。即使当开始时间大于当前时间,该函数也不会挂起该线程。具体原因我们在后面用专门的一节来解释。
While循环中,我们使用i作为计数来显示周期线程的执行次数。Thread1每次执行都会忙等待1秒钟,然后执行pthread_wait_np。这个函数表示将当前线程挂起,直到下次周期时间的到来。下次到达时间也在该函数中被设置,设置的代码是self->resume_time+=self->period.
该程序的运行结果如下所示:
I am the period thread, I have executed 1 times
I am the period thread, I have executed 2 times
I am the period thread, I have executed 3 times
I am the period thread, I have executed 4 times
thread terminated
编写周期线程应该注意以下几点:
1.pthread_make_periodic_np中第三个参数进行必要的强制类型转换:(hrtime_t)。
2.线程的周期不能小于该线程的执行时间,否则系统会一直执行实时程序,而无时间来响应非实时程序。
3.While循环体中必须包括pthread_wait_np函数,否则出现与2一样的情况。
4.周期线程是个死循环,如果想终止该线程,则需要在shell中通过rmmod命令卸载该实时模块。