找回密码
 注册
关于网站域名变更的通知
查看: 283|回复: 1
打印 上一主题 下一主题

Netlink实现热拔插监控

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-11-20 14:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
+ E3 Y8 J2 O% `8 T! b/ I0 H  ?
新的Linux内核使用udev代替了hotplug作为热拔插管理,虽然有udevd管理热拔插,但有时候我们还是需要在应用程序中检测热拔插事件以便快速地处理,比如在读写SD卡的时候拔下SD卡,那么需要立即检测出该情况,然后结束读写线程,防止VFS崩溃。Netlink是面向数据包的服务,为内核与用户层搭建了一个高速通道,是udev实现的基础。该工作方式是异步的,用户空间程序不必使用轮询等技术来检测热拔插事件。
0 u6 y& |6 T0 L/ T% r    内核中使用uevent事件通知用户空间,uevent首先在内核中调用netlink_kernel_create()函数创建一个socket套接字,该函数原型在netlink.h有定义,其类型是表示往用户空间发送消息的NETLINK_KOBJECT_UEVENT,groups=1,由于uevent只往用户空间发送消息而不接受,因此其输入回调函数input和cb_mutex都设置为NULL。
3 i* x- d# s/ a- w6 B% r#include  L4 }- T# z! |/ R
struct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups,
. P0 j  Z7 o0 n8 s; u                                                  void (*input)(struct sk_buff *skb),
: d, f4 ~) {* H$ o3 f                                                  struct mutex *cb_mutex,. U  v/ b/ m' v
                                                  struct module *module);
  u+ t/ A$ j) E3 ?
2 T  Y% A/ I. x9 q$ o) }ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,1 B& z7 f! ^: x. _4 Q
                                                        1, NULL, NULL, THIS_MODULE);' j% ]# O" ^0 `+ l$ U
当有事件发生的时候,调用 kobject_uevent()函数,实际上最终是调用5 K- U# k3 d; @$ ?! a6 M+ n
netlink_broadcast_filtered(uevent_sock, skb , 0, 1, GFP_KERNEL ,
! z0 ]. b' K/ s2 q6 n                                        kobj_bcast_filter, kobj);9 m0 k7 |' m) c3 z) S
完成广播任务。
+ s4 ?) V: a) r& P    用户空间程序只需要创建一个socket描述符,将描述符绑定到接收地址,就可以实现热拔插事件的监听了。
( Y( I. P0 n: ]
7 N" j% G6 H3 c- N; e8 z) d& [点击(此处)折叠或打开; @. |0 f# l0 @" U* n

# B) l7 ~8 L, D2 J$ E3 R4 G/ T#include <stdio.h>% |, c, C  u7 m& H, g2 ]9 C9 C5 B
#include <stdlib.h>4 z$ T. _, m( W6 V( F$ W
#include <string.h>
* c; }, {' o% N# [% @#include <errno.h>
$ T4 O, q5 ]- U#include <sys/types.h>- ^: R2 D* T; z7 S( p1 V  ]( Q6 M
#include <asm/types.h>% c* a+ p, f+ l, q
//该头文件需要放在netlink.h前面防止编译出现__kernel_sa_family未定义
; n$ d+ c  k6 j6 \( f0 z#include <sys/socket.h>  
! w0 c# S4 R1 }/ V+ r. _& p#include <linux/netlink.h># }% d; O: A/ K' o1 i% }  G
. p) E, Y# V( Y4 Y+ C
void MonitorNetlinkUevent()3 L1 L# N) R, z& h3 @
{& ]" K& z0 g3 c2 G, @
    int sockfd;
+ A2 E+ P5 P7 |5 Q8 M4 f; y# z    struct sockaddr_nl sa;
  q) D8 P3 Y  g/ s0 v7 P    int len;
2 \: Q7 z$ R2 t& @9 A) t    char buf[4096];7 l# x6 Q+ \+ \) j
    struct iovec iov;# k( w8 z1 U0 C- z3 X9 w2 l
    struct msghdr msg;2 _9 c& Q# c5 _; X4 S
    int i;# C. m/ k* e0 h4 w

! j7 {/ F, \* H( G6 \$ O5 {    memset(&sa,0,sizeof(sa));3 B2 L6 R) S8 b4 s& a
    sa.nl_family=AF_NETLINK;
! o4 ~, r0 U1 r    sa.nl_groups=NETLINK_KOBJECT_UEVENT;7 j7 V- j9 q$ h+ C9 g
    sa.nl_pid = 0;//getpid(); both is ok! x4 F' g( t+ O2 f9 D: t9 L( c
    memset(&msg,0,sizeof(msg));. b2 ], C; t) x7 a
    iov.iov_base=(void *)buf;
