linux與freebsd中tcp協議棧實現之比較

作爲兩個最有名的開源操作系統,Linux和FreeBSD是網管們的首選。Linux以開放性和衆多的驅動支持著稱,而FreeBSD有着優良的UNIX傳統,是公認的最穩定的操作系統。那麼,在這兩個操作系統間,該如何選擇呢?幸好,我們有源碼,可以從協議棧的實現中尋找答案。

 

TCP/IP協議棧是網絡中廣泛使用的事實網絡通信標準。最初的TCP實現源自4.4BSD lite,在Linux興起後,也不可避免得支持它。但Linux的實現自成體系,僅與傳統實現保持接口上的兼容,下面我們將針對源碼級的實現,來分析一下兩者的異同。但是,對於Linux和FreeBSD這樣優秀的系統來說,已經無所謂何優何劣,有的僅僅是實現策略與側重點上的不同而已。

從進程的角度上講,可以調用send,sendto,sendmsg來發送一段數據,來可以使用文件系統中的write和writev來發送數據。同理,接收數據可以使用相應的recv,recvmsg,recvfrom,也可以使用文件系統提供的read,readv來接收一段數據。對於接收來說,這是異步進行的,也就是說,這是中斷驅動的,在以後的分析中,我們要注意這點。爲簡單起見,同時不失一般性,我們將分析TCP協議的輸入輸出全過程,並以已對LINUX及FreeBSD的實現作一對比。

首先我們來看FreeBSD上的協議實現,這也是最正統的實現。下面是完整的輸入輸出路徑。

首先來看左邊的輸出,不管應用程序調用哪個輸出函數,最終都要調用sosend來完成輸出。Sosend將從用戶空間把數據複製進內核管理的m_buf數據結構,m_buf是FreeBSD的TCP實現使用的數據緩衝結構。在sosend完成數據複製後,將調用TCP的輸出函數,tcp_output要做的事情是分配一個新的m_buf來保存tcp頭,並計算相應的數據校驗碼,在下一步的ip_output中,同樣也要進行數據校驗工作,並進行數據路由選擇。最終ether_output將通過if_start來調用具體的硬件驅動程序來完成數據發送。在某個網卡的驅動中,ex_start將負責將數據從內核的m_buf緩衝複製進硬件自己的緩衝區,以完成數據發送工作。在這整個過程中,數據被複制兩次,並且也被遍歷兩次(計算校驗碼),這也是主要的影響效率的地方。
再來討論右邊的輸入,當網卡收到數據時,中斷處理程序ex_intr將被調用。驅動通過ex_rx_intr將數據從硬件緩衝複製進m_buf數據結構中,並調用ether_input來進一步處理。ether_input通過ether_demux進行分用。如果是一個Ip包,將通過軟中斷調用ip_fastforward進行數據校驗,並判斷是否要轉發,如果失敗,將進行ip_input進行完整的處理。在in_input中,同樣要判斷是否要進行轉發,如果不用,調用tcp_input進行進一步處理。在tcp_input中,進行數據校驗和驗證後,有一個叫做首部預測算法的優化,可以加快數據處理速度。進行完所有的操作後,如果是用戶數據,將喚醒用戶進程進行處理。同理,用戶可以使用多個函數進行數據接收,而soreceive將負責將數據從m_buf轉移至用戶進程緩衝。
可以看出,在FreeBSD中,發送和接收數據,所進行的操作差不多,都要進行兩次數據複製和兩次數據遍歷,這也是最大的影響效率的地方。兩次數據複製似乎無可避免,下面我們來看看Linux是怎麼做的。


