RTlinux下的精确定时【转】

来源:互联网 发布:2016年开淘宝店晚吗 编辑:程序博客网 时间:2024/04/30 12:57

一般来说,普通的linux系统,定时精度只有十毫秒,即使在2..6.18以后,其误差也至少在半个毫秒,但在rtlinux下,其精度可达到微妙级,甚至纳秒,这对于实时性要求很高的工业控制系统来说,简直是一大福音,因为rtlinux是开源的,免费的,不过令人沮丧的是,rtlinux2007年被Wind River公司收购了,从此rtlinux的最高版本只到2.6.9为止,不过有一个类似的实时系统RTAI,其性能也与rtlinux不相上下,而且RTAI一直有团队在免费维护和升级,配合当今最流行的ubuntu系统,相信能够让你的程序跑遍天下无敌手。下面简单说说rtlinux下的定时应用,本人在rtlinux下开发过程序,写此文的主要目的是为日后留个备案而已,同时亦希望对他人有所帮助。

       Rtlinux下主要有两种定时方式,在这之前,我得首先谈谈rtlinux的时钟,下面是一些支持的时钟类型:

CLOCK_MONOTONIC: POSIX时钟,以恒定速率运行;不会复位和调整

CLOCK_REALTIME: 标准POSIX实时时钟。目前与CLOCK_MONOTONIC时钟相同

CLOCK_RTL_SCHED: 调度器用来任务调度的时钟

以下是机器结构相关的时钟:

CLOCK_8254: x86单处理器机器上用于调度的时钟

CLOCK_APIC: 用在SMP x86机器的时钟

以下是时间相关的一些函数:

int clock_gettime(clockid_t clock_id, struct timespec *ts);

hrtime_t clock_gethrtime(clockid_t clock);

struct timespec {

time_t tv_sec; /* */

long tv_nsec; /* 纳秒 */

};

clock_gettime:读取当前的时间,保存到clock_id所指的对象中。

clock_gethrtime:读取当前时间,但返回一个64(hrtime_t)的纳秒时间值

一些时间转换的函数,用于把时间格式转换为另外一种格式。

时间转换函数:

hrtime_t timespec_to_ns(const struct timespec *ts); /* timespec到纳秒数转换 */

struct timespec timespec_from_ns(hrtime_t t); /* 纳秒数到timespec转换 */

const struct timespec * hrt2ts(hrtime_t value);

 

说完了时钟,下面谈谈定时涉及到的POSIX线程属性设置相关函数。

int pthread_create (pthread_t *thread, pthread_attr_t * attr, void * (* start_routine)(void *), void *arg)

这是RTLinux的标准POSIX线程创建函数。这个线程运行函数指针start_routine指向的过程,arg是这个函数的指针的入口参数。线程的属性由attr对象决定,可以为这个属性设置CPU号、堆栈大小等属性。设定若为NULL,将会使用默认属性。返回0表示成功创建线程,线程号放在thread所指向的空间;返回非0表示创建失败。线程的属性决定在特定的CPU上创建线程(pthread_attr_setcpu_np),是否使用FPU(pthread_attr_setfp_np)

int pthread_attr_init (pthread_attr_t *attr)

初始化线程运行的属性。

int pthread_ attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param)int pthread_ attr_setschedparam (const pthread_attr_t *attr, struct sched_param *param)

这两个函数根据程序的需要相应地从attr中设定/取得线程的运行参数。param是为调度的SCHED_FIFOSCHED_RR策略定义的属性。

int pthread_attr_setcpu_np (pthread_atte_t *attr, int cpu)

int pthread_attr_getcpu_np (pthread_atte_t *attr, int cpu)

设定/取得线程运行的CPU号。在SMP机器上允许线程在一个特定的CPU上运行。

int pthread_cancel (pthread_t thread)

取消一个运行的线程。

int pthread_delete_np (pthread_t thread)

删除一个线程,并且释放该线程的所有资源。返回0表示成功删除,0表示删除失败。

pthrad_t pthread_self (void)

获得当前正在运行的线程号。

clockid_t rtl_getschedclock (void)

获得当前调度方法的时钟。

int rtl_setclockmode (clockid_t clock, int mode, hrtime_t mode_param)

设置当前的时钟模式,mode=RTL_CLOCK_MODE_ONESHOT时是非周期(一次性)模式mode_param参数无用;mode =RTL_CLOCK_MODE_PERIODIC时是周期模式,mode_param参数是周期的长度。

int pthread_wait_np (void)

当前周期的线程运行结束,总是返回0

说完了线程属性设置,要想用好定时,还要谈谈线程调度相关函数:

RTLinux提供一些调度方式,允许线程代码在特定的时刻运行。RTLinux使用单纯优先级驱动的调度器,更搞优先级的线程总是被选择运行。如果两个线程的优先级拥有一样的优先级,选择那一个线程运行是不确定的。RTLinux使用下面的调度API

int pthread_setschedparam (pthread_t thread, int policy, const struct sched_param *param)

设置一个线程的调度参数,policysched_param两个参数设置thread的调度参数属性:

policy=SCHED_RR:使用Round-Robin方法调度

policy=SCHED_FIFO:使用先进先出的方法调度

