unimrcp源碼窺探及task異步架構的學習(一)(Framework Agent)

 
設置日誌DEBUG級別,對照日誌從main函數進入處理流程。必要時候用gdb工具單步執行調試。
 
一、task分析
瞭解task 的一切,從task創建開始。先來了解一下,apt_task_t這個結構體中包含了哪些數據。
  • 父task鏈表節點(註釋的說法是這樣的)  link
說明,環(ring)是一種雙向鏈表,可以在不知道其頭部在哪裏的情況下進行操作。APR中的環的介紹,請看另外一篇文章的詳解。
定義如下:      
     APR_RING_ENTRY(apt_task_t) link;                 /* entry to parent task ring */
 
 APR_RING_ENTRY是一個宏定義,按照宏定義展開的話,以上的定義是這樣的結構:
struct {                               \
   struct apt_task_t * volatile next;  \
   struct apt_task_t * volatile prev;  \
}link;
 
由此可見,link爲我們記錄了訪問父task鏈表的入口節點。
 
  • 子task的鏈表頭(按照註釋的理解) head
    APR_RING_HEAD(apt_task_head_t, apt_task_t) head; /* head of child tasks ring */
我們把APR_RING_HEAD的宏定義展開,得到實際的結構:
struct apt_task_head_t {                            \
    struct apt_task_t * volatile next;                    \
    struct apt_task_t * volatile prev;                    \
}head;
 
  • 無類型指針 obj
obj是與任務關聯的外部對象
 
 
  • 消息池   msg_pool
 
  • 互斥鎖 data_guard
 
  • 虛方法表結構 vtable
虛方法表結構中有三類函數指針:
task的方法,  start/ run/ terminate/destroy方法
消息處理的有關方法, 發信號消息處理(signal_msg) 、消息處理(process_msg)、消息處理啓動(process_start)、消息處理終止(process_terminate)
事件處理的有關方法, pre_run / post_run/  on_start_complete / on_terminate_complete/ on_offline_complete / on_online_complete  
 
紅色標註的三個方法,在 apt_task_create函數中分別進行了賦值,由此可見他們是所有task通用的方法,沒有自定義:
    task->vtable.terminate= apt_task_terminate_request;
    task->vtable.process_start= apt_task_start_process_internal;
    task->vtable.process_terminate= apt_task_terminate_process_internal;
 
二、task實體
那麼分析完apt_task_t以後,我們來解剖一下umc客戶端,看看運行這樣一個程序需要啓動的task實體類型。
 
1.Framework Agent
 配置類型?  對應的是UmcFramework的實例
最外層這個大的Agent,定義了UmcFramework這個類來進行管理。
重點關注m_pTask(apt_consumer_task_t*)變量 、m_pMrcpClient(mrcp_client_t*)變量和m_pMrcpApplication變量(mrcp_application_t*)
下面對此進行分解。首先是拿m_pTask來講一下。
①創建task  對應的函數是UmcFramework::CreateTask()
先調用apt_consumer_task_create去創建一個consumer_task結構,在這創建的過程中,首先就要創建apt_task_t的結構,並將apt_task_t作爲成員變量(base)存放於apt_consumer_task_t結構中。
 
②虛方法表中的run方法、signal_msg方法,在此處被定義賦值,既然是使用的 consumer,自然run方法和 singal_msg方法是跟consumer強關聯的:
static apt_bool_t apt_consumer_task_msg_signal(apt_task_t *task, apt_task_msg_t *msg);
static apt_bool_t apt_consumer_task_run(apt_task_t *task);
這兒兩個函數指針實例化,體現的正好是生產者/消費者模型。注意,不要跟apt_consumer_task_t的概念弄混淆。
 
signal後綴的函數,幹一個事情,就是將一個apt_task_msg_t類型的消息,壓入到消息隊列中(生產)。
run後綴的函數,裏面是一個無線循環,從消息隊列中取消息,然後進行處理(消費)。具體實現上,要區分消息類型,根據消息種類不同(TASK_MSG_CORE/ TASK_MSG_USER),交給不同的函數去處理:
TASK_MSG_CORE類型的消息,是通過apt_task_msg_process去調用apt_core_task_msg_process函數進行處理
TASK_MSG_USER類型的消息,是通過apt_task_msg_process去調用process_msg這個函數指針,目前執行到這兒這個函數指針還沒有實例化,是一個空指針。
 
處理完上述內容以後,回到CreateTask函數繼續, 接下來拿到虛方法表結構,在這裏先給process_msg方法賦值,也就是上述生產者/消費者模型中,真正要處理TASK_MSG_USER消息的地方。
再把兩個事件處理方法進行實現,就是on_start_complete方法和on_terminate_complete方法。
on_start_complete方法會在process_start執行的時候被調用到,
 
③最後一步,執行apt_task_start()函數,啓動這個task!
默認是會再啓一個線程執行啓動操作的。線程中按照時間軸處理,流程是這樣:
 
觸發on_pre_run事件(當前該task沒有定義該事件的觸發)
 
             ↓
 
    修改task狀態機
 
             ↓
 
process_start處理    
  • 實際執行apt_task_start_process_internal函數
  • 從子任務隊列中取出任務並執行(如果有)
  • 觸發on_start_complete事件(沒有子任務的情況下)
                                 
             ↓
 
      執行task
  • 虛方法表例的run方法,已用apt_consumer_task_run賦值
  • 無線循環,從消息池中取消息並消費消息
 
             ↓
 
task退出運行,修改task狀態機
 
             ↓
 
觸發on_post_run事件
 
 
 
Done!這樣Framework Agent跑起來了!
寫在最後:
 on_start_complete事件中,完成了sip協議棧、mrcp協議棧的工作線程的創建和啓動。詳細可以見(二)中的解析。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章