【轉載】Linux內核編程與應用編程對比

【轉載】Linux內核編程與應用編程對比html

 

轉載連接1:http://www.arrowapex.cn/archives/66.htmllinux

在此以前也不清楚linux內核編程跟用戶應用程序編程之間有什麼不一樣,正好這幾天作了一點linux模塊編程,遇到問題請教朋友並查一些資料,感受對內核編程和用戶應用程序編程的幾點不一樣有了一點體會,就寫了下來。編程

1.linux內核編程和用戶應用程序編程最大的不一樣是,前者是在內核態下運行的,然後者主要在用戶態下運行,有時經過一些系統調用切換到內核態下運行,但這時間不會太長。網絡

2.內核編程引進的頭文件都在內核源碼的include文件夾下,好比個人debian linux 2.6環境下是:/usr/src/linux/include下,而用戶應用程序編程引進的頭文件都是從開發環境頭文件的include文件夾下,好比 個人環境下是:/usr/include下。也就是凡是要include的頭文件在內核源碼include底下沒有的都不能用。多線程

3.要查詢一個函數可否在內核編程中用,能夠經過http://lxr-itec.uni-klu.ac.at/linux-2.6.4/ident查 (這是針對linux2.6內核,也有針對2.4內核的),若是能查到Defined as a function,那就能夠用,不然就不行。架構

4.舉個簡單的例子:當socket編程時,用戶在應用程序編程時,基本上都用到socket()函數建立一個socket描述符,include的頭文 件主要是<sys/socket.h>,<sys/types.h>和<netinet/ip.h>,這幾個頭文件 在內核源碼include下都沒有,全部不能用,但內核編程有它本身的一套。內核socket編程時,須要用sock_create()獲得一個 socket結構體,若是想跟用戶應用程序同樣用socket描述符來操做socket,能夠再用sock_map_fd()新建一個對應的描述符,並且 須要時能夠經過sockfd_lookup(),實現經過描述符查找對應的socket結構體。其實socket函數內核實現時就是先有 sock_create(),再有sock_map_fd()。併發

 

轉載連接2:http://blog.chinaunix.net/uid-23629988-id-3993750.htmlsocket

目前,內核編程給我最大的感觸是程序的執行流比較多,併發邏輯比應用編程要複雜的多。這個「執行流」是我杜撰的名詞,但基本上能夠表達個人意思。應用編程中,談到併發,無非是多進程多線程,通常對共享資源使用鎖保護就基本上沒有問題了。一個線程能夠視爲一個執行流,除非被信號打斷,該線程都是按照代碼順序執行。也就是說,咱們在應用層編寫的代碼和業務邏輯,只會被咱們定義的線程或者進程執行。信號處理函數通常狀況下,都會寫的比較簡單,大可能是設置標誌位。而在內核中,有中斷,軟中斷,定時器,還有系統調用等諸多會涉及業務邏輯的執行流。因爲內核自身的特性,對共享資源的保護,也要斟酌使用不一樣的手段。 ide

對於某些共享資源,有時候使用spin_lock進行保護,但隨着功能需求的增長。須要加入與用戶空間的交互,在代碼實現上,有時候會直接調用現成的代碼。結果那些代碼中對共享資源的保護使用的是spin_lock,而數據包轉發的業務邏輯代碼都是運行在軟中斷中,結果形成了死鎖。 函數

我還修正過一些別人寫的bug。其中有一個bug,給我留下的印象也很深。當時產品老是不按期重啓,而咱們這裏又沒法重現。我當時剛剛接觸已有的產品代碼,對於這種重啓的bug,在不能重現的狀況下,我選擇review代碼這看似笨重卻很是有效的方法。還好產品的關鍵功能的代碼量不算多,花了2天的時間把大部分代碼讀懂,同時順手修正了一些有可能形成重啓的問題。客戶升級之後,大部分沒有問題了,但仍是有個別重啓的現象,那麼這意味着還有漏網之魚。當時我基本已經把關鍵流程所有理通了,修正這個問題的流程頗有意思。我靠在椅背上,眼睛望向天花板,內心把數據包從入口到出口的流程走了一遍,並考慮全部的分支和特殊狀況。而後Get it!大概花了不到15分鐘的時間。而後看代碼,驗證本身的猜測。