返回0表示成功调度,0表示失败。

int pthread_getschedparam (pthread_t thread, int policy, const struct sched_param *param)

获得一个线程的调度参数。将获得的policysched_param结构放在入口参数所指向的地址里面。

int pthread_make_periodic_np (pthread_t thread, hrtime start_time, hrtime_t period)

这个函数标记thread线程为可运行。线程将在start_time时刻开始运行,运行的时间间隔由period给定。

int pthread_wait_np (void)

pthread_wait_np函数将挂起当前运行发线程直到下一周期。这个线程必须是pthread_make_periodic_np函数标记为可执行。

int sched_get_priority_max (int policy)int sched_get_priority_min (int policy)

确定sched_priority可能的值。

下面的程序是可以进行精确定时的,精度可达到纳秒,

 

#ifndef __KERNEL__

#  define __KERNEL__

#endif

 

#ifndef MODULE

#  define MODULE

#endif

 

#include<linux/config.h>

#if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS)

#  define MODVERSIONS

#endif

 

#ifdef MODVERSIONS

#  include<linux/modversions.h>

#endif

 

#include<linux/module.h>

 

#include<asm/io.h>

#include<asm/uaccess.h>

#include <linux/proc_fs.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/delay.h>

#include <linux/slab.h>

#include <linux/mm.h>

#include <linux/ioport.h>

//////////////////////////#include rtl_xxx.h///////////////////////////////

#include <rtl_fifo.h>

#include <pthread.h>

#include <rtl_posixio.h>

#include <rtl.h>

#include <time.h>

#include <math.h>

 

pthread_t thread_pci;

 

///////////////////////thread_pci function////////////////////////

void *start_routine(void *arg){

    //设置线程属性

    struct sched_param p;

    p.sched_priority=1;//优先级

    pthread_setschedparam(pthread_self(),SCHED_FIFO,&p);//先进先出调度关于调度可见我的其它文章

    pthread_setfp_np(pthread_self(),1);

    //pthread_make_periodic_np(pthread_self(),gethrtime(),10000000);//10ms

    //

    hrtime_t current_time;

    int period=10000000;

    struct timespec resolution;

    int ret=rtl_setclockmode(CLOCK_REALTIME,RTL_CLOCK_MODE_PERIODIC,period);

    clock_getres(rtl_getschedclock(),&resolution);

    period=timespec_to_ns(&resolution);

   

    pthread_make_periodic_np(pthread_self(),clock_gethrtime(rtl_getschedclock()),period);

    current_time=clock_gethrtime(CLOCK_MONOTONIC);

 

    while(1)

        {

        pthread_wait_np();//挂起线程直到下个周期被唤醒

        current_time=clock_gethrtime(CLOCK_MONOTONIC);//(CLOCK_REALTIME);

        current_time+=period;

        clock_nanosleep(CLOCK_REALTIME,TIMER_ABSTIME,hrt2ts(current_time),NULL);       

        }

}

 

////////////////////////module init and clean up//////////////////////////////////////////

void cleanup_module (void)

{

    pthread_delete_np(thread_pci);//delete

 

    printk (KERN_NOTICE "Module Unloaded successfully/n");

}

 

int init_module (void) {

       

    ///create thread

    pthread_create(&thread_pci,NULL,start_routine,NULL);

 

    rtl_printf(KERN_NOTICE "Module successfully loaded");

    return 0;

}

上面程序中的这种定时方式精度很高,即clock_nanosleep()可达到纳秒级,但如果仅仅使用pthread_make_periodic_np(pthread_self(),gethrtime(),10000000)其精度只能达到微妙级,误差为几个微妙吧,如果想使上面的程序运行起来,还需要编写Makefile,可参考入下(在内核2.4下,2.6内核的Makefile有所不同):

INCLUDEDIR = /usr/src/linux/include

include rtl.mk

INSTALLDIR = /lib/modules/2.4.20-rtl3.2-pre3/pmac

 

CFLAGS +=  -Wall -O -D__KERNEL__ -DMODULE -I$(INCLUDEDIR)

 

all: module.o

 

module.o:module.c

    $(CC) ${INCLUDE} ${CFLAGS} -c -o module_tmp.o module.c

    $(LD) -r -static module_tmp.o -o module.o -L/usr/lib -lm

    rm -f module_tmp.o

install:

    install -d $(INSTALLDIR)

    install -c module.o $(INSTALLDIR)

    install -c module.h $(INCLUDEDIR)/linux

 

clean:

    rm -f *.o *~ core

    rm -f $(INSTALLDIR)/module.o

    rm -f $(INCLUDEDIR)/linux/module.h

上面程序中的rtl.mk来自于编译实时内核时所产生的关于实时内核的目录信息。中间编译链接库时加入了数学库,这是为在内核中进行数学计算时所必须添加的,当然在本例中用不到。

在编写实时内核程序时会遇到以下几个问题:

1.              实时线程中频繁的动态申请内存时常宕机。例如经常用printk()进行输出,或者在参数的位置上采用字符串的形式都会造成死机。

2.                实时线程如果不主动让出cpu,任何用户程序无法运行,包括你的键盘响应!所以在执行完任务时必须加上pthread_wait_np();

原创粉丝点击