Linux netlink socket實現內核與用戶空間通信


內核空間與用戶空間通信機制:

1. ioctl

2. netlink

3. 系統調用

4. 內存映射

5. proc方式

 

netlink相對於其他的通信機制具有以下優點: 
1. 使用netlink通過自定義一種新的協議並加入協議族即可通過socket API使用netlink協議完成數據交換,而ioctl和proc文件系統均需要通過程序加入相應的設備或文件。 
2. netlink使用socket緩沖隊列,是一種異步通信機制,而ioctl是同步通信,如果傳輸數據量較大會影響系統性能。 
3. netlink支持多播,屬於一個netlink組的模塊和進程都能獲得該多播消息。 
4. netlink允許內核發起會話,而ioctl和系統調用只能由用戶空間發起。


 

 Netlink socket是用戶空間程序和內核模塊之間一種很靈活的通訊接口。它為應用程序和內核程序都提供了一個方便使用的API。它還提供了高級的通訊特性,比如全雙工,緩沖I/O,多點傳送和異步通訊,這些都是其他內核/用戶態的IPC所不具有的功能。


 

開發和維護內核是一件很繁雜的工作,因此,只有那些最重要或者與系統性能息息相關的代碼才將其安排在內核中。其它程序,比如GUI,管理以及控制部分的代碼,一般都會作為用戶態程序。在linux系統中,把系統的某個特性分割成在內核中和在用戶空間中分別實現一部分的做法是很常見的(比如linux系統的防火牆就分成了內核態的Netfilter和用戶態的iptables)。然而,內核程序與用戶態的程序又是怎樣行通訊的呢?
答案就是通過各種各樣的用戶態和內核態的IPC(interprocess   communication  )機制來實現。比如系統調用,ioctl接口,proc文件系統以及netlink socket,本文就是要討論netlink socekt並向讀者展示這種用網絡
通訊接口方式實現的IPC機制的優點。

 

為什么以上的功能在實現用戶程序和內核程序通訊時,都使用netlink方法而不是系統調用,ioctls
或者proc文件系統呢?原因在於:為新的特性添加一個新的系統調用,ioctls或者一個proc文件的做法並不是很容易的一件事情,因為我們要冒着污染內核代碼並且可能破壞系統穩定性的風險去完成這件事情。
然而,netlink socket卻是如此的簡單,你只需要在文件netlink.h中添加一個常量來標識你的協議類型,然后,內核模塊和用戶程序就可以立刻使用socket風格的API進行通訊了!
        Netlink提供了一種異步通訊方式,與其他socket API一樣,它提供了一個socket隊列來緩沖或者平滑
瞬時的消息高峰。發送netlink消息的系統調用在把消息加入到接收者的消息對列后,會觸發接收者的接收處理函數。接收者在接收處理函數上下文中,可以決定立即處理消息還是把消息放在隊列中,在以后其它上下文去處理它(因為我們希望接收處理函數執行的盡可能快)。系統調用與netlink不同,它需要一個同步的處理,因此,當我們使用一個系統調用來從用戶態傳遞消息到內核時,如果處理這個消息的時間很長的話,內核調度的粒度就會受到影響。
        內核中實現系統調用的代碼都是在編譯時靜態鏈接到內核的,因此,在動態加載模塊中去包含一個系統調用的做法是不合適的,那是大多數設備驅動的做法。使用netlink socket時,動態加載模塊中的netlink程序不會和linux內核中的netlink部分產生任何編譯時依賴關系。
