LXRTRTAI用户空间编程.docx
《LXRTRTAI用户空间编程.docx》由会员分享,可在线阅读,更多相关《LXRTRTAI用户空间编程.docx(32页珍藏版)》请在冰豆网上搜索。
LXRTRTAI用户空间编程
LXRT
——RTAI的用户空间编程
目录
一.rt_task_init_schmod(启用RTAI实时功能/实时任务的创建)2
1.函数2
2.参数2
3.注意2
4.示例代码2
二.实时任务定时器设置(rt_set_oneshot_mode等)2
1.rt_set_oneshot_mode3
2.rt_set_periodic_mode3
3.start_rt_timer3
4.注意3
5.示例代码3
三.rt_task_make_periodic(定期运行一个任务)4
1.函数4
2.参数4
3.注意4
四.代码基础框架4
1.Makefile4
2.run4
3..runinfo4
4.代码4
五.注意事项7
1.所谓的LXRT实即LinuxRealTimemodule,不存在LXRTscheduler7
2.LXRT下的硬实时相对于内核态下而言仍有若干微秒的差别7
3.在进入和离开LXRT域这两段时间,系统的实时性是无法保证的。
7
4.LXRTTipsAndTricks8
六.管道通讯8
1.发送函数8
2.接收函数8
3.示例代码9
七.共享内存通讯12
1.创建函数12
2.Free函数12
一.rt_task_init_schmod(启用RTAI实时功能/实时任务的创建)
实时任务的创建主要完成对代表实时任务实体的任务结构变量的初始化操作,包括分配任务栈、初始化任务栈、初始化链表指针等。
1.函数
RT_TASK*rt_task_init(unsignedlongname,intpriority,intstack_size,intmax_msg_size)
RT_TASK*rt_task_init_schmod(unsignedlongname,intpriority,intstack_size,int
max_msg_size,intpolicy,intcpus_allowed)
正如你可以看到的,有两个选项可以用来创建一个用户模式下的实时任务。
在用户模式下,rt_task_init被认为是一个更简单但因此有限的捷径。
总之他们都可以用于这个特定的目的。
只是为了记录第一个被定义为以下的:
RTAI_PROTO(RT_TASK*,rt_task_init,(unsignedlongname,intpriority,intstack_size,intmax_msg_size))
{
returnrt_task_init_schmod(name,priority,0,max_msg_size,SCHED_FIFO,0xFF);
}
它的意思是,它使用SCHED_FIFO策略并且设置cpu允许0XFF。
他们创建一个新的实时任务在用户空间,或者更好的说法是,他们在用户空间为LINUX进程/任务创建了一个RTAI任务扩展。
2.参数
Name:
任务名。
Priority:
任务优先级,0最高。
stack_size:
栈大小(不再被使用的遗留参数,只是为了可移植性才保留的)。
默认值是512。
max_msg_size:
可为0,在这种情况下使用一个默认的内部值。
默认值为256。
为了适合你的需求,有可能需要设置一个更大的值。
在这种情况下,要么合理设置宏MSG_SIZE来重新编译sys.c,要么在这里明确分配大点的尺寸。
但是,注意这点,消息大小不是关键的。
事实上在任何需要的时候,模块都会重新分配它,动态并且设置好了合适的大小。
policy:
调度策略,默认的是SCHED_FIFO,也可以设置成RT_SCHED_RR。
RR只是在有多个使用相同的优先级并且不释放CPU的任务的时候使用,。
在这种情况下,RR政策将会照顾它的平衡
cpus_allowed:
你想让任务运行的CPU。
它是个bit。
当RTAI希望一个任务静态地在一个特定的CPU上运行的时候,它是强制性的。
注意,如果你想在任务创建之后改变CPU,这一举动将会杀死real_time。
如果指定了更多的bit,RTAI通过尽量平衡这工作强迫任务运行在第一个可用的CPU上。
//SMP用的
3.注意
✧消息有个动态内存增加,每次buffer大小溢出时,现有的都会被释放,RTAIreal_timeheap会新创建一个大约比原来大20%新buffer。
这个操作显然是时间消费,所以为了最好的性能表现,强烈建议从最初始阶段就定义正确的大小。
4.示例代码
intmain(void)
{
RT_TASK*Main_Task;
if(!
(Main_Task=rt_task_init_schmod(nam2num("MNTSK"),0,0,0,SCHED_FIFO,0xF))){
printf("CANNOTINITMAINTASK\n");
exit
(1);
}
return0;
}
二.实时任务定时器设置(rt_set_oneshot_mode等)
在创建实时任务的过程中,主要通过3个函数对定时器进行设置,它们分别是:
rt_set_oneshot_mode,rt_set_periodic_mode和start_rt_timer,下面分别对这3个函数进行分析。
1.rt_set_oneshot_mode
用于将定时器设置为单触发模式,所谓单触发模式,就是说,每当定时器产生一次中断后,系统都要根据目前系统任务对时间精度的要求情况对定时器重新进行编程,设定下一次触发的时间。
rt_set_oneshot_mode完成的功能如下:
1.调用stop_rt_timer停止定时器的运行;
2.设置全局变量oneshot_timer的值为1;在时钟中断函数中,系统会检查oneshot_timer的值,判断定时器的工作模式,然后根据定时器的模式对定时器进行相应的操作。
2.rt_set_periodic_mode
rt_set_periodic_mode的操作与rt_set_oneshot_mode的类似,只不过它将oneshot_timer的值设为0,表示定时器应工作在周期模式;当定时器工作在周期模式下时,系统只要对定时器进行一次初始化,指定定时器产生中断的周期,以后就不再需要对定时器进行编程了。
3.start_rt_timer
根据设定的定时器运行模式对定时器进行初始化,它完成的主要功能如下:
1.调用函数rt_request_timer注册时钟中断服务程序rt_timer_handler;
2.初始化系统用于保存时间信息的结构rt_smp_times(就是前面所说的rt_times);
3.调用rt_request_linux_irq注册一个Linux下的中断服务程序recover_jiffies,这个中断程序和Linux的时钟中断服务程序共享时钟中断,recover_jiffies用于补偿Linux所丢失的时钟中断(因为可能由于实时任务的运行,使Linux很长一段时间得不到运行的机会,无法响应时钟中断)。
4.注意
✧请注意,如果你不设置模式,默认是周期性的。
✧使用stop_rt_timer停止定时器,会设置定时器返回默认的状态(即周期性的)。
如果没有删除在使用的RTAI调度程序,你想确定在多模块上是单触发模式,在每一个start_rt_timer前总要调用rt_set_oneshot_mode。
✧使用start_rt_timer(0),自动强制进入单触发模式。
✧rt_is_hard_timer_runningAPI可以知道是否已经有个计时器在运行了,该函数应该小心使用,因为它会形成一个“race”情况。
✧在使用任何与时间处理相关的函数前调用start_rt_timerAPI很重要,否则所有的值都被认为是错误的。
✧实时任务(本文所指的实时任务,如果没有特别说明都是硬实时的)都是以Linux内核模块方式实现的,要实现一个实时任务,在模块初始化的时候要调用RTAI的任务创建函数初始化实时任务相关的数据和环境,指定定时器的运行模式(单触发模式或周期模式),初始化定时器,然后开始执行任务;需要注意的是,当没有加载任何RTAI的实时任务模块的时候,RTAI的任务调度和时钟中断都没有启动。
5.示例代码
#defineTICK_TIME1000000
if((hard_timer_running=rt_is_hard_timer_running()))
{
printf("Skiphardreal_timersetting...\n");
sampling_interval=nano2count(TICK_TIME);
}
else
{
printf("Startingrealtimetimer...\n");
rt_set_oneshot_mode();
start_rt_timer(0);
}
sampling_interval=nano2count(TICK_TIME);
三.rt_task_make_periodic(定期运行一个任务)
1.函数
在这个阶段,计时器根据定时器政策选择(单触发vs周期)以适当的周期运行,它允许设置一个实时任务定期调度。
这可以用以下API实现:
Intrt_task_make_periodic(RT_TASK*task,RTIMEstart_time,RTTIMEperiod);
Intrt_task_make_periodic(RT_TASK*task,RTIMEstart_delay,RTTIMEperiod);
2.参数
task任务指针,之前由rt_task_init()创建的,当rt_task_wait_period()被调用时,以period为周期执行。
start_time:
第一次执行时间,是一个以clockticks测量的绝对值。
start_delay:
第一次执行时间,相对于当前时间,以纳秒测量。
period:
任务循环周期。
3.注意
✧为了更方便的处理clockticks和纳秒,在rtai_sched.h中有两个宏定义能帮助你,他们是RTIMEcount2nano(RTIMEtimercounts);RTIMEnano2count(RTIMEnanosecs);,它们只转换时间单位,但是你要记住,计数单位与选择的时间模式(单触发/周期)相关。
✧Recallthatthetermclockticksdependsonthemodeinwhichthehardtimerruns.Soifthehardtimerwassetasperiodicaclocktickwilllastastheperiodsetinstart_rt_timer,whileifoneshotmodeisusedaclocktickwilllastastheinverseoftherunningfrequencyofthehardtimerinuseandirrespectiveofanyperiodusedinthecalltostart_rt_timer.
四.代码基础框架
前面先准备三个文件:
1.Makefile
TARGET=periodic_task
SRCS=periodic_task.c
prefix:
=$(shellrtai-config--prefix)
ifeq($(prefix),)
$(errorPleaseadd/bintoyourPATHvariable)
endif
OBJECTS=$(SRCS:
.c=.o)
CC=$(shellrtai-config--cc)
LXRT_CFLAGS=$(shellrtai-config--lxrt-cflags)
LXRT_LDFLAGS=$(shellrtai-config--lxrt-ldflags
all:
$(TARGET)
%.o:
%.c
$(CC)-c$(LXRT_CFLAGS)$<
$(TARGET):
$(OBJECTS)
$(CC)-o$(TARGET)$(LXRT_LDFLAGS)-llxrt$(OBJECTS)
clean:
rm-f*.o*~core.*$(TARGET)
.PHONY:
clean
2.run
实际上是调用rtai-load来运行的.
${DESTDIR}/usr/realtime/bin/rtai-load
3..runinfo
隐藏文件rtai-load根据这个来运行
prog_name:
lxrt+sem+mbx+msg+fifos:
!
./prog_name;popall:
control_c
(2、3步骤可以简化成另一种方式,直接进入RTAI安装目录,然后insmodrtai_hal.koinsmodrtai_sched.koinsmodrtai_lxrt.ko……,然后运行程序./prog_name就行了)
4.代码
✧单触发模式
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
#include
#include
#include
//#include
//#include
staticintthread0;
staticvoid*fun0(void*arg)
{
RT_TASK*task;
task=rt_task_init_schmod(nam2num("TASK0"),0,0,0,SCHED_FIFO,0xF);
mlockall(MCL_CURRENT|MCL_FUTURE);
//设置优先级和调度算法这里他用SCHED_FIFO,而SCHED_OTHER(基于时间片)优先会低些,这个Name就是实时任务的标识 nam2num是把名字转为nameid以免命名冲突
//进入硬实时模式,无此语句时,默认的是软实时
//rt_make_hard_real_time();
//此处添加代码,如下语句
//rt_printk("HelloWorld!
\n");
//将实时任务或者线程返回到标准linux的状态
rt_make_soft_real_time();
//删除任务
rt_task_delete(task);
return0;
}
intmain(void)
{
RT_TASK*task;
//makemainthreadLXRTsoftrealtime
task=rt_task_init_schmod(nam2num("MYTASK"),9,0,0,SCHED_FIFO,0xF);
mlockall(MCL_CURRENT|MCL_FUTURE);
//startrealtimetimerandscheduler
rt_set_oneshot_mode();
start_rt_timer(0);
//createalinuxthread
thread0=rt_thread_create(fun0,NULL,10000);
//waitforendofprogram
printf("TYPETOTERMINATE\n");
getchar();
//cleanupstuff
stop_rt_timer();
return0;
}
✧周期模式
#include
#include
#include
#include
#include
#include
#include
#defineTICK_TIME1000000
#defineCPUMAP0x1
staticRT_TASK*main_Task;
staticRT_TASK*loop_Task;
intkeep_on_running=1;
staticpthread_tmain_thread;
staticRTIMEexpected;
staticRTIMEsampling_interval;
staticvoid*main_loop()
{
if(!
(loop_Task=rt_task_init_schmod(nam2num("RTAI01"),2,0,0,SCHED_FIFO,CPUMAP))){
printf("CANNOTINITPERIODICTASK\n");
exit
(1);
//mlockall(MCL_CURRENT|MCL_FUTURE);(源代码没有,…?
)
expected=rt_get_time()+100*sampling_interval;
rt_task_make_periodic(loop_Task,expected,sampling_interval);//
rt_make_hard_real_time();
while(keep_on_running)
{
//insertyourmainperiodicloophere
rt_task_wait_period();//
//setkeep_on_runningto0ifyouwanttoexit
}
rt_task_delete(loop_Task);
return0;
}
intmain(void)
{
RT_TASK*Main_Task;
if(!
(Main_Task=rt_task_init_schmod(nam2num("MNTSK"),0,0,0,SCHED_FIFO,0xF))){
printf("CANNOTINITMAINTASK\n");
exit
(1);
}
//mlockall(MCL_CURRENT|MCL_FUTURE);(源代码没有,…?
)
if((hard_timer_running=rt_is_hard_timer_running()))
{
printf("Skiphardreal_timersetting...\n");
sampling_interval=nano2count(TICK_TIME);
}
else
{
printf("Startingrealtimetimer...\n");
rt_set_oneshot_mode();
start_rt_timer(0);//启动计时器记得用完要关掉
}
sampling_interval=nano2count(TICK_TIME);
pthread_create(&main_thread,NULL,main_loop,NULL);
while(keep_on_running)
sampling_interval=sampling_interval;//donothing!
rt_task_delete(Main_Task);
return0;
}
五.注意事项
1.所谓的LXRT实即LinuxRealTimemodule,不存在LXRTscheduler
所谓的LXRT实即LinuxRealTimemodule,不存在LXRTscheduler。
RTAI下只有3种调度器,UP,SMP和MUP。
LXRT的存在使得RTAI所有的调度函数都能在Linux进程中直接调用。
这样一来,RTAI的实时服务就具备了所谓的“完全对称”——在RTAI域和LINUX域中的使用几乎完全一样。
也就是说,用户可以在LINUX和LINUX间,RTAI和LINUX间共用内存,发送消息,调用semaphore或时间相关服务,当然,在RTAI和RTAI之间更不是问题。
(源自DIAPMRTAI-Beginner'sGuide,https:
//www.rtai.org/index.php?
module=documents&JAS_DocumentManager_op=downloadFile&JAS_File_id=32)
2.LXRT下的硬实时相对于内核态下而言仍有若干微秒的差别
LXRT下的hardrealtime还是与内核态任务的执行有着本质区别的。
RTAI通过其RTAI_PROTO为用户态下进程提供调用内核API的捷径,并由RTAI自身的调度器保证了RTAI任务对中断的优先权。
处于中断处理链条终端的LINUX因此不能破坏RTAI任务的实时性能。
但无论如何,contextswitch是需要时间的。
在RTAI域内用户态和内核态的切换也免不了牺牲一定的时间开销。
RTAI相关的文档指出LXRT下的硬实时相对于内核态下而言仍有若干微秒的差别。
笔者也在试验中发现,前者实时性能与后者相比相差约有十几微秒,有时甚至更多。
对于一些要求苛刻的实时任务而言,LXRT下的时延(或者还有实时误差?
)是无法接受的。
3.在进入和离开LXRT域这两段时间,系统的实时性是无法保证的。
也就是说,在时间轴上的这两个区间,所有LXRT任务并非处于真正的硬实时状态,期间任何操作都有可能因其不稳定性导致10~20微秒的实时误差(具体数值未做严密测量)。
这一点在RTAI自身文档中也有阐述,但因为不引人注意,使得基于RTAI/LXRT的实时控制开发人员非常容易忽略。
对这一问题的最简单处理办法就是在调用rt_make_hard_real_time()后随即插入短暂的等待,再进入实时处理阶段。
实践中笔者在等待500微秒后得到理想的实时表现。
4.stop_rt_timer是全局变量,如果在进程中某个线程结束时候使用,则会结束全部线程的定时器。
解决这种情况可以,一,全部用周期,这样既不用去start也不用去stop;二,每个单独启动oneshot定时,然后在最后再结束。
5.LXRTTipsAndTricks
六.管道通讯
包含头文件#include"rtai_sched.h"
1.发送函数
intrt_mbx_send(MBX*mbx,void*