5 t% K5 ?" ~' k/ n    iov.iov_len=sizeof(buf);
+ S1 `: O) f7 v  i" e  V# p, ^    msg.msg_name=(void *)&sa;6 ?& X0 P- W0 f5 p9 v9 I3 J
    msg.msg_namelen=sizeof(sa);  q0 n7 j$ s. F# w- R) j* @
    msg.msg_iov=&iov;
3 l0 L8 [9 H% ^/ P$ ]4 a% X" u# {    msg.msg_iovlen=1;) s) s8 x, n# T, T% p2 F

# {7 U. l( g# p; `, u  I3 W2 B    sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);( b: Y  S/ Q! _# k7 Q- ]. I
    if(sockfd==-1)
( F: l$ B# O( @8 x9 O        printf("socket creating failed:%s\n",strerror(errno));( Q6 r1 N4 a' }# G$ Y
    if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-1)
& q1 V* M' X4 N        printf("bind error:%s\n",strerror(errno));
0 q( i  @7 \2 S. Z9 C/ y2 J; ~8 G+ B7 |1 u! o4 w+ n
    len=recvmsg(sockfd,&msg,0);
8 s0 \. w5 L  R/ C    if(len<0). o, I: q5 \- _9 L
        printf("receive error\n");: W- \# T+ s/ o* i# _
    else if(len<32||len>sizeof(buf))' T, P4 ~0 s+ e! L# _
        printf("invalid message");
& y/ g1 p9 ?" _- W    for(i=0;i<len;i++)- m! H2 q0 i( K" A, _
        if(*(buf+i)=='\0')& p' P+ [" ?1 ]3 g2 r4 z* a$ Q
            buf='\n';! d- ]; [, ^+ S( n" {) C$ W3 p+ m3 W
    printf("received %d bytes\n%s\n",len,buf);5 y( |) _# D9 J0 b' X
}( W! E2 K1 b+ |6 k$ j
7 ]! v# n% F' K, a( A5 A& `
int main(int argc,char **argv)1 Y$ G% h6 h- O" {: j& n, F
{
9 X) D1 D0 G  }0 O( B  I; E    MonitorNetlinkUevent();* w  p' `) b0 a  H
    return 0;) C. O: [( ]4 k+ ^
}
5 O2 S# V, y4 c  @" @( t) Y( `  |: J6 c9 [- T  l
创建socket描述符的时候指定协议族为AF_NETLINK或者PF_NETLINK,套接字type选择SOCK_RAW或者SOCK_DGRAM,Netlink协议并不区分这两种类型,第三个参数协议填充NETLINK_KOBJECT_UEVENT表示接收内核uevent信息。接着就绑定该文件描述, I7 f6 k/ V: z
符到sockadd_nl,注意该结构体nl_groups是接收掩码,取~0是将接收所有来自内核的消息,我们接收热拔插只需要填NETLINK_KOBJECT_UEVENT即可。接下来调用recvmsg开始接收内核消息,recvmsg函数需要我们填充message报头,包括指定接收缓存等工作。该函数会阻塞直到有热拔插事件产生。7 ?& J/ p3 D. r
- l' l/ w" l6 ^; I
运行程序,然后我插入一个U盘,得到下面的结果:
1 Z, @  K7 m6 e) C$ ./netlink 6 o8 a7 D4 F- S
received 289 bytes$ q& O) K5 O, @, w9 K( M8 \
add@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1
: \6 E$ \+ k% b& y) _( m( }ACTION=add
" E9 V! a- a* s  j. v6 L/ z( Z$ PDEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.17 a4 d3 \3 w. ]$ h
SUBSYSTEM=usb$ z( M( L: O* z0 h
MAJOR=189; J, X. P6 X3 A) ]1 \
MINOR=8
+ x% J- Z; B2 W/ MDEVNAME=bus/usb/001/0098 a. l% \6 r) i" a( M# Q
DEVTYPE=usb_device
* Z9 R" S' }4 wDEVICE=/proc/bus/usb/001/009& o+ A( T6 x- m
PRODUCT=781/5530/100
/ ]8 i. D4 g$ }; J3 I7 K  YTYPE=0/0/0" G- g- f; h# O# P4 f& _
BUSNUM=001& w8 `( f5 c. s4 O# @) B
DEVNUM=009* o3 q: f" ]* A0 t7 t* f
SEQNUM=2306
2 r* Q4 R6 d. M
; \$ |8 M- b4 O! s运行程序,拔掉U盘
+ n' G, S& P. o5 m6 O/ w. d1 g* g. m, p$ ./netlink 9 `2 @% Y, ]) b2 M6 g
received 294 bytes$ H( t2 \$ a; b# Q
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
0 k( x. p6 q1 D* E3 m5 p" l, }ACTION=remove
. B3 z7 B+ Z( v- b1 ~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
9 k3 y! O2 P/ E. r$ Z6 _SUBSYSTEM=bsg8 O: u" [0 r8 s- h7 Y8 X% c: C
MAJOR=253
$ s* ?9 y1 c; j+ r) j7 U! N( }MINOR=2
& z% w0 T% i: ]6 }7 zDEVNAME=bsg/10:0:0:0; _! R" M) ~! j1 m' B5 D
SEQNUM=2345. ]: x6 _& |  K* Y2 t. w

, a8 k& o2 z" X8 C7 z4 V程序正确地接收到了U盘热拔插事件,通过该信息用户程序可以在第一时间得到事件通知。事实上热拔插的时候产生的消息可不止一条呢,可以在revmsg的时候用一个循环接收更多的消息。
  • TA的每日心情
    慵懒
    2020-6-13 15:46
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-11-20 15:19 | 只看该作者
    Netlink实现热拔插监控
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-24 21:29 , Processed in 0.156250 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表