Netlink優於系統調用,ioctls和proc文件系統的另外一個特點就是它支持多點傳送。一個進程可以把消息傳輸給一個netlink組地址,然后任意多個進程都可以監聽那個組地址(並且接收消息)。這種機制為內核到用戶態的事件分發提供了一種近乎完美的解決方案。
系統調用和ioctl都屬於單工方式的IPC,也就是說,這種IPC會話的發起者只能是用戶態程序。但是,如果內核有一個緊急的消息想要通知給用戶態程序時,該怎么辦呢?如果直接使用這些IPC的話,是沒辦法做到這點的。通常情況下,應用程序會周期性的輪詢內核以獲取狀態的改變,然而,高頻度的輪詢勢必會增加系統的負載。Netlink 通過允許內核初始化會話的方式完美的解決了此問題,我們稱之為netlink socket的雙工特性。
        最后,netlink socket提供了一組開發者熟悉的BSD風格的API函數,因此,相對於使用神秘的系統調用API或者ioctl而言,netlink開發培訓的費用會更低些。
        與BSD的Routing socket的關系
在BSD TCP/IP的協議棧實現中,有一種特殊的socket叫做Routing socket.它的地址族為AF_ROUTE, 協議族為PF_ROUTE, socket類型為SOCK_RAW. 這種Routing socket是用戶態進程用來向內核中的路由表增加或者刪除路由信息用的。在Linux系統中,netlink socket通過協議類型NETLINK_ROUTE實現了與Routing socket相同的功能,可以說,netlink socket提供了BSD Routing socket功能的超集。


 

我最近有一個項目需求,需要在linux網卡驅動中加入一個自己的驅動,實現在內核態完成一些報文處理(這個過程可以實現一種零COPY的網絡報文截獲),對於復雜報文COPY下必要的數據交給用戶態來完成(因為過於復雜的報文消耗CPU太大,會導致中斷占用時間太長)。因此需要一種內核和用戶態配合的通信機制,嘗試了很多方式都不太理想,最后采用netlink+內存映射的模式很好的解決了這個問題。Netlink是一種采用socket通信的機制,用於linux內核和上層用戶空間進行通信的一種機制,通過實踐我認為netlink最大的優點是可以實現“雙向通信”,是內核向用戶態發起通知的一種最好選擇。

 

內核和用戶空間進行通信,大概有如下幾種方式可以考慮:
采用內存映射的方式,將內核地址映射到用戶態。這種方式最直接,可以適用大量的數據傳輸機制。這種方式的缺點是很難進行“業務控制”,沒有一種可靠的機制保障內核和用戶態的調動同步,比如信號量等都不能跨內核、用戶層使用。因此內存映射機制一般需要配合一種“消息機制”來控制數據的讀取,比如采用“消息”類型的短數據通道來完成一個可靠的數據讀取功能。
ioctl機制,ioctl機制可以在驅動中擴展特定的ioctl消息,用於將一些狀態從內核反應到用戶態。Ioctl有很好的數據同步保護機制,不要擔心內核和用戶層的數據訪問沖突,但是ioctl不適合傳輸大量的數據,通過和內存映射結合可以很好的完成大量數據交換過程。但是,ioctl的發起方一定是在用戶態,因此如果需要內核態主動發起一個通知消息給用戶層,則非常的麻煩。可能需要用戶態程序采用輪詢機制不停的ioctl。
其他一些方式比如系統調用必須通過用戶態發起,proc方式不太可靠和實時,用於調試信息的輸出還是非常合適的。
通過前面的項目背景,我需要一種可以在內核態主動發起消息的通知方式,而用戶態的程序最好可以采用一種“阻塞調用”的方式等待消息。這樣的模型可以最大限度的節省CPU的調度,同時可以滿足及時處理的要求,最終選擇了netlink完成通信的過程。
Netlink的通信模型和socket通信非常相似,主要要點如下:

    • netlink采用自己獨立的地址編碼,struct sockaddr_nl;
    • 每個通過netlink發出的消息都必須附帶一個netlink自己的消息頭,struct nlmsghdr;
    • 內核態的netlink的操作API和用戶態完全不一樣,后面再介紹;
    • 用戶態的netlink操作完成采用socket函數,非常方便和簡單,有TCP/UDP socket編程基礎的非常容易上手。

 

You're not allowed to free the skb after you've sent it.  nlmsg_unicast() will take care of that.
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM