談談dpdk應用層包處理程序的多進程和多線程模型選擇時的若干考慮

看到知乎上有個關於linux多進程、多線程的討論:http://www.zhihu.com/question/19903801/answer/14842584html

本身項目裏也對這個問題有過不少探討和測試,因此正好開貼整理一下,題目有點長,其實就2點:linux

1. 多進程模型和多線程模型,這兩種模型在linux上有什麼區別,各有何優缺點?編程

    這裏僅限於linux平臺,由於linux平臺跟win平臺關於線程的實現差別很大。緩存

2. 採用intel dpdk作包處理程序,是採用多進程模型好,仍是多線程模型好?安全

 這裏僅限於包處理程序(ips,waf,其餘網絡設備引擎),由於不一樣應用場景區別也很大。網絡

 

首先知乎裏邊的評論,有個miao網友說的跟個人經驗比較相符,先將其說法貼一下:多線程

"linux使用的1:1的線程模型,在內核中是不區分線程和進程的,都是可運行的任務而已。fork調用clone(最少的共享),pthread_create也是調用clone(最大共享).fork建立會比pthread_create多消耗一點點,由於要拷貝tables和cow mapping.可是其實差異真的很細微,這些在內核開發者的努力下已經變的很小了。
再來講說contex switch的cost吧。線程的context switch是要比process小一些,由於線程共享了大部分的memory和tables,當switch的時候這些東西已經在緩存中了。
可是其實差異也很細微。可是在multiprocessor的系統中不共享memory實際上是會比共享memory要有一點優點的,由於當任務在不一樣的processor中運行的時候,同步memory帶來的損耗是不可忽視的。"架構

 

他這裏說了兩點有價值的信息,1  linux裏的線程實現決定,建立、調度、切換線程的開銷跟進程相比,好不了多少。app

              2 多核CPU下因爲緩存命中率的問題,進程這種天生不共享內存的作法,實際上比線程這種天生共享內存框架

                的作法,從性能上是有好處的。

這兩點看法跟咱們項目實際測試和研究結果是相符合的。下面從幾個方面探討這些問題:

 

1 linux 線程建立方式

linux提供的線程其實是核外線程,即主要的線程機制是經過應用層面的庫pthread提供的(線程的id分配、線程建立和管理,聽說基本實現是pthread庫爲每個進程維護一個管理線程,單調用 pthread_create等posix API時,調用者與該管理線程經過管道傳遞命令),

核內層面,線程幾乎能夠等同於進程。  這裏貼一段從引用1 拷貝的內容:

Linux的線程實現是在覈外進行的,核內提供的是建立進程的接口do_fork()。內核提供了兩個系統調用__clone()和fork(),最終都用不一樣的參數調用do_fork()核內API。 do_fork() 提供了不少參數,包括CLONE_VM(共享內存空間)、CLONE_FS(共享文件系統信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信號句柄表)和CLONE_PID(共享進程ID,僅對核內進程,即0號進程有效)。當使用fork系統調用產生多進程時,內核調用do_fork()不使用任何共享屬性,進程擁有獨立的運行環境。當使用pthread_create()來建立線程時,則最終設置了全部這些屬性來調用__clone(),而這些參數又所有傳給核內的do_fork(),從而建立的」進程」擁有共享的運行環境,只有棧是獨立的,由 __clone()傳入。

         即:Linux下不論是多線程編程仍是多進程編程,最終都是用do_fork實現的多進程編程,只是進程建立時的參數不一樣,從而致使有不一樣的共享環境。Linux線程在覈內是以輕量級進程的形式存在的,擁有獨立的進程表項,而全部的建立、同步、刪除等操做都在覈外pthread庫中進行。pthread 庫使用一個管理線程(__pthread_manager() ,每一個進程獨立且惟一)來管理線程的建立和終止,爲線程分配線程ID,發送線程相關的信號,而主線程pthread_create()) 的調用者則經過管道將請求信息傳給管理線程。

