免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
用戶空間和內(nèi)核空間通訊之【Netlink 下】

 在上一篇博文中我們所遇到的情況都是用戶空間作為消息進程的發(fā)起者,Netlink還支持內(nèi)核作為消息的發(fā)送方的情況。這一般用于內(nèi)核主動向用

戶空間報告一些內(nèi)核狀態(tài),例如我們在用戶空間看到的USB的熱插拔事件的通告就是這樣的應(yīng)用。

 先說一下我們的目標(biāo),內(nèi)核線程每個一秒鐘往一個多播組里發(fā)送一條消息,然后用戶空間所以加入了該組的進程都會收到這樣的消息,并將消息內(nèi)容打印出來。

        Netlink地址結(jié)構(gòu)體中的nl_groups32位,也就是說每種Netlink協(xié)議最多支持32個多播組。如何理解這里所說的每種Netlink協(xié)議?在</usr/include/linux/netlink.h>里預(yù)定義的如下協(xié)議都是Netlink協(xié)議簇的具體協(xié)議,還有我們添加的NETLINK_TEST也是一種Netlink協(xié)議。


  1. #define NETLINK_ROUTE        0    /* Routing/device hook                */
  2. #define NETLINK_UNUSED        1    /* Unused number                */
  3. #define NETLINK_USERSOCK    2    /* Reserved for user mode socket protocols     */
  4. #define NETLINK_FIREWALL    3    /* Firewalling hook                */
  5. #define NETLINK_INET_DIAG    4    /* INET socket monitoring            */
  6. #define NETLINK_NFLOG        5    /* netfilter/iptables ULOG */
  7. #define NETLINK_XFRM        6    /* ipsec */
  8. #define NETLINK_SELINUX        7    /* SELinux event notifications */
  9. #define NETLINK_ISCSI        8    /* Open-iSCSI */
  10. #define NETLINK_AUDIT        9    /* auditing */
  11. #define NETLINK_FIB_LOOKUP    10    
  12. #define NETLINK_CONNECTOR    11
  13. #define NETLINK_NETFILTER    12    /* netfilter subsystem */
  14. #define NETLINK_IP6_FW        13
  15. #define NETLINK_DNRTMSG        14    /* DECnet routing messages */
  16. #define NETLINK_KOBJECT_UEVENT    15    /* Kernel messages to userspace */
  17. #define NETLINK_GENERIC        16
  18. /* leave room for NETLINK_DM (DM Events) */
  19. #define NETLINK_SCSITRANSPORT    18    /* SCSI Transports */
  20. #define NETLINK_ECRYPTFS    19
  21. #define NETLINK_TEST 20 /* 用戶添加的自定義協(xié)議 */

       在我們自己添加的NETLINK_TEST協(xié)議里,同樣地,最多允許我們設(shè)置32個多播組,每個多播組用1個比特表示,所以不同的多播組不可能出現(xiàn)重復(fù)。你可以根據(jù)自己的實際需求,決定哪個多播組是用來做什么的。用戶空間的進程如果對某個多播組感興趣,那么它就加入到該組中,當(dāng)內(nèi)核空間的進程往該組發(fā)送多播消息時,所有已經(jīng)加入到該多播組的用戶進程都會收到該消息。

       再回到我們Netlink地址結(jié)構(gòu)體里的nl_groups成員,它是多播組的地址掩碼,注意是掩碼不是多播組的組號。如何根據(jù)多播組號取得多播組號的掩碼呢?在af_netlink.c中有個函數(shù):


  1. static u32 netlink_group_mask(u32 group)
  2. {
  3.     return group ? 1 << (group - 1) : 0;
  4. }

       也就是說,在用戶空間的代碼里,如果我們要加入到多播組1,需要設(shè)置nl_groups設(shè)置為1;多播組2的掩碼為2;多播組3的掩碼為4,依次類推。為0表示我們不希望加入任何多播組。理解這一點很重要。所以我們可以在用戶空間也定義一個類似于netlink_group_mask()的功能函數(shù),完成從多播組號到多播組掩碼的轉(zhuǎn)換。最終用戶空間的代碼如下:

