設置日誌DEBUG級別,對照日誌從main函數進入處理流程。必要時候用gdb工具單步執行調試。
一、task分析
瞭解task 的一切,從task創建開始。先來了解一下,apt_task_t這個結構體中包含了哪些數據。
說明,環(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鏈表的入口節點。
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是與任務關聯的外部對象
虛方法表結構中有三類函數指針:
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處理
↓
執行task
↓
task退出運行,修改task狀態機
↓
觸發on_post_run事件
Done!這樣Framework Agent跑起來了!
寫在最後: