PF_NETLINK应用实例:NETLINK_KOBJECT_UEVENT的实现

udev的文档介绍:
  1. dynamic replacement for  /dev。作为devfs的替代者,传统的devfs不能动态分配major和minor的值,而major和minor非常有限,很快就会用完了。 udev能够像DHCP动态分配IP地址一样去动态分配major和minor。
  2. device naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,像/dev/hda1这样的名字肯定没有boot_disk这样的名字直观。udev能够像DNS解析域名一样去给设备指定一个有意义的名称。
  3. API to access info about current system devices 。提供了一组易用的API去操作sysfs,避免重复实现同样的代码。
  
  用户空间的程序与设备通信的方法,主要有以下几种方式,
  1. 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/outw/outl等函数,避开设备驱动程序,直接去操作IO端口。
  2. 用ioctl函数去操作/dev目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。
  3. 用write/read/mmap去操作/dev目录下对应的设备,这也是设备驱动程序提供的接口。像framebuffer等都是这样做的。
   #define NETLINK_ROUTE           0       /* Routing/device hook*/

        #define NETLINK_W1                1       /* 1-wire subsystem*/

        #define NETLINK_USERSOCK    2       /* Reserved for user mode socket protocols*/

        #define NETLINK_FIREWALL     3       /* Firewalling hoo*/

        #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 


  上面的方法在大多数情况下,都可以正常工作,但是对于热插拨(hotplug)的设备,比如像U盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的hotplug问题了。
  
   处理hotplug传统的方法是,在内核中执行一个称为hotplug的程序,相关参数通过环境变量传递过来,再由hotplug通知其它关注 hotplug事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用NETLINK实现的,这是一种特殊类型的socket,专门 用于内核空间与用户空间的异步通信。下面的这个简单的例子,可以监听来自内核hotplug的事件。
  1. #include <linux/netlink.h>
  2. #include <linux/types.h>
  3. #include <arpa/inet.h>
  4. #include <netinet/in.h>
  5. #include <sys/socket.h>
  6. #include <sys/ioctl.h>
  7. #include <sys/un.h>
  8. #include <errno.h>
  9. #include <unistd.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <ctype.h>


  14. #define UEVENT_BUFFER_SIZE 2048

  15. static int init_hotplug_sock(void)
  16. {
  17.     int ret;
  18.     int s =-1;
  19.     const int buffersize= 1024;
  20.     struct sockaddr_nl snl;

  21.     bzero(&snl, sizeof(struct sockaddr_nl));
  22.     snl.nl_family = AF_NETLINK;
  23.     snl.nl_pid = getpid();
  24.     snl.nl_groups = 1;

  25.     s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
  26.     if (s == -1){
  27.         perror("socket");
  28.         return -1;
  29.     }
  30.     setsockopt(s, SOL_SOCKET, SO_RCVBUF,&buffersize, sizeof(buffersize));

  31.     ret = bind(s,(struct sockaddr *)&snl, sizeof(struct sockaddr_nl));
  32.     if (ret< 0) {
  33.         perror("bind");
  34.         close(s);
  35.         return -1;
  36.     }

  37.     return s;
  38. }

  39. int main(int argc, char*argv[])
  40. {
  41.     int hotplug_sock = init_hotplug_sock();

  42.     while (1){   
  43.         char buf[UEVENT_BUFFER_SIZE * 2] = { 0 };

  44.         recv(hotplug_sock,&buf, sizeof(buf), 0);
  45.         printf("%s\n", buf);

  46.         /* USB 设备的插拔会出现字符信息,通过比较不同的信息确定特定设备的插拔,在这添加比较代码*/
  47.     }

  48.     return 0;
  49. }
编译:
  1. $ gcc -g hotplug.c -o hotplug
执行:
  1. $ ./hotplug
  2. remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0
  3. remove@/devices/pci0000:00/0000:00:1d.7/usb2/2-1
  4. add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1
  5. add@/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0

udev的主体部分在udevd.c文件中,它主要监控来自4个文件描述符的事件/消息,并做出处理:
  1. 来自客户端的控制消息。这通常由udevcontrol命令通过地址为/org/kernel/udev/udevd的本地socket,向udevd发送的控制消息。其中消息类型有:
  l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。
  l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。
  l UDEVD_CTRL_SET_LOG_LEVEL 设置LOG的级别。
  l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。
  l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制(遍历proc目录下所有进程,根据session的值判断)。
  l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。
   2. 来自内核的hotplug事件。如果有事件来源于hotplug,它读取该事件,创建一个udevd_uevent_msg对象,记录当前的消息序列号, 设置消息的状态为EVENT_QUEUED,然后并放入running_list和exec_list两个队列中,稍后再进行处理。
  3. 来自signal handler中的事件。signal  handler是异步执行的,即使有signal产生,主进程的select并不会唤醒,为了唤醒主进程的select,它建立了一个管道,在 signal handler中,向该管道写入长度为1个子节的数据,这样就可以唤醒主进程的select了。
  4. 来自配置文件变化的事件。udev通过文件系统inotify功能,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有变化,它就重新加载配置文件。
  
  其中最主要的事件,当然是来自内核的hotplug事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rule。
  
  关于rule的编写方法可以参考《writing_udev_rules》,udev_rules.c实现了对规则的解析。
  
  在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。
  
  在udevd.c:udev_event_process函数中,我们可以看到,如果RUN参数以”socket:”开头则认为是发到socket,否则认为是执行指定的程序。
  
  下面的规则是执行指定程序:
  60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
  
  下面的规则是通过socket发送消息:
  90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值