翻譯-pjsip開發者指南(一)總體設計

英文版的看完就忘,打算把它翻譯下來 ,不知道能堅持到第幾章,加油吧,一邊看一邊翻譯,難免有錯請指正。至於排版實在是很費時間的事情,先這樣吧。

 

Chapter 1:General Design

PJSIP是一個用C編寫,佔用資源少,高性能的sip協議棧。
 1.1 Architecture 
1.1.1 Communication Diagram

 下面的圖表展示了sip消息在pjsip組件中的流轉。

 1.1.2 Class Diagram
下面的是類圖

 1.2 The Endpoint
sip協議棧的核心就是sip endpoint,用不透明的類型pjsip_endpoint來表示。endpoint有以下的屬性和功能。
   #pool factory,用來爲所有sip組件分配內存池
   #timer heap instance,用來爲所有所有sip組件設置timer
   #transport manager instance,用來管理和控制sip傳輸,以及解析和打印消息
   #PJLIB’s ioqueu,用來分派網絡事務的proactor模式(個人注:網絡編程中的一種設計模式)
   #polling function,提供線程安全的輪詢功能,應用的線程可以輪詢timer和socket事務(PJSIP自身不具備創建線程的能力)
   #PJSIP module,enpoint管理pjsip module,module是消息解析和傳輸之外的一種主要擴展
   #endpoint從transport manager接收sip消息,然後分發到各個module

 1.2.1 Pool Allocations and Deallocations
 sip組件的所有內存分配都通過endpoint,來保證線程安全和約束整個應用的一致性。其中一個策略就是pool cache,未被使用的內存池不會銷燬而是留作之後使用。
endpoint使用以下方式來分配和釋放內存池
    pjsip_endpt_create_pool()
     pjsip_endpt_release_pool()
endpoint使用 pjsip_endpt_create() 來創建。endpoint一旦創建必須指明應用程序的pool factory。在endpoint整個生命週期內,endpoint都持有該pool factory的指針,並且創建和釋放內存池。
 1.2.2 Timer Management
endpoint有一個timer heap來管理timer。所有sip組件的timer創建和設置都是通過endpoint。
endpoint使用以下方式來管理timer
    pjsip_endpt_schedule_timer()
    pjsip_endpt_cancel_timer()
調用endpoint的輪詢功能來檢查timer的週期。
 1.2.3 Polling the Stack
endpoint通過函數 pjsip_endpt_handle_events()來檢查是否有timer和網絡事務。應用程序可以指定等待某種事務的時間。
PJSIP協議棧從不創建線程。協議棧中正在運行的都可以認爲是application創建的線程,比如API的調用,或者輪詢功能的調用。
輪詢功能可以根據timer heap的內容來優化等待時間。例如,如果知道一個timer在5ms之後結束,那麼就不會花更多的時間等待socket events。當沒有網絡事務的時候,application就沒必要等待那麼久。timer的精確度依據不同平臺有不同的變化。


 1.3 Thread Safety and Thread Complications
1.3.1 Thread Safety

線程安全是一個複雜的問題。不過幸運是整個協議棧都適用以下設計的規範。
       objects必須是線程安全的,而structure必須不是。
對象和結構在這裏來看,區別並沒有很清楚。但是給了幾個例子幫助理解。
數據結構的例子:
 PJLIB’s data structures, such as lists, arrays, hash tables, strings, and memory pools.
 SIP messaging elements such as URIs, header fields, and SIP messages.
這些結構都不是線程安全的,它們的安全性是由包含它們的對象來確定的。如果數據結構是線程安全的,那麼會對協議棧的性能產生影響,並且耗盡操作系統的資源。
相反的, sip的objects必須是線程安全的,以下是例子
    PJLIB objects, such as ioqueue.
     PJSIP objects, such as endpoint, transactions, dialogs, dialog usages, etc.
 1.3.2 The Complications(線程複雜性)
糟糕的是,一些objects在頭文件中暴露了它們的定義( e.g. pjsip_transaction and pjsip_dialog)。儘管這些objects暴露的API確定是安全的,但訪問應用程序代碼中的數據結構的時候,應用程序也必須獲得相應的鎖。比如objects mutex,調用 pj_mutex_lock() 。
更糟糕的是,dialog公開了用於鎖dialog的不同的API。application應調用pjsip_dlg_inc_lock()和pjsip_dlg_dec_lock(),來代替pj_mutex_lock() 和pj_mutex_unlock()。這兩者的區別是,dialog的inc/dec鎖來確保函數調用的dialog不會被銷燬,而dialog的銷燬會引起pj_mutex_unlock()的崩潰。
看下面的例子

   1 pj_mutex_lock(dlg->mutex);
   2 pjsip_dlg_end_session(dlg, ...);
   3 pj_mutex_unlock(dlg->mutex);

在以上的例子中,應用程序可能會在第三句崩潰, 因爲pjsip_dlg_end_session在一定的條件下,會被銷燬。如果發出的invite請求沒有響應,那麼此次事務將被立即銷燬,同時dialog也被銷燬。dialog的inc/dec鎖通過臨時增加dialog會話計數器來防止這個問題,這樣dialog就不會在end_session()函數中被銷燬。dialog可能在dec_lock()裏被銷燬。所以一個dialog恰當的鎖應該是下面這樣的順序。
    1 pjsip_dlg_inc_lock(dlg);
    2 pjsip_dlg_end_session(dlg, ...);
    3 pjsip_dlg_dec_lock(dlg)

不過,lock的順序必須是正確的,否則會有死鎖發生。比如,應用程序想要在一個dialog中對dialog和transaction都上鎖,應用程序必須在transaction的mutex(互斥鎖)之前獲得dialog的mutex,否則其他的線程此時以相反的順序獲取同一個dialog和transaction,就會發生死鎖。
 1.3.3 The Relief
幸運的是,應用程序很少會去直接獲取object的mutex,所以上面說的情況極少會出現。
應用程序應該通過object的API來獲取可用的object,APIs保證了由於object被刪除後的鎖的正確性來避免死鎖,以及程序崩潰。
當object(dialog或transaction)調用應用程序的回調時,這些回調通常在object的鎖已被獲取時調用。所以應用程序可以安全的的訪問object的數據結構而不用去獲取鎖。
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章