Linux嵌入式实时操作系统开发与设计(七)

第四章  RTLinux应用程序设计

4.1  程序结构

每个实时应用程序可以分为两部分:实时部分和非实时部分[2]。非实时部分在用户空间执行,称为用户部分。实时部分要尽可能简单,只包含直接与时间相关的代码;由于硬件对时间的约束,低级的与硬件通信的代码一般也包含在实时部分。用户部分的代码主要实现为数据的处理,包括数据的发布、保存和用户界面。两部分之间的通信采用数据缓冲区。

4.1所示的数据流程图是依照这个程序模型的典型实时应用程序。

 

 

4.1  程序结构图

 

4.2  基本API

4.2.1  POSIX线程创建函数

就像前面介绍的那样,一个实时程序是由几个执行的线程组成的。线程是轻量级进程,它们共享共有的地址空间。在RTLinux中,所有的线程共享Linux内核地址空间。

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参数是周期的长度。(有关时钟模式见3.4节的说明)

 

int pthread_wait_np (void)

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

 

4.2.2        时间相关函数

RTLinux提供了一些时钟函数用于计时功能,包括线程调度,获TSP(timestamps)等。

下面的是一般的计时函数:

/*  #include   */

 

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)的纳秒时间值。

 

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

时间转换函数:

/*  #include   */

 

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); /* 

 

下面是一些支持的时钟类型。

时钟类型相关的宏:

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

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

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

 

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

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

l          CLOCK_APIC: 用在SMP x86机器的时钟

 

4.2.3  线程调度函数

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可能的值。

 

4.3  编程示例

前面介绍了RTLinux的基本API,在这里以一个实例来说明RTLinux下的编程方法。这是一以测试RTLinux下中断延迟的程序。正如前面所说的,程序分为两部分,实时部分和非实时部分。实时部分通过使用一个模块,在将实时模块插入后,运行实时任务。对于非实时部分,实现对FIFO设备的读取,完成和实时任务的通信。

 

4.2  实时程序结构图

 

4.3.1  实时部分

init_module完成对实时部分的初始化。cleanup_module实现关闭实时模块的任务。

/*  * RTLinux scheduling accuracy measuring example  */

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "common.h"

 

int ntests=500;

int period=1000000;

int bperiod=3100000;

int mode=0;

int absolute=0;

int fifo_size=4000;

int advance=0;

 

MODULE_PARM(period,"i");

MODULE_PARM(bperiod,"i");

MODULE_PARM(ntests,"i");

MODULE_PARM(mode,"i");

MODULE_PARM(absolute,"i");

MODULE_PARM(advance,"i");

 

pthread_t thread;

int fd_fifo;

 

 

void *thread_code(void *param)

{

hrtime_t expected;

hrtime_t diff;

hrtime_t now;

hrtime_t last_time = 0;

hrtime_t min_diff;

hrtime_t max_diff;

struct sample samp;

int i;

int cnt = 0;

int cpu_id = rtl_getcpuid();

 

rtl_printf ("Measurement task starts on CPU %d/n", cpu_id);

if (mode) {

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

 

      if (ret != 0) {

          conpr("Setting periodic mode failed/n");

          mode = 0;

      }

} else {

 

      rtl_setclockmode (CLOCK_REALTIME, RTL_CLOCK_MODE_ONESHOT, 0);

}

 

expected = clock_gethrtime(CLOCK_REALTIME) + 2 * (hrtime_t) period;

 

 

fd_fifo = open("/dev/rtf0", O_NONBLOCK);

if (fd_fifo < 0) {

      rtl_printf("/dev/rtf0 open returned %d/n", fd_fifo);

      return (void *) -1;

}

 

if (advance) {

      rtl_stop_interrupts(); /* Be careful with this! The task won't be preempted by anything else. This is probably only appropriate for small high-priority tasks. */

}

 

/* first cycle */

clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, hrt2ts(expected - advance), NULL);

expected += period;

now = clock_gethrtime(CLOCK_MONOTONIC);

last_time = now;

 