上述內容基本能夠這麼表示:

  建立進程= fork ——> do_fork(不使用共享屬性)

      建立線程= pthread_create——>__clone ——> do_fork(共享地址空間(代碼區、數據區)、頁表、文件描述符、信號。。)

這裏其實另一種多進程建立方式,就是腳本直接啓動多個進程

 

下面再貼一段:

「對於一個進程來講必須有的數據段、代碼段、堆棧段是否是全盤複製呢?對於多進程來講,代碼段是確定不用複製的,由於父進程和各子進程的代碼段是相同的,數據段和堆棧段呢?也不必定,由於在Linux裏普遍使用的一個技術叫copy-on-write,即寫時拷貝。copy-on-write意味着什麼呢?意味着資源節省,假設有一個變量x在父進程裏存在,當這個父進程建立一個子進程或多個子進程時這個變量x是否複製到了子進程的內存空間呢?不會的,子進程和父進程使用同一個內存空間的變量,但當子進程或父進程要改變變量x的值時就會複製該變量,從而致使父子進程裏的變量值不一樣。」

這裏個人理解是,剛fork完,子進程和父進程代碼段、頁表等仍是共享的,接下去有兩種可能發展方向,1是子進程修改了數據,這時候,代碼段:仍然是共享的,不須要拷貝;堆和靜態數據區: 根據copy-on-wirte機制,不改變值的地方仍然共享,改變值的地方須要從新申請物理頁面並修改值,修改頁表(可能還要拷貝頁表);棧: 無論進程仍是線程,都不能共享,都須要建立的時候分配棧區。2是fork以後立刻調用 exec 用新的進程替換,這時候會載入新的代碼段、數據段,構建新的頁表。

對於咱們的包處理系統而已,不管怎麼啓動,建立時的性能開銷實際上是無所謂的,由於都是在系統初始化的時候建立。

 

2 調度和切換

因爲核內的線程本質就是進程,其調度過程跟進程同樣。切換,不管是進程切換仍是線程切換,都須要替換運行環境(內核堆棧,運行時寄存器等),對於內存的切換,內核部份內存是同樣的,用戶空間部分:若是是進程,須要替換頁目錄基址寄存器,若是是線程,不須要替換;整體而言,linux進程和線程的切換,從內存寄存器、內核堆棧寄存器、其餘寄存器等的換值開銷應該是差很少的。具體切換代碼參考引用2

可是因爲多線程共享地址空間,從一個線程切換到同一個進程上另外一個線程運行,頁表,數據區等不少都已經在內存甚至緩存裏,而從一個進程切換到另外一個進程,可能因爲剛切換進來的進程的頁面被虛擬內存管理模塊替換出去致使的頁面替換開銷,另外還有緩存tlb失效致使的緩存更新開銷,這裏性能有所差異。

 對於咱們的包處理系統而已,採用多核架構,主體進程/線程是綁定到不一樣的物理CPU core上並獨佔的,因此發生調度和切換的狀況很少,於是這種影響不是很重要。

3. 地址空間共享相關問題

進程地址空間是獨立的,這意味着,不一樣進程的內存天生就是不共享的,若是要共享,則須要開發者本身構建共享機制,好比使用IPC。

線程地址空間是共享的,這意味着,同一進程不一樣線程的內存天生是共享的,若是想要不共享,須要開發者本身實施,好比使用線程本地變量。

進程模型和線程模型,地址空間不共享和共享,會引起如下系列問題:

3.1 進程模型更安全、更健壯、更容易開發

因爲通常公司成熟產品不是從無到有一個項目就開發完畢,必然有不少歷史代碼、多項目組合做的代碼,這時候採用多進程模型,

能夠有效隔離歷史代碼和當下代碼、不一樣項目組的代碼,固然,這須要產品自己是能夠這麼作的。好比,項目組A開發包處理進程,

