在linux下使用udev获取热插拔(hotplug)事件

 

https://blog.csdn.net/u012247418/article/details/80555556?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.control&dist_request_id=43a1f2c7-77bf-4244-b079-7595b57d8dae&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.control

udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等,设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。udev同时提供了监视接口,当设备的状态改变时,监视接口可以向应用程序报告发生的事件,当设备加入系统或从系统移除时都可以接到通知。

udev只支持linux-2.6及以上版本的内核,因为udev严重依赖于sysfs文件系统提供的信息,而sysfs文件系统只在linux-2.6内核中才有。

udev能够实现所有devfs实现的功能。但udev运行在用户模式中,而devfs运行在内核模式中。

 

作用:

1. 动态创建或删除设备文件

2. 遍历sysfs设备文件

3. hotplug(利用netlink)

 

使用udev需要先安装libudev库,在程序中包含libudev.h头文件,并且在编译时加上-ludev告诉编译器去链接udev库。

 

1. 安装libudev

sudo apt-get install libudev-dev

 

2. 编写测试代码udev-hotplugin.c

 

 
  1. #include <unistd.h>

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <errno.h>

  5. #include <signal.h>

  6. #include <sys/time.h>

  7. #include <sys/socket.h>

  8. #include <sys/un.h>

  9. #include <sys/select.h>

  10. #include <linux/types.h>

  11. #include <linux/netlink.h>

  12. #include <libudev.h>

  13.  
  14.  
  15. #undef asmlinkage

  16. #ifdef __i386__

  17. #define asmlinkage __attribute__((regparm(0)))

  18. #else

  19. #define asmlinkage

  20. #endif

  21.  
  22.  
  23. static int udev_exit;

  24.  
  25. static void asmlinkage sig_handler(int signum)

  26. {

  27. if (signum == SIGINT || signum == SIGTERM)

  28. udev_exit = 1;

  29. }

  30.  
  31.  
  32. static void print_device(struct udev_device *device, const char *source, int env)

  33. {

  34. struct timeval tv;

  35. struct timezone tz;

  36. gettimeofday(&tv, &tz);

  37.  
  38. printf("%-6s[%llu.%06u] %-8s %s (%s)\n",

  39. source,

  40. (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec,

  41. udev_device_get_action(device),

  42. udev_device_get_devpath(device),

  43. udev_device_get_subsystem(device));

  44.  
  45. if (env) {

  46. struct udev_list_entry *list_entry;

  47. udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))

  48. printf("%s=%s\n",

  49. udev_list_entry_get_name(list_entry),

  50. udev_list_entry_get_value(list_entry));

  51. printf("\n");

  52. }

  53.  
  54. }

  55.  
  56.  
  57. int udevadm_monitor(struct udev *udev)

  58. {

  59. struct sigaction act;

  60. int env = 0;

  61. int print_kernel = 1;

  62. struct udev_monitor *kernel_monitor = NULL;

  63. fd_set readfds;

  64. int rc = 0;

  65.  
  66. if (getuid() != 0) {

  67. fprintf(stderr, "root privileges needed to subscribe to kernel events\n");

  68. goto out;

  69. }

  70.  
  71. /* set signal handlers */

  72. memset(&act, 0x00, sizeof(struct sigaction));

  73. act.sa_handler = (void (*)(int)) sig_handler;

  74. sigemptyset(&act.sa_mask);

  75. act.sa_flags = SA_RESTART;

  76. sigaction(SIGINT, &act, NULL);

  77. sigaction(SIGTERM, &act, NULL);

  78.  
  79. printf("monitor will print the received events.\n");

  80.  
  81. if (print_kernel) {

  82. kernel_monitor = udev_monitor_new_from_netlink(udev, "udev"); //这里的udev源码中没有"udev"这个参数,不加进去返回值就为NULL,所以要加这个

  83. if (kernel_monitor == NULL) {

  84. rc = 3;

  85. printf("udev_monitor_new_from_netlink() error\n");

  86. goto out;

  87. }

  88.  
  89. if (udev_monitor_enable_receiving(kernel_monitor) < 0) {

  90. rc = 4;

  91. goto out;

  92. }

  93.  
  94. printf("UEVENT the kernel uevent: \n");

  95. }

  96.  
  97. printf("\n");

  98. while (!udev_exit) {

  99. int fdcount;

  100. FD_ZERO(&readfds);

  101.  
  102. if (kernel_monitor != NULL)

  103. FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);

  104.  
  105. fdcount = select(udev_monitor_get_fd(kernel_monitor)+1, &readfds, NULL, NULL, NULL);

  106. if (fdcount < 0) {

  107. if (errno != EINTR)

  108. fprintf(stderr, "error receiving uevent message: %m\n");

  109. continue;

  110. }

  111.  
  112. if ((kernel_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {

  113. struct udev_device *device;

  114. device = udev_monitor_receive_device(kernel_monitor);

  115. if (device == NULL)

  116. continue;

  117. print_device(device, "UEVENT", env);

  118. udev_device_unref(device);

  119. }

  120.  
  121. }

  122.  
  123. out:

  124. udev_monitor_unref(kernel_monitor);

  125. return rc;

  126. }

  127.  
  128.  
  129.  
  130. int main(int argc, char *argv[])

  131. {

  132. struct udev *udev;

  133. int rc = 1;

  134.  
  135. udev = udev_new();

  136. if (udev == NULL)

  137. goto out;

  138.  
  139. udevadm_monitor(udev);

  140. goto out;

  141. rc = 2;

  142.  
  143. out:

  144. udev_unref(udev);

  145. return rc;

  146. }

 

 

3. 测试

1)编译