do {

      min_diff = 2000000000;

      max_diff = -2000000000;

 

      for (i = 0; i < ntests; i++) {

          ++cnt;

          clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, hrt2ts(expected - advance), NULL);

 

          now = clock_gethrtime(CLOCK_MONOTONIC);

          if (absolute && advance && !mode) {

               if (now < expected) {

                    rtl_delay (expected - now);

               }

               now = clock_gethrtime(CLOCK_MONOTONIC);

          }

          if (absolute) {

               diff = now - expected;

          } else {

               diff = now - last_time - period;

               if (diff < 0) {

                    diff = -diff;

               }

          }

          if (diff < min_diff) {

               min_diff = diff;

          }

           if (diff > max_diff) {

               max_diff = diff;

          }

 

          expected += period;

          last_time = now;

      }

 

      samp.min = min_diff;

      samp.max = max_diff;

      write (fd_fifo, &samp, sizeof(samp));

} while (1);

return 0;

}

 

pthread_t background_threadid;

 

void *background_thread(void *param)

{

hrtime_t next = clock_gethrtime(CLOCK_REALTIME);

while (1) {

      hrtime_t t = gethrtime ();

      next += bperiod;

      /* the measurement task should preempt the following loop */

      while (gethrtime() < t + bperiod * 2 / 3);

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

}

}

 

 

int init_module(void)

{

pthread_attr_t attr;

struct sched_param sched_param;

int thread_status;

        int fifo_status;

 

rtf_destroy(0);

fifo_status = rtf_create(0, fifo_size);

if (fifo_status) {

      rtl_printf("RTLinux measurement test fail. fifo_status=%d/n",fifo_status);

      return -1;

}

 

 

rtl_printf("RTLinux measurement module on CPU %d/n",rtl_getcpuid());

pthread_attr_init (&attr);

if (rtl_cpu_exists(1)) {

      pthread_attr_setcpu_np(&attr, 1);

}

sched_param.sched_priority = 1;

pthread_attr_setschedparam (&attr, &sched_param);

rtl_printf("About to thread create/n");

thread_status = pthread_create (&thread,  &attr, thread_code, (void *)1);

if (thread_status != 0) {

      rtl_printf("failed to create RT-thread: %d/n", thread_status);

      return -1;

} else {

      rtl_printf("created RT-thread/n");

}

 

if (bperiod) {

      pthread_create (&background_threadid,  NULL, background_thread, NULL);

}

return 0;

}

 

 

void cleanup_module(void)

{

        rtl_printf ("Removing module on CPU %d/n", rtl_getcpuid());

pthread_cancel (thread);

pthread_join (thread, NULL);

close(fd_fifo);

rtf_destroy(0);

if (bperiod) {

      pthread_cancel (background_threadid);

      pthread_join (background_threadid, NULL);

}

}

 

4.3.2  非实时部分

    非实时部分实际上建行就是一个应用程序,可以在控制台下编写。不过必须有/dev/rtf0设备的读取权限才能运行。

#include

#include

#include

#include

#include

#include

#include

#include

#include "common.h"

 

 

 

int main()

{

int fd0;

int n;

struct sample samp;

if ((fd0 = open("/dev/rtf0", O_RDONLY)) < 0) {

      fprintf(stderr, "Error opening /dev/rtf0/n");

      exit(1);

}

 

 

while (1) {

      n = read(fd0, &samp, sizeof(samp));

      printf("min: %8d, max: %8d/n", (int) samp.min, (int) samp.max);

      fflush(stdout);

}

 

return 0;

}

 

4.3.3  编译和运行程序

在一台Celeron 412MHz196MB内存,RTLinux3.1的机器上进行如下的Makefile编译:

all: rt_process.o irqsema.o monitor histplot

 

include ../../rtl.mk

 

monitor: monitor.c

$(CC) ${USER_CFLAGS} ${INCLUDE} -Wall -O2 -o monitor monitor.c

 

clean:

rm -f *.o monitor histplot periodic_monitor gnuplot.out

 

include $(RTL_DIR)/Rules.make

 

则程序可以测试调度的时间精度,程序的运行结果为:

min:        0, max:    24480

min:        0, max:    10720

min:        0, max:    10912

min:        0, max:    10976

min:        0, max:    11072

min:        0, max:    10656

min:        0, max:    10944

min:        0, max:    11200

min:        0, max:    11200

min:        0, max:    11008

min:        0, max:    10912

.........

你可能感兴趣的:(Linux嵌入式实时操作系统开发与设计(七))