形成重啓問題的緣由以下:由於某種業務邏輯需求,申請了一個動態結構,並設置了定時器超時,到期釋放。當業務邏輯訪問這個動態結構時,會刷新它的訪問時間,延長其生命週期。可是在某些狀況下,可能須要提早刪除這個結構時,會調用del_timer刪除定時器,而後釋放內存。看到這樣的代碼,我馬上就懷疑當del_timer刪除定時器時,若是該定時器正在處於執行階段,怎麼辦?上網查詢了一下,果不其然,del_timer返回時不能保證沒有正在執行的定時器。那麼當定時器還在執行的時候,這個動態結構就被釋放了,定時器也會隨着動態結構的釋放而釋放。這樣的代碼確定是有問題的。如何解決這個問題呢?第一個念頭,就是保證同步刪除定時器。根據搜索的結果,可使用del_timer_sync。然而我仔細一想,這樣仍然有問題。原本這個動態結構是使用定時器來釋放,可是這裏確實強制釋放,那麼即便使用了del_timer_sync停掉了定時器,那麼這時定時器可能已經完成了超時,並釋放了動態結構。這時再強制釋放等於double free。同時del_timer_sync還有一個問題,這種同步操做,必然帶來性能上的降低。因此最終的選擇方案是增長一個標誌,在強制刪除時,將標誌置位,保證釋放操做只有一個執行者,同時引入引用計數。

最近,爲了優化性能,我也引入了兩個bug,還好都及時修正了。bug形成的緣由,仍是因爲對linux內核自己不太熟悉形成的。其中一個最近發現的bug,竟然花費我一天的時間才找到緣由。當使用某個應用程序時,會形成內核崩潰。起初我一度甚至懷疑這是內核的bug——雖然我以爲不大可能,因而我就開始驗證排除這個可能。由於不開這個應用程序時,內核模塊徹底沒有問題。打開應用程序時,內核就會崩潰。而這個應用程序跟內核模塊,徹底沒有任何的交互。後來分析這個應用程序的代碼,與網絡關係緊密的就是註冊了一個PF_PACKET的socket,用於抓取全部網卡的數據包。因而我去查看了相關代碼,當PF_PACKET的接受包函數,會檢查skb是否被共享,若是是共享的就clone一份,ip_rcv入口處也有相似的代碼。那麼當該應用程序運行時,就意味着ip_rcv會檢測發現這個skb是共享的,因而就會clone一份。這就是該應用程序運行時與不運行時,內核處理數據包流程的最大區別。因而,我修改ip_rcv的代碼,再也不堅持skb是不是共享,而是直接clone。果真,內核在不啓動該應用程序時候,依然崩潰。這樣就證實了,問題仍是出自本身的代碼處,並且是與skb相關的代碼。通過一番查找,最終找到了根本緣由。

我在netfilter的兩個hook點上,註冊了兩個hook函數。前一個鉤子函數,初始化了一些per cpu變量,後一個鉤子函數,簡單檢測了per_cpu->skb與hook的參數skb若是是相等的狀況,就再也不初始化,直接使用per cpu的變量了。形成問題的緣由就在於,在有skb_clone調用時,不一樣hook調用時,skb->data發生了變化。第二個hook位置,skb->data指向的內存與第一個hook處不一致。可是skb_clone自己並不會形成這樣的結果。這說明在netfilter的不一樣hook之間,當skb被clone了,會從新分配skb的數據空間——具體是哪處代碼,我暫時沒有找到。

這個bug讓我吸收了一個教訓。內核編程,因爲你不可能熟悉linux內核全部的代碼,全部在編程中,要想着,除非內核已經明肯定義的行爲,才能放心使用。不是明肯定義的行爲,不能根據平時簡單的測試,就確信沒有問題。如上面的例子,內核歷來沒有說過兩個hook點之間,skb是同樣的,skb的數據空間即skb->data是同樣的。

對於在linux內核實現網關的某些功能時,我發現,雖然linux已經提供了不少現成的東西,能夠保證快速開發。可是內核自己架構是一個通用計算機,不是專門針對網絡處理的。其網絡模塊的架構自己有不少弊端和不便處,尤爲是對比我前公司的產品架構——該架構看上去挺簡單的,但越體會越能感受,簡單就是美!就是效率——一個是產品效率即性能,還一個是開發效率。

Note: 其實作網絡設備的,作到高性能的產品,大部分架構都比較類似,但在細微處的不一樣,造就了不一樣的產品性能。

相關文章
相關標籤/搜索