Netlink实现热拔插监控
生活随笔
收集整理的這篇文章主要介紹了
Netlink实现热拔插监控
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
新的Linux內核使用udev代替了hotplug作為熱拔插管理,
雖然有udevd管理熱拔插,但有時候我們還是需要在應用程序中檢測熱拔插事件以便快速地處理,比如在讀寫SD卡的時候拔下SD卡,那么需要立即檢測出該情況,然后結束讀寫線程,防止VFS崩潰。Netlink是面向數據包的服務,為內核與用戶層搭建了一個高速通道,是udev實現的基礎。該工作方式是異步的,用戶空間程序不必使用輪詢等技術來檢測熱拔插事件。
? ? 內核中使用uevent事件通知用戶空間,uevent首先在內核中調用netlink_kernel_create()函數創建一個socket套接字,該函數原型在netlink.h有定義,其類型是表示往用戶空間發送消息的NETLINK_KOBJECT_UEVENT,groups=1,由于uevent只往用戶空間發送消息而不接受,因此其輸入回調函數input和cb_mutex都設置為NULL。
#include
struct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex,
struct module *module); ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, 1, NULL, NULL, THIS_MODULE); 當有事件發生的時候,調用?kobject_uevent()函數,實際上最終是調用 netlink_broadcast_filtered(uevent_sock, skb ,?0, 1, GFP_KERNEL ,? kobj_bcast_filter,?kobj); 完成廣播任務。 用戶空間程序只需要創建一個socket描述符,將描述符綁定到接收地址,就可以實現熱拔插事件的監聽了。
#include?<stdio.h>
#include?<stdlib.h>
#include?<string.h>
#include?<errno.h>
#include?<sys/types.h>
#include?<asm/types.h>
//該頭文件需要放在netlink.h前面防止編譯出現__kernel_sa_family未定義 #include?<sys/socket.h> ?
#include?<linux/netlink.h>
void MonitorNetlinkUevent()
{
????int?sockfd;
????struct sockaddr_nl sa;
????int?len;
????char buf[4096];
????struct iovec iov;
????struct msghdr msg;
????int?i;
????memset(&sa,0,sizeof(sa));
????sa.nl_family=AF_NETLINK;
????sa.nl_groups=NETLINK_KOBJECT_UEVENT;
????sa.nl_pid?=?0;//getpid();?both?is?ok
????memset(&msg,0,sizeof(msg));
????iov.iov_base=(void?*)buf;
????iov.iov_len=sizeof(buf); ????msg.msg_name=(void?*)&sa;
????msg.msg_namelen=sizeof(sa);
????msg.msg_iov=&iov;
????msg.msg_iovlen=1;
????sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);
? ? if(sockfd==-1) ? ? ? ? printf("socket creating failed:%s\n",strerror(errno)); ????if(bind(sockfd,(struct sockaddr?*)&sa,sizeof(sa))==-1)
????????printf("bind error:%s\n",strerror(errno));
????len=recvmsg(sockfd,&msg,0);
????if(len<0)
????????printf("receive error\n");
????else?if(len<32||len>sizeof(buf))
????????printf("invalid message");
????for(i=0;i<len;i++)
????????if(*(buf+i)=='\0')
? ? ? ? ? ? buf[i]='\n';
????printf("received %d bytes\n%s\n",len,buf);
}
int?main(int?argc,char?**argv)
{
????MonitorNetlinkUevent();
????return 0;
}
創建socket描述符的時候指定協議族為AF_NETLINK或者PF_NETLINK,套接字type選擇SOCK_RAW或者SOCK_DGRAM,Netlink協議并不區分這兩種類型,第三個參數協議填充NETLINK_KOBJECT_UEVENT表示接收內核uevent信息。接著就綁定該文件描述 符到sockadd_nl,注意該結構體nl_groups是接收掩碼,取~0是將接收所有來自內核的消息,我們接收熱拔插只需要填NETLINK_KOBJECT_UEVENT即可。接下來調用recvmsg開始接收內核消息,recvmsg函數需要我們填充message報頭,包括指定接收緩存等工作。該函數會阻塞直到有熱拔插事件產生。
運行程序,然后我插入一個U盤,得到下面的結果: $ ./netlink? received 289 bytes add@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1 SUBSYSTEM=usb MAJOR=189 MINOR=8 DEVNAME=bus/usb/001/009 DEVTYPE=usb_device DEVICE=/proc/bus/usb/001/009 PRODUCT=781/5530/100 TYPE=0/0/0 BUSNUM=001 DEVNUM=009 SEQNUM=2306
運行程序,拔掉U盤 $ ./netlink? received 294 bytes remove@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0 ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0 SUBSYSTEM=bsg MAJOR=253 MINOR=2 DEVNAME=bsg/10:0:0:0 SEQNUM=2345
struct module *module); ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, 1, NULL, NULL, THIS_MODULE); 當有事件發生的時候,調用?kobject_uevent()函數,實際上最終是調用 netlink_broadcast_filtered(uevent_sock, skb ,?0, 1, GFP_KERNEL ,? kobj_bcast_filter,?kobj); 完成廣播任務。 用戶空間程序只需要創建一個socket描述符,將描述符綁定到接收地址,就可以實現熱拔插事件的監聽了。
點擊(此處)折疊或打開
創建socket描述符的時候指定協議族為AF_NETLINK或者PF_NETLINK,套接字type選擇SOCK_RAW或者SOCK_DGRAM,Netlink協議并不區分這兩種類型,第三個參數協議填充NETLINK_KOBJECT_UEVENT表示接收內核uevent信息。接著就綁定該文件描述 符到sockadd_nl,注意該結構體nl_groups是接收掩碼,取~0是將接收所有來自內核的消息,我們接收熱拔插只需要填NETLINK_KOBJECT_UEVENT即可。接下來調用recvmsg開始接收內核消息,recvmsg函數需要我們填充message報頭,包括指定接收緩存等工作。該函數會阻塞直到有熱拔插事件產生。
運行程序,然后我插入一個U盤,得到下面的結果: $ ./netlink? received 289 bytes add@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1 SUBSYSTEM=usb MAJOR=189 MINOR=8 DEVNAME=bus/usb/001/009 DEVTYPE=usb_device DEVICE=/proc/bus/usb/001/009 PRODUCT=781/5530/100 TYPE=0/0/0 BUSNUM=001 DEVNUM=009 SEQNUM=2306
運行程序,拔掉U盤 $ ./netlink? received 294 bytes remove@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0 ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0 SUBSYSTEM=bsg MAJOR=253 MINOR=2 DEVNAME=bsg/10:0:0:0 SEQNUM=2345
程序正確地接收到了U盤熱拔插事件,通過該信息用戶程序可以在第一時間得到事件通知。事實上熱拔插的時候產生的消息可不止一條呢,可以在revmsg的時候用一個循環接收更多的消息。
轉自:?http://blog.chinaunix.net/uid-24943863-id-3223000.html
總結
以上是生活随笔為你收集整理的Netlink实现热拔插监控的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Doc2Vec训练相似文章识别模型
- 下一篇: 动态内存的分配用法和构造动态一维数组