gcc -o udevhotplug udev-hotplugin.c -ludev

2)以root权限执行

sudo ./udevhotplug

当插拔一个USB设备时,显示如下:

 

4. libudev API介绍

4.1 初始化

首先调用udev_new,创建一个udev library context。udev library context采用引用记数机制,创建的context默认引用记数为1,使用udev_ref和udev_unref增加或减少引用记数,如果引用记数为0,则释放内部资源。

 

4.2 枚举设备

使用udev_enumrate_new创建一个枚举器,用于扫描系统已接设备。使用udev_enumrate_ref和udev_enumrate_unref增加或减少引用记数。

使用udev_enumrate_add_match/nomatch_xxx系列函数增加枚举的过滤器,过滤关键字以字符表示,如"block"设备。

使用udev_enumrate_scan_xxx系列函数扫描/sys目录下,所有与过滤器匹配的设备。扫描完成后的数据结构是一个链表,使用udev_enumerate_get_list_entry获取链表的首个结点,使用udev_list_entry_foreach遍历整个链表。

 

4.3 监控设备插拔 udev的设备插拔基于netlink实现。

1)使用udev_monitor_new_from_netlink创建一个新的monitor,函数的第二个参数是事件源的名称,可选"kernel"或"udev"。基于"kernel"的事件通知要早于"udev",但相关的设备结点未必创建完成,所以一般应用的设计要基于"udev"进行监控。

2)使用udev_monitor_filter_add_match_subsystem_devtype增加一个基于设备类型的udev事件过滤器,例如: "block"设备。

3)使用udev_monitor_enable_receiving启动监控过程。监控可以使用udev_monitor_get_fd获取一个文件描述符,基于返回的fd可以执行poll操作,简化程序设计。

4)插拔事件到达后,可以使用udev_monitor_receive_device获取产生事件的设备映射。调用udev_device_get_action可以获得一个字符串:"add"或者"remove",以及"change", "online", "offline"等,但后三个未知什么情况下会产生。

 

4.4 获取设备信息

使用udev_list_entry_get_name可以得到一个设备结点的sys路径,基于这个路径使用udev_device_new_from_syspath可以创建一个udev设备的映射,用于获取设备属性。获取设备属性使用udev_device_get_properties_list_entry,返回一个存储了设备所有属性信息的链表,使用udev_list_entry_foreach遍历链表,使用udev_list_entry_get_name和udev_list_entry_get_value获取属性的名称和值。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值