RTAI的用户空间编程

一,任务创建

实时任务的创建主要完成对代表实时任务实体的任务结构变量的初始化操作,包括分配任务栈、初始化任务栈、初始化链表指针等。
1. 函数

RT_TASK* rt_task_init(unsigned long name, int priority, int stack_size, int max_msg_size)
RT_TASK*rt_task_init_schmod(unsigned long name, int priority, int stack_size, int
max_msg_size, int policy, int cpus_allowed)

正如你可以看到的,有两个选项可以用来创建一个用户模式下的实时任务。在用户模式下,rt_task_init被认为是一个更简单但因此有限的捷径。总之他们都可以用于这个特定的目的。只是为了记录第一个被定义为以下的:

RTAI_PROTO(RT_TASK *,rt_task_init,(unsigned long name, int priority, int stack_size, int max_msg_size))
{
return rt_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大小溢出时,现有的都会被释放,RTAI real_time heap会新创建一个大约比原来大20%新buffer。这个操作显然是时间消费, 所以为了最好的性能表现,强烈建议从最初始阶段就定义正确的大小。

4. 示例代码

int main(void)
{
RT_TASK *Main_Task;
if (!(Main_Task = rt_task_init_schmod(nam2num("MNTSK"), 0, 0, 0, SCHED_FIFO, 0xF))) {
printf("CANNOT INIT MAIN TASK\n");
exit(1);
}
return 0;
实时任务定时器设置

在创建实时任务的过程中,主要通过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. 注意

  1. 请注意,如果你不设置模式,默认是周期性的。
  2. 使用stop_rt_timer停止定时器,会设置定时器返回默认的状态(即周期性的)。如果没有删除在使用的RTAI调度程序,你想确定在多模块上是单触发模式,在每一个start_rt_timer前总要调用rt_set_oneshot_mode。
  3. 使用start_rt_timer(0),自动强制进入单触发模式。 
  4. rt_is_hard_timer_running API可以知道是否已经有个计时器在运行了,该函数应该小心使用,因为它会形成一个“race”情况。
  5. 在使用任何与时间处理相关的函数前调用start_rt_timer API很重要,否则所有的值都被认为是错误的。
  6. 实时任务(本文所指的实时任务,如果没有特别说明都是硬实时的)都是以Linux内核模块方式实现的,要实现一个实时任务,在模块初始化的时候要调用RTAI的任务创建函数初始化实时任务相关的数据和环境,指定定时器的运行模式(单触发模式或周期模式),初始化定时器,然后开始执行任务;需要注意的是,当没有加载任何RTAI的实时任务模块的时候,RTAI的任务调度和时钟中断都没有启动。

5. 示例代码

#define TICK_TIME 1000000
if ((hard_timer_running = rt_is_hard_timer_running()))
{
    printf("Skip hard real_timer setting...\n");
    sampling_interval = nano2count(TICK_TIME);
}
else
{
    printf("Starting real time timer...\n");
    rt_set_oneshot_mode();
    start_rt_timer(0);
}

sampling_interval = nano2count(TICK_TIME);

二,定期运行一个任务

1. 函数
在这个阶段,计时器根据定时器政策选择(单触发vs周期)以适当的周期运行,它允许设置一个实时任务定期调度。这可以用以下API实现:

int rt_task_make_periodic (RT_TASK *task, RTIME start_time, RTTIME period);
int rt_task_make_periodic (RT_TASK *task, RTIME start_delay, RTTIME period);

2.参数
task:任务指针,之前由rt_task_init()创建的,当rt_task_wait_period()被调用时,以period为周期执行。
start_time:第一次执行时间,是一个以clock ticks测量的绝对值。
start_delay:第一次执行时间,相对于当前时间,以纳秒测量。
period:任务循环周期。

3. 注意

  • 为了更方便的处理clock ticks和纳秒,在rtai_sched.h中有两个宏定义能帮助你,RTIME count2nano(RTIME timercounts);RTIME nano2count (RTIME nanosecs),它们只转换时间单位,但是你要记住,计数单位与选择的时间模式(单触发/周期)相关。
  • Recall that the term clock ticks depends on the mode in which the hard timer runs. So if the hard timer was set as periodic a clock tick will last as the period set in start_rt_timer,while if oneshot mode is used a clock tick will last as the inverse of the runningfrequency of the hard timer in use and irrespective of any period used in the call tostart_rt_timer.

三,代码基础框架

前面先准备三个文件:
1. Makefile

TARGET = periodic_task  
SRCS = periodic_task.c 
prefix := $(shell rtai-config --prefix)
ifeq ($(prefix),)
$(error Please add <rtai-install>/bin to your PATH variable)
endif
OBJECTS = $(SRCS:.c=.o)
CC = $(shell rtai-config --cc)
LXRT_CFLAGS = $(shell rtai-config --lxrt-cflags)
LXRT_LDFLAGS = $(shell rtai-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
  • 1

(2 、3步骤可以简化成另一种方式,直接进入RTAI安装目录,然后insmod rtai_hal.ko insmod rtai_sched.ko insmod rtai_lxrt.ko……,然后运行程序./prog_name 就行了)

4. 代码框架

  • 单触发模式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
//#include <sys/time.h>
//#include <signal.h>
#include <fcntl.h>
#include <sched.h>
#include <rtai_lxrt.h>
//#include <rtai_sem.h>
//#include <rtai_msg.h>

static int thread0;
static void *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 是把名字转为name id 以免命名冲突

    // 进入硬实时模式,无此语句时,默认的是软实时
    //rt_make_hard_real_time();

    //此处添加代码,如下语句
    //rt_printk("Hello World!\n");

    //将实时任务或者线程返回到标准linux的状态
    rt_make_soft_real_time();

    //删除任务
    rt_task_delete(task);
    return 0;
}

int main(void)
{
    RT_TASK *task;

    // make main thread LXRT soft realtime
    task = rt_task_init_schmod(nam2num("MYTASK"), 9, 0, 0, SCHED_FIFO, 0xF);
    mlockall(MCL_CURRENT | MCL_FUTURE);

    // start realtime timer and scheduler
    rt_set_oneshot_mode();
    start_rt_timer(0);

    // create a linux thread 
    thread0 = rt_thread_create(fun0, NULL, 10000);

    // wait for end of program
    printf("TYPE <ENTER> TO TERMINATE\n");
    getchar();

    // cleanup stuff
    stop_rt_timer();
    return 0;
}

- 周期模式

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <rtai_lxrt.h>
#include <sys/io.h>

#define TICK_TIME 1000000
#define CPUMAP 0x1

static RT_TASK *main_Task;
static RT_TASK *loop_Task;
int keep_on_running = 1;
static pthread_t main_thread;
static RTIME expected;
static RTIME sampling_interval;

static void *main_loop()
{
    if (!(loop_Task = rt_task_init_schmod(nam2num("RTAI01"), 2, 0, 0, SCHED_FIFO, CPUMAP))) 
    {
        printf("CANNOT INIT PERIODIC TASK\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)
    {
        //insert your main periodic loop here
        rt_task_wait_period();//
        //set keep_on_running to 0 if you want to exit
    } 

    rt_task_delete(loop_Task);
    return 0;
}

int main(void)
{
    RT_TASK *Main_Task;
    if (!(Main_Task = rt_task_init_schmod(nam2num("MNTSK"), 0, 0, 0, SCHED_FIFO, 0xF))) 
    {
        printf("CANNOT INIT MAIN TASK\n");
        exit(1);
    } 

    //mlockall(MCL_CURRENT | MCL_FUTURE); 
    if ((hard_timer_running = rt_is_hard_timer_running()))
    {
        printf("Skip hard real_timer setting...\n");
        sampling_interval = nano2count(TICK_TIME);
    }
    else
    {
        printf("Starting real time timer...\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; //do nothing!

    rt_task_delete(Main_Task);
    return 0;
}
四,注意事项
  1. 所谓的LXRT实即Linux Real Time module,不存在LXRT scheduler
    所谓的LXRT实即Linux Real Time module,不存在LXRT scheduler。RTAI下只有3种调度器,UP,SMP和MUP。LXRT的存在使得RTAI所有的调度函数都能在Linux进程中直接调用。这样一来,RTAI的实时服务就具备了所谓的“完全对称”——在RTAI域和LINUX域中的使用几乎完全一样。也就是说,用户可以在LINUX和LINUX间,RTAI和LINUX间共用内存,发送消息,调用semaphore或时间相关服务,当然,在RTAI和RTAI之间更不是问题。(源自DIAPM RTAI - Beginner’s Guide,https://www.rtai.org/index.php?module=documents&JAS_DocumentManager_op=downloadFile&JAS_File_id=32
  2. LXRT下的硬实时相对于内核态下而言仍有若干微秒的差别
    LXRT下的hard real time还是与内核态任务的执行有着本质区别的。RTAI通过其RTAI_PROTO为用户态下进程提供调用内核API的捷径,并由RTAI自身的调度器保证了RTAI任务对中断的优先权。处于中断处理链条终端的LINUX因此不能破坏RTAI任务的实时性能。但无论如何,context switch是需要时间的。在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定时,然后在最后再结束。

五,管道通讯

包含头文件#include “rtai_sched.h”
1. 发送函数

int rt_mbx_send (MBX* mbx, void* msg, int msg_size);
//无条件发送消息

int rt_mbx_send_wp (MBX* mbx, void* msg, int msg_size); 
//在不阻塞当期调用的任务的情况下,尽可能多的发送数据。消息长度是msg_size。

int rt_mbx_send_if (MBX* mbx, void* msg, int msg_size); 
//发送一个消息, 要求在不阻塞当前调用任务的情况下,整个消息可以完整传递。

int rt_mbx_send_until (MBX* mbx, void* msg, int msg_size, RTIME time);
int rt_mbx_send_timed (MBX* mbx, void* msg, int msg_size, RTIME delay);
//rt_mbx_send_until和rt_mbx_send_timed发送消息msg_size大小的msg到邮箱mbx。调用者将被阻塞,直到所有字节的入消息队列、超时过期或发生错误。

函数返回
成功,未寄出的字节的数量被返回;失败,返回负值,负值定义如下:EINVAL mbx指向无效的mailbox(mbx points to not a valid mailbox).

2. 接收函数

int rt_mbx_receive (MBX* mbx, void* msg, int msg_size);
//无条件接受消息

int rt_mbx_receive_wp (MBX* mbx, void* msg, int msg_size);
//在不阻塞当期调用的任务的情况下,尽可能多的接收数据。

int rt_mbx_receive_if (MBX* mbx, void* msg, int msg_size);
//整个msg_size大小的消息能立刻获取到时,接收消息。

int rt_mbx_receive_until (MBX* mbx, void* msg, int msg_size, RTIME time);
int rt_mbx_receive_timed (MBX* mbx, void* msg, int msg_size, RTIME delay);
//调用者将被阻塞,直到所有字节的入消息队列、超时过期或发生错误。time是绝对值,Delay是相对于当前时间的相对值.

函数返回
成功,返回收到的字节数量。
失败,返回一个负值,定义如下:EINVAL mbx指向无效的mailbox(mbx points to not a valid mailbox).

3. 示例代码

/* messqueue.c*/
/* ------------ headers ------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <linux/errno.h> /* error codes */
#include <signal.h>
#include <pthread.h>

#include <rtai_lxrt.h>
#include <rtai_mbx.h>
#include <rtai_msg.h>

/* ------------ globals ------------------------------------------- */

/* turn on(1) or off(0) debugging */
const int DEBUG=1;

// fix include warning
//#include <asm/system.h>
//#include <rtai_posix.h>
int pthread_cancel_rt(pthread_t thread);

// linux threads id's
static int thread1;
static int thread2;
// realtime task structures
static RT_TASK  *t1;
static RT_TASK  *t2;

// message queue
static MBX *mesgQueueId;
#define MAX_MESSAGES 100
#define MAX_MESSAGE_LENGTH 50

/* ------------ functions ------------------------------------------- */

void taskOne(void *arg)
{
    int retval;
    char message[] = "Received message from taskOne";

    /*  make this thread LXRT soft realtime */
    t1 = rt_task_init_schmod(nam2num("TASK1"), 0, 0, 0, SCHED_FIFO, 0xF);
    mlockall(MCL_CURRENT | MCL_FUTURE);

    // makes task hard real time (only when not developing/debugging)
    if ( !DEBUG )  rt_make_hard_real_time();

    rt_printk("Started taskOne\n");   
    /* send message */
    retval = rt_mbx_send(mesgQueueId, message, sizeof(message));
    if (0 != retval) {
        if (-EINVAL == retval) {
          rt_printk("mailbox is invalid\n");
        } else {
          /* unknown error */
          rt_printk("Unknown mailbox error\n");
        }
    }

}

void taskTwo(void *arg)
{
    int retval;
    char msgBuf[MAX_MESSAGE_LENGTH];

    /*  make this thread LXRT soft realtime */
    t2 = rt_task_init_schmod(nam2num("TASK2"), 0, 0, 0, SCHED_FIFO, 0xF);
    mlockall(MCL_CURRENT | MCL_FUTURE);

    // makes task hard real time (only when not developing/debugging)
    if ( !DEBUG )  rt_make_hard_real_time();

    rt_printk("Started taskTwo\n");

    /* receive message */
    retval = rt_mbx_receive_wp(mesgQueueId, msgBuf, 50);
    if (-EINVAL == retval) {
          rt_printk("mailbox is invalid\n");
    } else {
        rt_printk("message length=50-%d\n",retval);
        rt_printk("message : %s\n",msgBuf); 
    }
}

void cleanup(void)
{
    /* delete message queue */      
    rt_mbx_delete(mesgQueueId);

    // task end themselves -> not necesssary to delete them 
    return;
}

/* ------------ main ------------------------------------------- */

int main(void)
{   
    printf("Start of main\n");

    /*  make this thread LXRT soft realtime */
    rt_task_init_schmod(nam2num("TASK0"), 0, 0, 0, SCHED_FIFO, 0xF);
    mlockall(MCL_CURRENT | MCL_FUTURE);

    // set realtime timer to run in oneshot mode    
    rt_set_oneshot_mode();
    // start realtime timer and scheduler
    start_rt_timer(1);


    /* create message queue */    
    mesgQueueId = rt_typed_mbx_init (nam2num("MSGQUEUE"), MAX_MESSAGES, FIFO_Q);
    if (mesgQueueId  == 0) {
        printf("Error creating message queue\n");
        return 1;
    }

    // create the linux threads 
    thread1 = rt_thread_create(taskOne, NULL, 10000);
    thread2 = rt_thread_create(taskTwo, NULL, 10000);


    // wait for end of program
    printf("TYPE <ENTER> TO TERMINATE\n");
    getchar();

    // cleanup
    cleanup();

    printf("Finished\n");    
    return 0;
}

六,共享内存通讯

一. 创建函数

void *rtai_malloc_adr (void *adr, unsigned long name, int size);
void *rtai_malloc (unsigned long name, int size);
void *rtai_kmalloc (unsigned long name, int size);
//rtai_malloc_adr和rtai_malloc用于在用户空间分配而rtai_kmalloc用于在内核空间分配。

参数
adr 用户期望的地址,被分配的内存应该映射到用户空间。
name 无符号长标识符
size 需要的共享内存数量
由于name可以是一个复杂的标识符,系统提供了将6字符标识符转换为无符长整型的服务,反之亦然。由函数nam2num 和 num2nam实现。

unsigned long nam2num (const char *name);
void num2nam(unsigned long id, const char* name);

必须说,第一个分配做一个真正的分配, 从Linux进程调用的任何具有相同名称的后续分配,只是将区域映射到用户空间,或者返回相关指针到已经在内核空间中分配的空间。
这个函数返回一个指针指向被分配的内存,适当地映射到使用的内存空间。

函数返回
成功,返回一个有效地址
失败,返回0.
注意:如果同一个进程在同一个进程中调用了rtai_malloc_adr 和 rtai_malloc两次,将会在第二次调用时返回0.

二. Free函数

void rtai_free (unsigned long name, void *adr);
void rtai_kfree(void *adr); 

rtai_free用于释放用户空间中一个以前分配的共享内存,rtai_kfree是用来在内核空间中做同样的操作。

参数
Name 当内存被分配时候使用的无符号长整型标识符
Adr 对应的地址
注意
类似分配函数的操作,释放调用只对取消映射共享内存有影响,这些共享内存到最后被释放,这时才是真正的释放分配的内存。

4. 示例代码(http://chrtai.sourceforge.net/

/*
  shm_task.c

  Set up a periodic task that increments a heartbeat array in shared memory.
*/

#define MODULE
#define __KERNEL__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>  /* ENOMEM */
#include <stddef.h>       /* sizeof() */
#include "rtai.h"
#include "rtai_sched.h"
#include "rtai_shm.h"
#include "common.h"     /* SHM_KEY, SHM_HOWMANY */

MODULE_LICENSE("GPL");

RT_TASK shm_task;
static RTIME shm_period_ns = 100000;

static int * shm_array = 0;

static void shm_function(int arg)
{
  int heartbeat = 0;
  int t;

  while (1) {
    heartbeat++;
    /* make sure the memory has been allocated before we write to it */
    if (0 != shm_array) {
      /* now write to it */
      for (t = 0; t < SHM_HOWMANY; t++) {
    shm_array[t] = heartbeat;
      }
    }
    rt_task_wait_period();
  }

  return;
}

int init_module(void)
{
  int retval;
  RTIME shm_period_count;

  shm_array = rtai_kmalloc(SHM_KEY, SHM_HOWMANY * sizeof(int));
  if (0 == shm_array) {
    return -ENOMEM;
  }
  rt_set_periodic_mode();
  shm_period_count = nano2count(shm_period_ns);
  start_rt_timer(shm_period_count);
  retval = rt_task_init(&shm_task, shm_function, 0, 1024,
             RT_LOWEST_PRIORITY, 0, 0);
  if (0 != retval) {
    return retval;
  }
  retval = rt_task_make_periodic(&shm_task,
                 rt_get_time() + shm_period_count,
                 shm_period_count);
  if (0 != retval) {
    return retval;
  }
  return 0;
}
void cleanup_module(void)
{
  rt_task_delete(&shm_task);

  rtai_kfree(SHM_KEY);

  return;
}

/*--------------------------------------------*/
#include <stdio.h>        /* printf() */
#include <stddef.h>       /* sizeof() */
#include <signal.h>       /* signal(), SIGINT */
#include <sys/mman.h>     /* PROT_READ, needed for rtai_shm.h */
#include <sys/types.h>        /* off_t, needed for rtai_shm.h */
#include <sys/fcntl.h>        /* O_RDWR, needed for rtai_shm.h */
#include <rtai_shm.h>     /* rtai_malloc,free() */
#include "common.h"     /* SHM_KEY, SHM_HOWMANY */

/*
  This signal handler just sets the 'done' flag, which we will loop
  on when printing the shared memory. This lets us quit nicely.
 */
static int done = 0;
static void quit(int sig)
{
  done = 1;
}
int main(void)
{
  int * shm_array;
  int head, tail;

  shm_array = rtai_malloc(SHM_KEY, SHM_HOWMANY * sizeof(int));
  if (0 == shm_array) {
    fprintf(stderr, "can't allocate shared memory\n");
    return 1;
  }
  /*
    Attach our signal hander to SIGINT, the signal raised when we hit
    Control-C.
   */
  signal(SIGINT, quit);

  while (! done) {
    /*
      Read the first and last element of the array. The should be
      the same, but if we are interrupted by the RT code in between
      these reads, the tail will be newer.
    */
    head = shm_array[0];
    /* we could get interrupted here */
    tail = shm_array[SHM_HOWMANY - 1];
    /*
      Print out head and tail of shared memory, and an error if
      they're different.
    */
    printf("%10d\t%10d\r", head, tail);
    if (head != tail) {
      printf("\nsplit read\n");
    }
  }
  /*
    Control-C stopped us, so let's exit nicely by freeing up our connection
    to shared memory.
   */
  rtai_free(SHM_KEY, shm_array);
  return 0;
}
七,中断

本文档为整理及示例代码展示,相关内容可见文档rtai_man_API官方说明第68-75页。

1. PIC(可编程中断控制器)管理函数

#include "rtai.h"
void rt_startup_irq(unsigned int irq);
//启动并初始化PIC确认中断请求irq

void rt_shutdown_irq(unsigned int irq);
//关闭PIC,不再确认中断请求irq

void rt_enable_irq(unsigned int irq);
//使能PIC中断请求irq

void rt_disable_irq(unsigned int irq);
//禁止PIC中断请求irq

void rt_mask_and_ack_irq(unsigned int irq);
//屏蔽(mask)中断请求,并承认,这样一旦CPU启用中断了,其他中断就能被确认,它们依赖于处理的PIC和编程方式。

void rt_unmask_irq (unsigned int irq);
//不屏蔽PIC中断请求irq,这样相关请求就能再次中断CPU,只要它还被承认

void rt_ack_irq (unsigned int irq);
//承认PIC中断请求irq,这样相关请求就能再次中断CPU,只要它没被屏蔽

以上函数允许您操纵手上的PIC,但是你必须清楚你在做什么。这种职责不适合本手册,你应该参考你的PIC数据表。

注意,linux具备相同的功能,但是只能用来处理它们自己的中断,只有以上函数能安全地在实时处理程序中使用。

再次声明,当你安装了一个实时处理程序时,在传递控制到你的中断处理程序前,RTAI要么已经调用了rt_mask_and_ack_irq,来处理水平触发中断(level triggered interrupts),要么已经调用了rt_ack_irq来处理边缘触发中断(edge triggered interrupts)。通常你应该只在适当的时候对水平触发中断调用rt_unmask_irq,对边缘触发中断什么都不做。在后一种情况下,一旦你使能了CPU级别的中断,应该尽快允许任何在相同的请求上的新中断。

通常某些上面的函数能做等效的事情。除了通过了解硬件没有正确处理方法时,你可以再一次手动操作一次。

而且你必须记住,当你安装一个实时处理程序时,相关的中断通常是禁止的,除非你超出(overtake)了那个linux已有的已经被它使能了的那个。记住如果已经正确处理了,中断也没有出现,有可能你只需rt_enable_irq你的irq。

返回:
无。在24.1.11rt_start_irq返回的不是void类型,而是unsigned long。

int rt_assign_irq_to_cpu (int irq, int cpu);
//强迫外部中断irq的分配转到CPUcpu
int rt_reset_irq_to_sym_mode (int irq);
//重置中断irq为对称(symmetric)中断管理,对称模式在所有CPU上分配IRQ。

返回:
如果系统上只有一个CPU,返回1
如果至少有两个,成功,返回0
如果cpu指向的是不存在的CPU,返回CPU的数目
其他失败,返回一个负值
负值定义如下:
EINVAL (irq 不是有效的IRQ数或者发现某些内部数据不一致。
(irq is not a valid IRQ number or some internal data inconsistency is found.)

注意:
- 这些功能只对多处理器系统有影响。
- 有了linux2.4.xx,这样的服务最终在原始内核本地可用
- 有了这样的linux版本,rt_reset_irq_to_sym_mode,重置原始linux交付模式,或者它们称之为的交付亲和力(deliver affinity)。警告:这样的名字主要是为了兼容性原因才保存的,对于这样一个内核,重置操作不一定意味着对称外部中断交付。

int rt_request_global_irq (unsigned int irq, void (*handler)(void));
//为IRQ级别的irq安装函数handlers实时中断服务程序,最后窃取到LINUX。随后handlers在中断数irq发生的时候被援用。安装的handler必须处理好他窃取的使用相同的irq数的linux handler,用rt_pend_linux_irq来适当激活

int rt_free_global_irq (unsigned int irq);
//卸载中断服务程序,如果之前已经被内核拥有,则为linux重新设置它

int request_RTirq (unsigned int irq, void (*handler)(void));
int free_RTirq (unsigned int irq);
//rtai.h里定义的宏,只有我们为2.0.35做的变体RT_linux以向后兼容支持它。他们完全等效于上面两个函数。

int rt_request_linux_irq (unsigned int irq, void (*handler)(int irq, void *dev_id, struct pt_regs *regs), char *linux_handler_id, void *dev_id);
//作为标准linux中断服务进程为IQO级别的irq安装函数处理程序,迫使linux和其他中断处理程序共享IRQ,就算它不希望这样。处理程序附加到任何已经存在的处理同样的irq的linux处理程序上,作为linux irq自己的处理程序运行。这样实时应用可以以自身的意愿监视linux中断处理程序。处理程序出现在/pro/interrupts.
//linux_handler_id 是/pro/interrupts的名称。参数dev_id是以与标准linux irq请求调用同样的方式传给中断处理程序的。

int rt_free_linux_irq (unsigned int irq, void *dev_id);
//

void rt_pend_linux_irq (unsigned int irq);
//给linux服务一个中断,附加一个linux中断irq用于linux IRQ模式处理,也就是,同时硬件中断完全启用。

示例代码

#include "rtai.h"
#include "rtai_srq.h"
int rt_request_srq (unsigned int label, void (*rtai_handler)(void),long long (*user_handler)(unsigned int whatever));
int rt_free_srq (unsigned int srq);
void rt_pend_linux_srq (unsigned int srq);

2. 内核模块(?)
This is the ways to write your ISR to prevent it from being preempted:

your_isr()
{
    disable_sched();

    //Your actual ISR code goes here....

    enable_sched();
}

在RTAI和Linux间共享IRQ

static struct pci_dev* plx;

static void PLX_linuxPostIrqHandler( int irq, void *dev_id,
                                 struct pt_regs *regs)
{
    rt_enable_irq(plx->irq);
}

static void PLX_irqHandler(void )
{
...
    /* Lock scheduling */
    rt_sched_lock();

    // Do the realtime irq handling if required

...
    /* The irq is not for me, give to linux */
    if (!myIrq)
    {
        rt_mask_and_ack_irq(plx->irq);
        rt_pend_linux_irq(plx->irq);
    }

    /* Unlock scheduling */
    rt_sched_unlock();
}


//init_module
...
/* Register irq handler */
rt_request_global_irq( plx->irq, PLX_irqHandler );
rt_request_linux_irq( plx->irq, PLX_linuxPostIrqHandler,
                            "LINUX_POST_IRQHANDLER", PLX_linuxPostIrqHandler );
    rt_startup_irq(plx->irq);
    rt_enable_irq( plx->irq );
..

//cleanup
...

rt_disable_irq( plx->irq );
rt_free_global_irq( plx->irq );
rt_free_linux_irq( plx->irq, PLX_linuxPostIrqHandler );






















阅读更多

没有更多推荐了,返回首页