點擊(此處)折疊或打開

  1. #include <sys/stat.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <string.h>
  8. #include <asm/types.h>
  9. #include <linux/netlink.h>
  10. #include <linux/socket.h>
  11. #include <errno.h>

  12. #define MAX_PAYLOAD 1024 // Netlink消息的最大載荷的長度

  13. unsigned int netlink_group_mask(unsigned int group)
  14. {
  15.     return group ? 1 << (group - 1) : 0;
  16. }

  17. int main(int argc, char* argv[])
  18. {
  19.     struct sockaddr_nl src_addr;
  20.     struct nlmsghdr *nlh = NULL;
  21.     struct iovec iov;
  22.     struct msghdr msg;
  23.     int sock_fd, retval;

  24.     // 創(chuàng)建Socket
  25.     sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
  26.     if(sock_fd == -1){
  27.         printf("error getting socket: %s", strerror(errno));
  28.         return -1;
  29.     }

  30.     memset(&src_addr, 0, sizeof(src_addr));
  31.     src_addr.nl_family = PF_NETLINK;
  32.     src_addr.nl_pid = 0; // 表示我們要從內(nèi)核接收多播消息。注意:該字段為0有雙重意義,另一個意義是表示我們發(fā)送的數(shù)據(jù)的目的地址是內(nèi)核。
  33.     src_addr.nl_groups = netlink_group_mask(atoi(argv[1])); // 多播組的掩碼,組號來自我們執(zhí)行程序時輸入的第一個參數(shù)

  34.     // 因為我們要加入到一個多播組,所以必須調(diào)用bind()。
  35.     retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
  36.     if(retval < 0){
  37.         printf("bind failed: %s", strerror(errno));
  38.         close(sock_fd);
  39.         return -1;
  40.     }

  41.     // 為接收Netlink消息申請存儲空間
  42.     nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
  43.     if(!nlh){
  44.         printf("malloc nlmsghdr error!\n");
  45.         close(sock_fd);
  46.         return -1;
  47.     }

  48.     memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
  49.     iov.iov_base = (void *)nlh;
  50.     iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);

  51.     memset(&msg, 0, sizeof(msg));
  52.     msg.msg_iov = &iov;
  53.     msg.msg_iovlen = 1;

  54.     // 從內(nèi)核接收消息
  55.     printf("waitinf for...\n");
  56.     recvmsg(sock_fd, &msg, 0);
  57.     printf("Received message: %s \n", NLMSG_DATA(nlh));
  58.     
  59.     close(sock_fd);

  60.     return 0;
  61. }

       可以看到,用戶空間的程序基本沒什么變化,唯一需要格外注意的就是Netlink地址結(jié)構(gòu)體中的nl_groups的設(shè)置。由于對它的解釋很少,加之沒有有效的文檔,所以我也是一邊看源碼,一邊在網(wǎng)上搜集資料。有分析不當(dāng)之處,還請大家?guī)臀抑赋觥?/font>

       內(nèi)核空間我們添加了內(nèi)核線程和內(nèi)核線程同步方法completion的使用。內(nèi)核空間修改后的代碼如下:

點擊(此處)折疊或打開

  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/skbuff.h>
  4. #include <linux/init.h>
  5. #include <linux/ip.h>
  6. #include <linux/types.h>
  7. #include <linux/sched.h>
  8. #include <net/sock.h>
  9. #include <net/netlink.h>

  10. MODULE_LICENSE("GPL");
  11. MODULE_AUTHOR("Koorey King");

  12. struct sock *nl_sk = NULL;
  13. static struct task_struct *mythread = NULL; //內(nèi)核線程對象

  14. //向用戶空間發(fā)送消息的接口
  15. void sendnlmsg(char *message/*,int dstPID*/)
  16. {
  17.     struct sk_buff *skb;
  18.     struct nlmsghdr *nlh;
  19.     int len = NLMSG_SPACE(MAX_MSGSIZE);
  20.     int slen = 0;

  21.     if(!message || !nl_sk){
  22.         return;
  23.     }

  24.     // 為新的 sk_buffer申請空間
  25.     skb = alloc_skb(len, GFP_KERNEL);
  26.     if(!skb){
  27.         printk(KERN_ERR "my_net_link: alloc_skb Error./n");
  28.         return;
  29.     }

  30.     slen = strlen(message)+1;

  31.     //用nlmsg_put()來設(shè)置netlink消息頭部
  32.     nlh = nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0);

  33.     // 設(shè)置Netlink的控制塊里的相關(guān)信息
  34.     NETLINK_CB(skb).pid = 0; // 消息發(fā)送者的id標(biāo)識,如果是內(nèi)核發(fā)的則置0
  35.     NETLINK_CB(skb).dst_group = 5; //多播組號為5,但置成0好像也可以。

  36.     message[slen] = '\0';
  37.     memcpy(NLMSG_DATA(nlh), message, slen+1);

  38.     //通過netlink_unicast()將消息發(fā)送用戶空間由dstPID所指定了進程號的進程
  39.     //netlink_unicast(nl_sk,skb,dstPID,0);
  40.     netlink_broadcast(nl_sk, skb, 0,5, GFP_KERNEL); //發(fā)送多播消息到多播組5,這里我故意沒有用1之類的“常見”值,目的就是為了證明我們上面提到的多播組號和多播組號掩碼之間的對應(yīng)關(guān)系
  41.     printk("send OK!\n");
  42.     return;
  43. }

  44. //每隔1秒鐘發(fā)送一條“I am from kernel!”消息,共發(fā)10個報文
  45. static int sending_thread(void *data)
  46. {
  47.      int i = 10;
  48.      struct completion cmpl;
  49.      while(i--){
  50.             init_completion(&cmpl);
  51.             wait_for_completion_timeout(&cmpl, 1 * HZ);
  52.             sendnlmsg("I am from kernel!");
  53.      }
  54.      printk("sending thread exited!");
  55.      return 0;
  56. }

  57. static int __init myinit_module()
  58. {
  59.     printk("my netlink in\n");
  60.     nl_sk = netlink_kernel_create(NETLINK_TEST,0,NULL,THIS_MODULE);

  61.     if(!nl_sk){
  62.         printk(KERN_ERR "my_net_link: create netlink socket error.\n");
  63.         return 1;
  64.     }

  65.     printk("my netlink: create netlink socket ok.\n");
  66.     mythread = kthread_run(sending_thread,NULL,"thread_sender");
  67.     return 0;
  68. }

  69. static void __exit mycleanup_module()
  70. {
  71.     if(nl_sk != NULL){
  72.         sock_release(nl_sk->sk_socket);
  73. }
  74. printk("my netlink out!\n");
  75. }

  76. module_init(myinit_module);
  77. module_exit(mycleanup_module);

       關(guān)于內(nèi)核中netlink_kernel_create(int unit, unsigned int groups,…)函數(shù)里的第二個參數(shù)指的是我們內(nèi)核進程最多能處理的多播組的個數(shù),如果該值小于32,則默認(rèn)按32處理,所以在調(diào)用netlink_kernel_create()函數(shù)時可以不用糾結(jié)第二個參數(shù),一般將其置為0就可以了。

 

       在skbuff{}結(jié)構(gòu)體中,有個成員叫做"控制塊",源碼對它的解釋如下:

點擊(此處)折疊或打開

  1. struct sk_buff {
  2.     /* These two members must be first. */
  3.     struct sk_buff        *next;
  4.     struct sk_buff        *prev;
  5.     … …
  6.     /*
  7.      * This is the control buffer. It is free to use for every
  8.      * layer. Please put your private variables there. If you
  9.      * want to keep them across layers you have to do a skb_clone()
  10.      * first. This is owned by whoever has the skb queued ATM.
  11.      */
  12.     char            cb[48];

  13.     … …
  14. }
       當(dāng)內(nèi)核態(tài)的Netlink發(fā)送數(shù)據(jù)到用戶空間時一般需要填充skbuff的控制塊,填充的方式是通過強制類型轉(zhuǎn)換,將其轉(zhuǎn)換成struct netlink_skb_parms{}之后進行填充賦值的:

點擊(此處)折疊或打開

  1. struct netlink_skb_parms
  2. {
  3.     struct ucred        creds;        /* Skb credentials    */
  4.     __u32            pid;
  5.     __u32            dst_group;
  6.     kernel_cap_t        eff_cap;
  7.     __u32            loginuid;    /* Login (audit) uid */
  8.     __u32            sid;        /* SELinux security id */
  9. };

       填充時的模板代碼如下:

點擊(此處)折疊或打開

  1. NETLINK_CB(skb).pid=xx;
  2. NETLINK_CB(skb).dst_group=xx;

       這里要注意的是在Netlink協(xié)議簇里提到的skbuffcb控制塊里保存的是屬于Netlink的私有信息。怎么講,就是Netlink會用該控制塊里的信息來完成它所提供的一些功能,只是完成Netlink功能所必需的一些私有數(shù)據(jù)。打個比方,以開車為例,開車的時候我們要做的就是打火、控制方向盤、適當(dāng)?shù)乜刂朴烷T和剎車,車就開動了,這就是汽車提供給我們的“功能”。汽車的發(fā)動機,輪胎,傳動軸,以及所用到的螺絲螺栓等都屬于它的“私有”數(shù)據(jù)cb。汽車要運行起來這些東西是不可或缺的,但它們之間的協(xié)作和交互對用戶來說又是透明的。就好比我們Netlink的私有控制結(jié)構(gòu)struct netlink_skb_parms{}一樣。

       目前我們的例子中,將NETLINK_CB(skb).dst_group設(shè)置為相應(yīng)的多播組號和0效果都是一樣,用戶空間都可以收到該多播消息,原因還不是很清楚,還請Netlink的大蝦們幫我點撥點撥。

       編譯后重新運行,最后的測試結(jié)果如下:

       注意,這里一定要先執(zhí)行insmod加載內(nèi)核模塊,然后再運行用戶空間的程序。如果沒有加載mynlkern.ko而直接執(zhí)行./test 5bind()系統(tǒng)調(diào)用時會報如下的錯誤:

       bind failed: No such file or directory

       因為網(wǎng)上有寫文章在講老版本Netlink的多播時用法時先執(zhí)行了用戶空間的程序,然后才加載內(nèi)核模塊,現(xiàn)在(2.6.21)已經(jīng)行不通了,這一點請大家注意。

       小結(jié):通過這三篇博文我們對Netlink有了初步的認(rèn)識,并且也可以開發(fā)基于Netlink的基本應(yīng)用程序。但這只是冰山一角,要想寫出高質(zhì)量、高效率的軟件模塊還有些差距,特別是對Netlink本質(zhì)的理解還需要提高一個層次,當(dāng)然這其中牽扯到內(nèi)核編程的很多基本功,如臨界資源的互斥、線程安全性保護、用Netlink傳遞大數(shù)據(jù)時的處理等等都是開發(fā)人員需要考慮的問題。

       完。


本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服