Xenomai 再探

一、Xenomai API 接口使用总结

 

  1. Alarm-操作: 在使用实时任务过程中,采用看门狗定时器进行延时操作时,会产生实时域到非实时域的上下文切换操作,从而导致实时线程实时性受到影响,具体如下:

    void RT_TASK_CallBack_Handle(void *pUsrArg)
    {
        int err;
        T_RT_PARAM *rt_param = (T_RT_PARAM *)pUsrArg;
        rt_printf("## Running=[%d,%d] ##:%s-%d\n", rt_param->Param1, rt_param->Param2, __FUNCTION__, __LINE__);
    
        while(1)
        {
            err = rt_alarm_wait(&rt_param->alarm_desc); // 
    
            rt_printf("Hello Xenomai World!\n");
        }
    }
    

    通过xenomai程序状态文件查看 MSW 参数的情况,发现其一直在增加:cat sched/stat

    root@MM5718v1:/proc/xenomai# cat sched/stat 
    CPU  PID    MSW        CSW        XSC        PF    STAT       %CPU  NAME
    0  0      0          15690      0          0     00018000   99.8  [ROOT/0]
    0  2869   14         17         77         0     000680c0    0.0  RTDemoExe
    0  2871   1          21         44         0     00040042    0.0  timer-internal
    0  2872   20         40         47         0     00048042    0.0  TEST_TASK
    0  0      0          69867498   0          0     00000000    0.2  [IRQ20: [timer]]
    

    结论:不要在实时线程任务中启用Alarm函数进行延时处理,会存在上下文切换过程。


  2. Cond-操作: 在使用 err = rt_cond_bind(&cond, "TEST_COND_VAR_NAME", TM_INFINITE); 函数去跨函数绑定条件变量的过程中,此函数必须在实时任务中去调用,否则调用不成功,且不会阻塞等待到该条件变量的创建。 程序按下 s 即可触发执行

    rt_printf("&&& Hello KeyboardMonitor World!\n");
    err = rt_cond_bind(&cond, "TEST_COND_VAR_NAME", TM_INFINITE);
    rt_printf("### Hello KeyboardMonitor World!\n");
    

    上述代码块必须在实时线程中调用才会生效。bind的功能就像是socket编程中的bind功能,能够阻塞获取到指定名称的信号量对象,从而保证在当前实时线程中也能够获取到对应的信号量,从而完成实时线程控制。

    clock_gettime(CLOCK_REALTIME, &time_stamp);
    time_stamp.tv_sec += 5; // 需要将cond等待时间向后设置5s作为终止时间
    rt_printf("The Current TimeStamp=[%ld:%ld]\n", time_stamp.tv_sec, time_stamp.tv_nsec);
    
    err = rt_cond_wait_timed(&cond, &mutex_var, &time_stamp); // 设置了cond条件等待的时间节点,如果到达时间节点,条件为被设置则返回超时错误 -ETIMEDOUT
    if(0 != err)
    {
        rt_printf("Xenomai-CondVariable wait Error:[%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM);
    }
    

    注意: 目前对于cond条件的 rt_cond_broadcast 以及 rt_cond_signal 还未测试成功!


  3. Queue-操作: Xenomai提供了一套IPC实时线程通信方案 rt_queue , 能够采用 bind 的方式在不同的实时线程中通过queue的名称获取指定的队列句柄,从而进行数据交换的操作,其基本使用流程如下:

    实时线程Task1

    定义Task1-rt_queue队列

    阻塞绑定队列1rt_queue_bind

    定时获取队列数据rt_queue_receive_timed

    使用相关数据

    释放数据内存rt_queue_free

    实时线程Task2

    定义Task2-rt_queue队列

    阻塞绑定队列2rt_queue_bind

    申请数据内存rt_queue_alloc

    更新内存数据内容

    发送队列数据rt_queue_send

    主实时线程Task

    定义原始rt_queue队列

    创建相关队列rt_queue_create

    while

    相关测试代码如下(编译程序,输入i键即可):

    void RT_TASK_Queue_CallBack_Handle(void *pUsrArg)
    {
        int err;
        int counter = 0;
        int SamplePeriod = 200000000;
    
        T_RT_PARAM *rt_param = (T_RT_PARAM *)pUsrArg;
        rt_printf("## Running=[%d,%d] ##:%s-%d\n", rt_param->Param1, rt_param->Param2, __FUNCTION__, __LINE__);
    
        RT_QUEUE rt_queue;
        RT_QUEUE_INFO rt_queue_info;
    
        rt_printf("&&& Hello KeyboardMonitor World!\n");
        err = rt_queue_bind(&rt_queue, "RT_QUEUE_DEMO", TM_INFINITE);
        rt_printf("### Hello KeyboardMonitor World!\n");
    
        err = rt_queue_inquire(&rt_queue, &rt_queue_info);
        rt_printf("The KeyBoard RT-Queue[%s]:\n", rt_queue_info.name);
        rt_printf("  Number of task currently waiting on the queue for messages:[%d]\n", rt_queue_info.nwaiters);
        rt_printf("  Number of messages pending in queue:[%d]\n", rt_queue_info.nmessages);
        rt_printf("  Queue Mode Bits:[%d]\n", rt_queue_info.mode);
        rt_printf("  Maximum number of messages in queue:[%d]\n", rt_queue_info.qlimit);
        rt_printf("  Size of memory pool for holding message buffers:[%d]\n", rt_queue_info.poolsize);
        rt_printf("  Amount of memory consumed from the buffer pool:[%d]\n", rt_queue_info.usedmem);
    
        struct timespec time_stamp;
    
        void *buf_addr = NULL;
        ssize_t buf_size;
    
        rt_task_set_periodic(NULL, TM_NOW, SamplePeriod); // 200ms更新一次
        while(1)
        {
            rt_task_wait_period(NULL);
            rt_printf("Hello Xenomai-CondVariable World[%d]!\n", counter);
    
            clock_gettime(CLOCK_REALTIME, &time_stamp);
            time_stamp.tv_nsec += 100000;
            // rt_printf("The Current TimeStamp=[%ld:%ld]\n", time_stamp.tv_sec, time_stamp.tv_nsec);
    
            buf_size = rt_queue_receive_timed(&rt_queue, &buf_addr, &time_stamp);
    
            if(buf_size > 0)
            {
                rt_printf("Show the Buffer Content:");
                for(int i=0 ; i < buf_size ; i++)
                {
                    rt_printf(" %d", ((char *)buf_addr)[i]);
                }
                rt_printf("\n");
            }
            rt_queue_free(&rt_queue, buf_addr);
        }
    
        err = rt_queue_unbind(&rt_queue);
    }
    

  4. HEAP-操作: 在使用Xenomai预先申请的内存池内容时,Xenomai提供了 heap 相关的操作API,在创建heap过程中,如果配置heap模式为 H_SINGLE 则在 rt_heap_alloc 函数调用时,需要将所有内存全部申请完,否则程序报错。同时 Heap 内存操作还提供了 rt_heap_bind 的绑定功能,从而能够方便 RT-Task 之间进行 IPC 内存共享通讯。具体参考程序:MainRTHeap.c

    root@MM5718v1:~/Burnish# ./RTDemoExe 
    Heap Informations:
    Number of tasks waitting for aviliable memory alloc:0
    The Heap Mode Flags given while Creation:0
    Size of the Heap(Bytes) While Create:10 Bytes
    Maximum amount of memory avaliable from heap:1032 Bytes
    Amount of memory currently consumed:3250 Bytes
    Name of memory heap:HeapTest
    The iPointer[0]=
    0 1 2 3 4 5 6 7 8 9 
    

    🐛 Some-Bugs:
    B1: 在一个实时线程中申请超过约 1MB1MB 的内存时,发生错误 [-ENOMEM] ,当前系统配置总内存 (52428800/1024/1024=50Mb)(52428800/1024/1024=50Mb) 如下所示:

    root@MM5718v1:~# cat /proc/xenomai/heap 
        TOTAL      FREE  NAME
    52428800  52427776  system heap
    4194304   4194176  shared heap
    

    测试情况如下:

    • Heap 被 rt_heap_bind 接口绑定的情况下只能申请到 200KB 的内存大小。
    • Heap 在单一线程中可申请的最大 Heap 空间为 1MB1MB 左右,也就是在 Xenomai 的实时线程中,对于单一实时线程的最大 Heap 申请大小进行了限制,这与Linux系统下的单进程的分配空间存在上限的情形相似。

  5. Mutex-操作: 在使用Xenomai架构支持的Mutex锁资源的相关接口过程中,基本上和普通线程的 Mutex-Lock 的使用方式基本一致,只是Xenomai的所有资源的共享方式都可以使用 bind 绑定的方式完成,因而无需在线程之间传递 Mutex 变量(这是由于Xenomai架构底层决定的,其所有资源的申请都不是临时/创建时申请的,而是在架构初始化时就完成了相关资源的初始化,后续只是在资源池中取出来分配给大家使用)。MainRTMutex.c

    root@MM5718v1:~# ./Burnish/RTDemoExe 
    ## Running ##:RT_TASK_CallBack_HandleA-68
    Heap Informations:
    Number of tasks waitting for aviliable memory alloc:0
    The Heap Mode Flags given while Creation:1
    Size of the Heap(Bytes) While Create:10 Bytes
    Maximum amount of memory avaliable from heap:1032 Bytes
    Amount of memory currently consumed:3250 Bytes
    Name of memory heap:HeapTest
    Task Get Info Error:[-22,-22,-43,-4,-11,-110,-1,-12,-17]!
    Mutex Informations:
    Owner-Current Mutex Hold Task@Name:»
    Owner-Current Mutex Hold Task@Priority:-1225016136
    Name of Sync Mutex Lock:MutexTest
    The RT_TASK_CallBack_HandleA iPointer[0]=0 0 0 0 0 0 0 0 0 0   ===>   0 1 2 3 4 5 6 7 8 9 
    ## Running ##:RT_TASK_CallBack_HandleB-212
    Heap Informations:
    Number of tasks waitting for aviliable memory alloc:0
    The Heap Mode Flags given while Creation:0
    Size of the Heap(Bytes) While Create:10 Bytes
    Maximum amount of memory avaliable from heap:1032 Bytes
    Amount of memory currently consumed:3274 Bytes
    Name of memory heap:HeapTest
    Task Get Info Error:[-22,-22,-43,-4,-11,-110,-1,-12,-17]!
    Mutex Bind Informations:
    Owner-Current Mutex Hold Task@Name:
    Owner-Current Mutex Hold Task@Priority:0
    Name of Sync Mutex Lock:MutexTest
    The RT_TASK_CallBack_HandleB iPointer[0]=0 1 2 3 4 5 6 7 8 9   ===>   100 101 102 103 104 105 106 107 108 109 
    The RT_TASK_CallBack_HandleA iPointer[0]=100 101 102 103 104 105 106 107 108 109   ===>   0 1 2 3 4 5 6 7 8 9 
    The RT_TASK_CallBack_HandleB iPointer[0]=0 1 2 3 4 5 6 7 8 9   ===>   100 101 102 103 104 105 106 107 108 109 
    The RT_TASK_CallBack_HandleA iPointer[0]=100 101 102 103 104 105 106 107 108 109   ===>   0 1 2 3 4 5 6 7 8 9 
    The RT_TASK_CallBack_HandleB iPointer[0]=0 1 2 3 4 5 6 7 8 9   ===>   100 101 102 103 104 105 106 107 108 109 
    The RT_TASK_CallBack_HandleA iPointer[0]=100 101 102 103 104 105 106 107 108 109   ===>   0 1 2 3 4 5 6 7 8 9 
    The RT_TASK_CallBack_HandleB iPointer[0]=0 1 2 3 4 5 6 7 8 9   ===>   100 101 102 103 104 105 106 107 108 109 
    

  6. Pipe-操作: 管道Pipe作为一种半双工的IPC通信方式,在同一时刻只允许有一个进程或者线程对Pipe管道对象进行操作,因此需要在写入和读取端之间形成互斥,亦可以通过信号量的方式协调两端数据的读取写入过程,因此在下面的PIPE测试程序中,分别包括的 PipeServer 端、PipeClient 端,分别在两个终端下执行两个应用程序,测试其半双工通信功能。
    RTDemoExePipeServer.c
    I. 数据发送实时线程:

    RT_PIPE pipe;
    const char PipeName[] = "PipeTest";
    
    const char writer_data[] = "Writer PIPE DATA...";
    const char stream_data[] = "Stream PIPE DATA...";
    
    err = rt_pipe_create(&pipe, PipeName, 1, 1000); // minor 用来配置在Linux设备端创建 /dev/rtpN 的过程中配置 N 的号码,当minor=1时,则创建的设备为 /dev/rtp1, 如果设置为P_MINOR_AUTO:设备Pipe节点自动创建功能  poolsize:0 Pipe管道缓冲池的大小,设置为0时则直接使用 Xenomai 系统的Heap空间
    if(0 != err)
    {
        rt_printf("Pipe Create Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    
    while(1)
    {
        data_count = rt_pipe_write(&pipe, writer_data, strlen(writer_data), P_NORMAL); // 使用write的方式完成Pipe数据的传输过程 mode:P_URGENT 标识数据的后入先出顺序 LIFO | P_NORMAL==>FIFO
        rt_task_sleep(10); // rt-thread 实时线程延迟函数 相对延时函数 最小延迟精度 clock ticks 需要绝对延时时间使用 rt_task_sleep_until() 接口
        data_count = rt_pipe_stream(&pipe, stream_data, strlen(writer_data)); // 使用流的方式Stream传输数据
        rt_printf("RT_TASK_CallBack_HandleA %d Datas Send over...\n", data_count);
        rt_task_wait_period(NULL);
    }
    
    err = rt_pipe_delete(&pipe); // 清理mutex资源 对于 Xenomai 的数据资源使用完之后一定要严格回收,否则会导致资源流失,影响整个系统的资源获取
    if(0 != err)
    {
        rt_printf("Pipe Delete Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    

    II. 数据接收实时线程:

    RT_PIPE pipe;
    const char PipeName[] = "PipeTest";
    err = rt_pipe_bind(&pipe, PipeName, TM_NONBLOCK); // 不限时等待绑定 TM_INFINTE TM_NONBLOCK
    if(0 != err)
    {
        rt_printf("Pipe Create Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    
    ssize_t DataLen = 0;
    char data_read[10]={0};
    while(1)
    {
        rt_task_wait_period(NULL); // 不能放在 continue 的下面
        DataLen = rt_pipe_read(&pipe, data_read, 10, TM_NONBLOCK); // TM_INFINITE 永久等待读取到Pipe中的数据 
        if(DataLen < 0)
        {
            rt_printf("Pipe No Data Recieved Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", DataLen, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
            continue;
        }
        rt_printf("Recieve Data[%d]: %s\n", DataLen, data_read);
    }
    
    err = rt_pipe_unbind(&pipe); // TM_INFINITE 永久等待 TM_NONBLOCK
    if(0 != err)
    {
        rt_printf("Pipe Unbind Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    

    RTDemoExePipeClient.c
    数据发送接收普通线程: RTDemoExePipeClient.c

    FILE *PipeHandle = NULL;
    char DataBuffer[30];
    PipeHandle = fopen("/dev/rtp1", "r"); // /dev/rtp1 此设备为 rtpN创建 Pipe 过程中产生的,N的编号为 rt_pipe_create 时,minor参数决定的
    
    ssize_t DataLen = 0;
    
    while(1)
    {
        DataLen = fread(DataBuffer, 1, 30, PipeHandle);
        if( DataLen > 0 )
        {
            for(int i = 0 ; i < DataLen ; i++)
            {
                printf("%c", DataBuffer[i]);
            }
            printf("\n");
        }
        usleep(1000);
    }
    

         通过 rt_pipe_create 接口创建了管道Pipe之后,会在 /proc/xenomai/registry 目录下产生 /rtipc/xddp/PipeTest 文件句柄,此句柄实际上为 /dev/rtp1 设备的 SymbolLink ,如下所示:

    root@MM5718v1:~# ls -al /proc/xenomai/registry/rtipc/xddp/PipeTest 
    lrwxrwxrwx    1 root     root             9 Nov 28 03:55 /proc/xenomai/registry/rtipc/xddp/PipeTest -> /dev/rtp1
    

    我们可以通过 cat /proc/xenomai/registry/rtipc/xddp/PipeTest 命令来查看写入到 PipeTest 管道中的数据:

    Writer PIPE DATA...Stream PIPE DATA...Stream PIPE DATA...Stream PIPE DATA...Stream PIPE DATA...Stream PIPE DATA...
    

    🐛 Some-Bugs:
    B1: 在使用Xenomai提供的实时线程接口创建Pipe管道的过程中,由于Pipe是用来进行核间跨域通信的接口,不要在两个实时线程中尝试使用 Pipe ,经测试是无法完成数据传输功能的,所以这里一定需要注意!


  7. Task-操作:
    Step1. 创建实时任务回调函数:

    void RT_TASK_CallBack_HandleA(void *pUsrArg)
    {
        int ret,err;
        int SamplePeriod = 200000000;
        rt_task_set_periodic(NULL, TM_NOW, SamplePeriod); // 200ms更新一次
        while(1)
        {
            // do something ...
            rt_task_wait_period(NULL); // 等待下一个执行周期的到达
        }
    }
    

    Step2. 在主进程中创建并启动一个实时任务:

    RT_TASK RTTaskHandleA;
    ret = rt_task_create(&RTTaskHandleA, "RTTaskHandleA", TASK_STKSZ, TASK_PRIO, TASK_MODE); // 创建一个实时线程
    ret = rt_task_start(&RTTaskHandleA, &RT_TASK_CallBack_HandleA, NULL); // 启动一个普通的实时任务,不通过 rt_alarm_wait 看门狗定时器延迟等待
    

    Some-Bugs:
    🐛 B1 @ CPU死机?
         在使用Xenomai提供的实时线程接口创建实时任务的过程中,如果在While循环当中使用了 rt_task_wait_period(NULL); 接口来等待当前实时任务的完成,则不能跳过此函数重复执行While,否则将导致CPU被完全占用(死机),具体错误代码示例如下:

        while(1)
        {
            rt_task_wait_period(NULL); // 允许的 rt_task_wait_period 接口位置
            DataLen = rt_pipe_read(&pipe, data_read, 10, TM_NONBLOCK); // TM_INFINITE 永久等待读取到Pipe中的数据 
            if(DataLen < 0)
            {
                rt_printf("Pipe No Data Recieved Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", DataLen, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
                continue;
            }
            // cotinue 一下的位置则不允许的 rt_task_wait_period 调用
            rt_printf("Recieve Data[%d]: %s\n", DataLen, data_read);
        }
    

         因此,从这个角度上可以猜测到 rt_task_wait_period 的功能在于针对确定定时范围内需要完成的任务,如果在定时任务定时范围之内完成的任务,则调用 rt_task_wait_period 接口后会延迟直到定时任务时间到达为止,如果跳过此环节将会导致CPU被独占死机。
    🐛 B2 @ 出现了段错误 rt_task_join 出现了段错误的问题?
         究其原因一定是传入到该函数的 task-handle 任务句柄存在问题,通过排查发现是因为 rt_task_join 函数传入了一个无效的句柄,是由于在申请实时线程资源时未成功申请到而导致的结果。
    🐛 B3 @ 出现了 Lack of Core Thread EGAIN 导致无法正常申请到线程锁,任务无法正常启用?
         排查过程如下,由于出现了线程资源匮乏的问题,考虑到使用的是实时线程,首先针对实时线程的记录文件进行排查,通过查看 cat /proc/xenomai/register/usage 可以观察线程使用的情况,根据实验结果查看,发现线程数量的确在不断的增加,此时考虑到实时线程中某些特殊操作可能影响线程的因素,其中就包括了线程锁的安全问题,具体:由于实时线程的安全锁问题,当使用 rt_task_delete&rt_task_join 函数接口对实时线程进行删除回收时,可以发现实际上线程并未完成回收,尽管此时通过 cat /proc/xenomai/sched/threads 查看到的RT_Task只有几个,实际上是因为 thread 文件中之记录了会被调度到的线程,而其他被删除后的线程不会再被调度了,导致之一问题的原因可能是因为该线程并未释放或者销毁其使用的线程锁,如果其他线程使用了对应的线程锁,将有可能导致该线程死锁,因此考虑到安全问题,被删除终止的实时线程实际上资源并未回收完成,并一直占用了实施线程资源池,通过排查发现,在实时线程初通过 pthread_mutex_create() 创建的锁资源未解锁及释放,因此需要使用 pthread_mutex_unlock & pthread_mutex_destroy 释放销毁即可。
    🐛 B4 @ 反复创建删除 RT_Task 过程中出现了内存增长的问题,而线程数量是未增涨?
         这个问题涉及到了实时线程销毁回收的问题,由于 Destroy 接口并不需要等到该线程的终止,在创建 TrajectoryDestroy_V1 资源回收线程的过程中,由于我使用了 T_JOINABLE 模式创建了该线程,因此在线程结束后的资源回收应该需要 rt_task_join 对该线程进行回收操作才能完成所有资源回收。由于该线程无需 阻塞 join ,故此直接在创建该线程时采用 #define POSGEN_TASK_MODE 0 /* No flags */ 即可,再运行时则发现无内存增涨的情况。


  8. Semaphore-操作: PV-IPC信号操作作为实时线程间的互斥IPC通信方式,用于线程之间的数据信息同步操作,其功能类似于 mutex 的功能,但PV操作在阻塞的情况下可能会出现 优先级翻转的现象 ,而 Mutex 则不会出现这样的情况。
    MainRTSem.c

    RT_SEM sem;
    const char SEMName[] = "SemTest";
    err = rt_sem_create(&sem, SEMName, 2, S_FIFO); // S_PRIO 以优先级排队领取可用Sem S_FIFO 以先到先得的方式领取可用Sem
    if(0 != err)
    {
        printf("Sem Create Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    
    RT_SEM_INFO seminfo;
    err = rt_sem_inquire(&sem, &seminfo); // H_PRIO 以优先级排队领取可用heap H_FIFO 以先到先得的方式领取可用heap  H_SINGLE 将创建的 heap内存当作一整个内存,在alloc申请时必须整块申请,否则会报错
    if(0 != err)
    {
        printf("Sem Get Info Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    printf("Sem Informations:\n");
    printf("  Current Semaphore Value:%ld\n", seminfo.count);
    printf("  Number of tasks waitting for this Semaphore:%d\n", seminfo.nwaiters);
    printf("  Name of Semaphore:%s\n", seminfo.name);
    
    int SamplePeriod = 200000000;
    rt_task_set_periodic(NULL, TM_NOW, SamplePeriod); // 200ms更新一次
    
    while(1)
    {
        rt_sem_p(&sem, TM_INFINITE); // 永久等待 Semaphore 释放
    
        rt_printf("## Running ##:%s-%d\n", __FUNCTION__, __LINE__);
    
    #if SEMAPHORE_BROADCAST_TSET
        rt_sem_broadcast(&sem);
    #else
        rt_sem_v(&sem); // Unblock the semaphore
    #endif
        rt_task_wait_period(NULL);
    }
    
    err = rt_sem_delete(&sem); // 清理mutex资源 对于 Xenomai 的数据资源使用完之后一定要严格回收,否则会导致资源流失,影响整个系统的资源获取
    if(0 != err)
    {
        printf("Semaphore Delete Error:[%d,%d,%d,%d,%d,%d,%d,%d,%d]!\n", err, -EINVAL, -EIDRM, -EINTR, -EWOULDBLOCK, -ETIMEDOUT, -EPERM, -ENOMEM, -EEXIST);
    }
    

    实验结果如下:

    root@MM5718v1:~# ./Burnish/RTDemoExe
    Sem Informations:
    Current Semaphore Value:2
    Number of tasks waitting for this Semaphore:0
    Name of Semaphore:SemTest
    ## Running-SemaphoreValue[1] ##:RT_TASK_CallBack_HandleA-104
    Sem Bind Informations:
    Current Semaphore Value:2
    Number of tasks waitting for this Semaphore:0
    Name of Semaphore:SemTest
    ## Running-SemaphoreValue[1] ##:RT_TASK_CallBack_HandleB-170
    ## Running-SemaphoreValue[1] ##:RT_TASK_CallBack_HandleA-104
    ...
    

    Some-Bugs:
    🐛 B1 @ 运行相关程序运行后报如下错误? 0"000.000| BUG in low_init(): [main] Cobalt core not enabled in kernel

    1. 可能是由于在内核编译的过程中未使能 Xenomai 的部分,或者在Linux编译前,Kernel源代码的实时内核Xenomai补丁未打.

二、Source Code & Compile System

1. main.c

主要测试了 ALRAM;COND;QUEUE;EVENT 相关内容

点击查看代码

 

2. MainRTHeap.c

主要测试了 HEAP 相关内容

点击查看代码

 

3. MainRTMutex.c

主要测试了 MUTEX 相关内容

点击查看代码

 

4. MainRTPipeClient.c

主要测试了 PipeClient 相关内容,需要配合 MainRTPipeServer.c

点击查看代码

 

5. MainRTPipeServer.c

主要测试了 PipeClient 相关内容,需要配合 MainRTPipeClient.c

点击查看代码

 

6. MainRTSem.c

主要测试了 Sem 相关内容

点击查看代码

 

7. MAKEFILE

# Makefile for Basler pylon C sample program
.PHONY: all clean

# The program to build
EXECUTABLE := RTDemoExe # RTDemoExePipeServer RTDemoExePipeClient RTDemoExe
TARGET_IP := 188.188.0.32
# Installation directories for pylon

# Build tools and flags
CC = arm-linux-gnueabihf-gcc
XENO_CONFIG := xgrosconfig
CFLAGS := $(shell $(XENO_CONFIG) --opencv --pylon6 --compat --posix --alchemy --cflags)
LD     := $(shell $(XENO_CONFIG) --opencv --pylon6 --compat --posix --alchemy --ldflags --mysqlc)
LD     := $(LD) -lrt -lpthread -ldl -lm -lcJSON -lcrypto -lssl -lethercat_rtdm
CFLAGS     := $(CFLAGS) -I ../src/ -g

MAKETIME := $(shell date "+%G.%m.%d-%H.%M.%S")
EXEVERSION := 1.0.01N

src = $(wildcard ../src/*.c)
src :=$(src) MainRTHeap.c # MainRTSem.c MainRTPipeClient.c MainRTPipeServer.c MainRTMutex.c MainRTHeap.c main.c
obj = $(patsubst %.c, %.o, $(src))
all: $(EXECUTABLE)
$(EXECUTABLE): $(obj)
	$(CC) -o $@ $^ $(LD)
	scp ./$(EXECUTABLE) root@$(TARGET_IP):~/MMWorkSpace/

%.o:%.c
	$(CC) -o $@ -c $<  $(CFLAGS)

.PHONY: clean
clean:
	rm -f $(EXECUTABLE) $(obj)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Xenomai is a real-time development framework for Linux, which allows developers to create real-time applications on a standard Linux kernel. Docker, on the other hand, is a platform that allows for the packaging and distribution of applications in lightweight, isolated containers. If you are looking to use Xenomai within a Docker container, you can create a Docker image that includes the Xenomai libraries and dependencies. This image can then be used to run Xenomai applications in a containerized environment. To get started, you will need to set up a Dockerfile that defines the steps to build your Docker image. In this Dockerfile, you will specify the base image, install Xenomai and its dependencies, and copy your Xenomai application code into the container. Once the Dockerfile is ready, you can build the image using the 'docker build' command. Here is a sample Dockerfile that demonstrates how to set up Xenomai in a Docker container: ``` FROM ubuntu:latest # Install Xenomai and its dependencies RUN apt-get update && apt-get install -y xenomai # Copy your Xenomai application code into the container COPY app /app # Set the working directory WORKDIR /app # Define the command to run your Xenomai application CMD ["./app"] ``` Once you have built the Docker image using the Dockerfile, you can run your Xenomai application in a container using the 'docker run' command. Please note that configuring and using Xenomai in a Docker container may require additional setup and configuration depending on your specific use case.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2301_76429513

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值