內核通信之Netlink源碼分析-基礎架構


2017-07-04


netlink是一種基於網絡的通信機制,一般用於內核內部或者內核與用戶層之間的通信。其有一個明顯的特點就是異步性,通信的雙方不要求同時在線,也就不用阻塞等待。NetLink按照數據包的格式發送/接收消息,提供雙向通信,和其他內核與用戶層通信機制相比,NetLink有其特有的優勢:

  • 任何一方不需要輪訓,如果通過文件傳遞狀態信息,則需要不斷的檢查是否有新消息到達。
  • 系統調用和ioctl均可以從用戶層向內核傳遞消息,但比簡單的Netlink鏈接難於實現,另外,NetLink是系統提供的機制,不會和任何模塊其沖突,兼容性好。
  • 使用NetLink可以讓內核主動的向用戶層發消息,此時內核扮演的就是一個邏輯用戶;而ioctl和系統調用、/proc均只能由用戶層發起請求,讓內核去被動響應。
  • Netlink不僅支持單播,也支持多播即向多個進程發送消息。

 從邏輯上來講,NetLink應該處於傳輸層,因此其同樣需要特定的協議。對於NetLink就是Netlink協議,內核中有netlink_family_ops,在用戶層創建netlink類型的socket的時候最終會調用到netlink_family_ops注冊的create函數

static const struct net_proto_family netlink_family_ops = {
    .family = PF_NETLINK,
    .create = netlink_create,
    .owner    = THIS_MODULE,    /* for consistency 8) */
};

 每種協議都需要一組協議處理函數,針對Netlink有netlink_ops

static const struct proto_ops netlink_ops = {
    .family =    PF_NETLINK,
    .owner =    THIS_MODULE,
    .release =    netlink_release,
    .bind =        netlink_bind,
    .connect =    netlink_connect,
    .socketpair =    sock_no_socketpair,
    .accept =    sock_no_accept,
    .getname =    netlink_getname,
    .poll =        netlink_poll,
    .ioctl =    sock_no_ioctl,
    .listen =    sock_no_listen,
    .shutdown =    sock_no_shutdown,
    .setsockopt =    netlink_setsockopt,
    .getsockopt =    netlink_getsockopt,
    .sendmsg =    netlink_sendmsg,
    .recvmsg =    netlink_recvmsg,
    .mmap =        netlink_mmap,
    .sendpage =    sock_no_sendpage,
};

 我們重點關注netlink_sendmsg和netlink_recvmsg,這是netlink發送和接收數據的處理函數。

地址表示

NetLink的地址表示由sockaddr_nl負責

struct sockaddr_nl {
    __kernel_sa_family_t    nl_family;    /* AF_NETLINK    */
    unsigned short    nl_pad;        /* zero        */
    __u32        nl_pid;        /* port ID    */
    __u32        nl_groups;    /* multicast groups mask */
};

nl_family制定了協議族,這里肯定是AF_NETLINK,nl_pid用以該套接字,對於內核套接字來講,nl_pid為0,而對於用戶層程序而言,該值可以為任何值,協商好並保證唯一性即可。一般取為進程id或線程ID。nl_groups用以多播,當不需要多播時,該字段為0。

NetLink消息

NetLink消息是作為套接字緩沖區sk_buff的數據部分傳遞的,其消息本身又分為頭部和數據。頭部為

struct nlmsghdr {
    __u32        nlmsg_len;    /* Length of message including header */
    __u16        nlmsg_type;    /* Message content */
    __u16        nlmsg_flags;    /* Additional flags */
    __u32        nlmsg_seq;    /* Sequence number */
    __u32        nlmsg_pid;    /* Sending process port ID */
};

nlmsg_len為消息的長度,包含該頭部在內。nlmsg_pid為發送進程的端口ID,這個用戶可以自定義。其余的字段暫時用不到。在用戶層還用到了一個結構

struct msghdr {
    void    *    msg_name;    /* Socket name            */
    int        msg_namelen;    /* Length of name        */
    struct iovec *    msg_iov;    /* Data blocks            */
    __kernel_size_t    msg_iovlen;    /* Number of blocks        */
    void     *    msg_control;    /* Per protocol magic (eg BSD file descriptor passing) */
    __kernel_size_t    msg_controllen;    /* Length of cmsg list */
    unsigned int    msg_flags;
};

msg_name是目標socket的地址結構sockaddr_nl的地址,msg_namelen是長度。msg_iov指向一個iov向量,msg_iovlen是向量的個數。剩下的暫不考察。當用戶層向內核發送消息時,該結構作為參數傳遞。nlmsghdr 只能描述單個消息,而msghdr描述一組消息。通過iov向量聚合所有的消息,每個消息都有其對應的nlmsghdr.邏輯結構如下:

之前提到,socket也是一種特殊的文件,通過VFS的借口同樣可以對其實現管理。sokket本身就需要實現文件系統的相應接口,有自己的文件系統操作。

內核中的表示

netlink socket在內核中涉及到的結構有下:socket,sock,netlink_sock,前者是套接字通用結構,sock是套接字在網絡層的體現,而sock其實是內嵌在netlink_sock結構中,后者保存有鏈接兩端的端口ID,並包含一個函數,用於在接收數據時使用!

 

以馬內利

參考資料:

《linux3.10.1源碼》

 


免責聲明!

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



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