可以看到,在LINUX上的實現稍顯複雜。讓我們首先從發送開始分析。在LINUX上,socket被實現爲一個文件系統,這樣可以通過vfs的write來調用,也可以直接使用send來調用,它們最終都是調用sock_sendmsg。Sock_sendmsg通過它的內核版本__sock_sendmsg直接調用tcp_sendmsg來發送數據。在tcp_sendmsg中,同時完成數據複雜和數據校驗,這樣節省了一次遍歷操作,這也是和FreeBSD不同的地方。Linux使用skb結構來管理數據緩衝,這和FreeBSD的m_buf大同小異。當複製完數據後,使用tcp_push來進行下一步發送。Tcp_push通過__tcp_push_pending_frames來調用tcp_write_xmit將數據填入tcp的發送緩衝區。這裏的填充僅是指針引用而已。下一步,tcp_transmit_skb將數據放入IP的發送隊列。Ip_queue_xmit函數完成IP包頭的設置以及數據效驗,並調用ip_output進入下一步發送。如果不用分片,將使用ip_finish_output繼續發送。在這裏,填充數據的以太網包頭後調用dev_queue_xmit函數來進一步處理。Dev_queue_xmit函數將數據轉移至網絡核心層的待發送隊列,調用具體的驅動程序cp_start_xmit來完成數據的最終發送。最後的cp_start_xmit做的事情和freebsd的相應函數差不多,檢查數據,並複製進硬件緩衝。
當接收到一個數據包的時候,網卡會產生中斷,這樣網卡驅動的cp_interrupt會被調用。cp_interrupt做的事情很少,只進行必要的檢查後就返回了,更多的事情通過cp_rx_poll來完成,cp_rx_poll在軟中斷中被調用,這樣做是爲了提高驅動的處理效率。Cp_rx_poll做的事情主要就是把申請並將數據複製進一個skb緩衝中。netif_rx函數將數據從這個隊列中轉移至網絡核心層隊列中,netif_receive_skb從這裏接收數據,調用ip_rcv來處理。Ip_rc和ip_rcv_finish一起檢查數據,得到包的路由,並調用相應的input函數來完成路由,在這裏就是ip_local_deliver,ip_local_deliver完成IP包的重組後,使用ip_local_deliver_finish來進入tcp的處理流程,tcp_v4_rcv完成數據校驗以及一些簡單的檢查,主要的工作在tcp_v4_do_rcv中完成。tcp_v4_do_rcv先判斷是否正常的用戶數據,如果是則用tcp_rcv_established處理,否則用tcp_rcv_state_process來更新連接的狀態機。tcp_rcv_established中同樣有首部預測。如果一切順便,將喚醒等待在tcp_recvmsg中的用戶進程。後者將數據從skb緩衝中複製進用戶進程緩衝。並進行逐級返回。

通過以上分析不難看出,Linux的代碼比較混亂,可讀性沒有FreeBSD的好,比如說,Linux省略了以太網層,而且在接收數據中有多次異步操作,也許這將會影響內核的穩定性,FreeBSD的代碼就比較清晰,程序處理一目瞭然,可讀性也高,最穩定的操作系統名不虛傳。這也可以從兩個操作系統的起源得到解釋。Linux起源於互聯網時代,由衆多愛好者一起完成,並沒有一個完整的規劃,代碼也多次經過變動,而作者水平也參次不齊,造成現在的樣子。而FreeBSD系出名門,一直由一個獨立的小組進行維護,多年來更新不大,只有少許優化,所以代碼的可讀性非常高。但從另一方面講,不斷更新的Linux在代碼方面比較激進,比如Linux使用skb緩衝效率要較FreeBSD使用的m_buf爲高,這裏限於篇幅,就不再具體分析了。而且linux發送數據時,在複製數據的同時完成的tcp的效驗,這樣就節省了一次數據的遍歷操作。也提高了效率。

通過以上比較我們不難得出結論,如果效率爲先,則Linux當爲首推,如果穩定至上,FreeBSD應該爲不二之選。不過,這兩個操作系統都是非常優秀並久經考驗,之間的差別也僅存於字面分析上。無論選擇哪個,都不會讓您後悔,不是嗎?

參考文獻:

W.Richard Stevens TCP/IP Illustrated, Volume 1: The Protocols     機械工業出版社,2004        

Gary R.Wright&W.Richard Stevens  TCP/IP Illustrated,Volume 2:The Implementation           機械工業出版社,2002       

Alessandro Rubini,Jonathan Corbet     Linux Device Drivers       中國電力出版社, 2004

Uresh Vahalia   UNIX Internals:The New Frontiers         人民郵電出版社, 2003            

 倪繼利 Linux內核分析及編程           電子工業出版社, 2005 

轉自:http://blog.sina.com.cn/s/blog_544050ed0100012p.html

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