|
|
| | -文章搜索 - 最新文章 - | |
Linux嵌入式实时操作系统开发与设计(5) |
| 发布时间:2006年6月22日 点击次数:1217 |
| 来源: 作者: |
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); }
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 ......... |
|||
|
|
|
[嵌入式系统] 相关文章: Linux嵌入式实时操作系统开发与设计(4)简介:
3.3 实时任务 实时任务是一个用户定义的程序,它按照在内核控制下的特定的调度方式来执行。 最开始的设计是给每一个实时任务有自己的地址空间来提供内存保护。这通过80x86处理器内置的分页机制[10]。在每次上下文切换中,页目录是基于寄存器的变化来指向新任务的页目录。 任务间的切换非常频繁,如果在TLB没有命中时,使得系统在上下文切换的开销很大,系统性能会降低。别的系统开销还有是系统的调用,在保护模式下也是个费时的操作。 一种提高性能的方法是所有的实时任务运行在一个地址空间。通过使用内核地址空间,除去了保护模式变换的系统开销。Linux一个很有用的特性是:可装载内...... Linux嵌入式实时操作系统开发与设计(1)
Linux嵌入式实时操作系统开发与设计(6)
Linux嵌入式实时操作系统开发与设计(3)
MCU控制风光互补独立电源系统
基于嵌入式控制器的水质实时远程检测系统
嵌入式远程视频采集系统的设计与实现
小容量单片机系统的C语言程序结构
基于GPRS的火灾视频监测终端网络接口设计与实现
嵌入式指纹锁的设计与实现 |
|
|
|