uevent是user space event,是内核通知用户空间某种事件发生的一种机制。而用户空间可以通过Netlink去监听Uevent,来捕捉uevent事件。而系统已经提供了相关的协议类型 linux/netlink.h:
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Firewalling hook */
#define NETLINK_INET_DIAG 4 /* INET socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
这里是监听uevent,那么就使用NETLINK_KOBJECT_UEVENT
用户空间:
1. 需要建立一个线程去监听通过NETLINK_KOBJECT_UEVENT协议簇创建的socket:
static int init_netlink_sock(struct sockaddr_nl *nl)
{
const int buffersize = 4096;
int ret;
nl->nl_family = AF_NETLINK;
nl->nl_pid = getpid(); /*这里是作为bind函数的参数,那么这个值没啥意义,保证唯一性就可以了,如果不清楚就设置成0,让内核去保证它的唯一性,如果这个nl要应用到sendto中,那么这个nl_pid就有不同的含义:
0:发送到内核;非0:则表示发送到其他进程*/
nl->nl_groups =1; /*组播掩码,当使用sendto的时候,这个值设置了非0,那么接收进程也必须设置相同的才能收取,这里经过测试,再监听内核的时候,uevent的事件,设置成1就可以*/
int s = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
if (s < 0)
{
perror("socket");
return -1;
}
//setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
ret = bind(s, (struct sockaddr *)nl, sizeof(struct sockaddr_nl)); /*绑定socket*/
if (ret < 0)
{
perror("bind");
close(s);
return -1;
}
return s;
}
1. 调用recv函数监听socket
bzero((void *)&nl, (size_t)(sizeof(struct sockaddr_nl)));
int uevent_sock = init_netlink_sock(&nl);/*调用第1点,请求socket并绑定*/
len= recv(uevent_sock, &buf, 4096, 0); /*阻塞收取uevent*/
printf("recv:");
int i = 0;
for(i=0;i<len;i++)
if(*(buf+i)=='\0') buf[i]='\n';
printf("len:%d ---%s\n", len,buf);
内核模块端:
内核驱动注册设备后,调用kobject_uevent函数或者kobject_uevent_env函数向用户空间广播uevent事件。
kobject_uevent,kobject_uevent_env 两者区别是kobject_uevent_env可以发送用户自定义的环境变量。
最后,用户空间还可以使用recvmsg的方法来收取数据:
则在调用recvmsg的时候,需要填充第二个参数: struct msghdr *msg, 下面给出其初始化过程:
struct iovec iov; /*定义一个缓冲区*/
struct msghdr msg;
memset(&msg,0,sizeof(msg));
iov.iov_base=(void *)buf;
iov.iov_len=sizeof(buf);
msg.msg_name=(void *)&nl;
msg.msg_namelen=sizeof(nl);
msg.msg_iov=&iov;
msg.msg_iovlen=1;
然后:
len=recvmsg(uevent_sock,&msg,0);
经测试,能够在用户空间捕捉到uevent事件:
recvmsg:len:165 ---change@/devices/virtual/misc/uevent1
ACTION=change
DEVPATH=/devices/virtual/misc/uevent1
SUBSYSTEM=misc
GPIO=0
GPIO1=1 /*用户添加的内容*/
MAJOR=10
MINOR=56
DEVNAME=uevent1
SEQNUM=7807
以上完整历程见附件:
评论0
最新资源