访问手机版页面
你的位置:老古开发网 > 其他 > 正文  
Linux嵌入式实时操作系统开发与设计(5)
内容导读:
第四章 RTLinux应用程序设计

4.1 程序结构

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

图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_FIFO和SCHED_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)

设置一个线程的调度参数,用policy和sched_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)

获得一个线程的调度参数。将获得的policy和sched_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.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 wont 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 412MHz,196MB内存,RTLinux3.1的机器上进行如下的Makefile编译:

all: rt_process.o irqsema.o monitor histplot

include ../../rtl.mk

monitor: monitor.c

$(CC) $ $ -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

.........

标签:
来源: 作者: 时间:2006/9/25 16:50:53
相关阅读
推荐阅读
阅读排行
最近更新
商品推荐