項目組B開發包安全檢測功能,兩個功能是兩個進程,這種模型無疑更容易開發和維護

另外,因爲天生因此變量都不共享,對開發者要求也比開發多線程要低

3.2 多核下的性能

傳統意義上,通常認爲多線程比多進程性能要高,這實際上是有前提的。好比不一樣線程之間須要頻繁交互大量數據,因爲IPC自己的開銷,

若是數據交互很是頻繁且量大,多線程會比多進程性能要高。

對於基於DPDK的多核數據包處理程序而言,因爲3個緣由,多進程模型更可預見性能高於多線程:

a DPDK提供了基於hugepage的共享內存機制,使得多進程物理地址相同,其虛擬地址也相同,這事實上就跟多線程之間共享地址空間是

同樣的了。即採用DPDK的基礎庫,多進程之間不須要共享部分使用普通內存(libc malloc,靜態區,棧區),相互隔離很安全。須要共享

部分採用dpdk hugepage 內存,經過特殊映射,也能共享虛擬地址。在這片共享內存上交互數據和指針(虛擬地址是同樣的),性能

遠高於利用內核的IPC機制。

b 多核緩存僞共享問題

這個問題在以前帖子裏http://www.cnblogs.com/jiayy/p/3246133.html說過,多核架構通常有3層緩存,緩存命中率是系統總體性能最關鍵是因素之一。緩存命中率有一個致命殺手就是

僞共享現象,多線程因爲天生全部內存所有是共享的,因此更容易發生僞共享現象,其任何變量,只要一個CPU核改了,其他CPU核都產生

一次緩存失效並從新加載。。,而多進程模型,共享部分是有限的且開發者能夠精確設計和控制的,其僞共享現象能夠獲得有效控制。

在項目實際開發中,常常的狀況就是多線程性能低於多進程,須要將很大變量改成線程局部變量,才能讓性能有所提高。

c 同步互斥

其實,不管是多線程仍是多進程,都須要面臨同步和互斥,這個不是進程/線程模型決定的,而是業務模型決定的。dpdk 提供了應用層

空間實現的基礎互斥同步接口,包括原子操做、自旋鎖、讀寫鎖等,主要是配合共享內存的訪問,由於從數據包處理系統來講,基本上

沒有阻塞的概念,因此這種原子操做和忙等待的鎖能夠知足大部分需求,對於須要阻塞的系統,好比應用層協議棧,則仍是須要使用內核的

機制,好比信號量等

4 最終採用的模型

最終咱們採用的模型是:主體框架是多進程,主進程內部有若干線程用於處理諸如命令接收、文件監控、配置同步、統計數據寫出、

debug數據寫出等功能,包處理的主體流程是多進程的,不一樣進程之間基礎表項、數據包等數據採用dpdk共享內存,在系統啓動時

靜態映射好,這些關鍵的基礎表項和數據包結構針對緩存作細緻優化,好比對齊內存以免發生僞共享。因爲咱們的業務同步和互斥方面

的要求很少,因此只使用了有限的忙等待的鎖和原子操做函數。這種模型實際上也是intel 推薦的模型。固然,選擇多進程模型後,

又有不少須要考慮的東西了,好比是流水線的worker1-worker2-worker3的多進程,仍是 master-worker-worker-worker的對稱多進程,這裏頭根據業務邏輯、同步互斥、性能、擴展性、可維護性有不少深刻的考慮,這裏就不詳細說了。

http://www.soft-bin.com/html/2010/07/09/%E5%A4%9A%E8%BF%9B%E7%A8%8Bvs%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%8C%E4%B8%80%E4%B8%AA%E9%95%BF%E6%9C%9F%E7%9A%84%E4%BA%89%E8%AE%BA.html

http://blog.sina.com.cn/s/blog_d9889c5b0101e7x6.html

相關文章
相關標籤/搜索