Xenomai 再探

2023-03-23 12:08:44

一、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的名稱獲取指定的佇列控制程式碼,從而進行資料交換的操作,其基本使用流程如下:

    graph TD; 實時執行緒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