在linux下使用udev获取热插拔(hotplug)事件
?
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
?
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <linux/types.h> #include <linux/netlink.h> #include <libudev.h>#undef asmlinkage #ifdef __i386__ #define asmlinkage __attribute__((regparm(0))) #else #define asmlinkage #endifstatic int udev_exit;static void asmlinkage sig_handler(int signum) {if (signum == SIGINT || signum == SIGTERM)udev_exit = 1; }static void print_device(struct udev_device *device, const char *source, int env) {struct timeval tv;struct timezone tz;gettimeofday(&tv, &tz);printf("%-6s[%llu.%06u] %-8s %s (%s)\n",source,(unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec,udev_device_get_action(device),udev_device_get_devpath(device),udev_device_get_subsystem(device));if (env) {struct udev_list_entry *list_entry;udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))printf("%s=%s\n",udev_list_entry_get_name(list_entry),udev_list_entry_get_value(list_entry));printf("\n");}}int udevadm_monitor(struct udev *udev) {struct sigaction act;int env = 0;int print_kernel = 1;struct udev_monitor *kernel_monitor = NULL;fd_set readfds;int rc = 0;if (getuid() != 0) {fprintf(stderr, "root privileges needed to subscribe to kernel events\n");goto out;}/* set signal handlers */memset(&act, 0x00, sizeof(struct sigaction));act.sa_handler = (void (*)(int)) sig_handler;sigemptyset(&act.sa_mask);act.sa_flags = SA_RESTART;sigaction(SIGINT, &act, NULL);sigaction(SIGTERM, &act, NULL);printf("monitor will print the received events.\n");if (print_kernel) {kernel_monitor = udev_monitor_new_from_netlink(udev, "udev"); //這里的udev源碼中沒有"udev"這個參數,不加進去返回值就為NULL,所以要加這個if (kernel_monitor == NULL) {rc = 3;printf("udev_monitor_new_from_netlink() error\n");goto out;}if (udev_monitor_enable_receiving(kernel_monitor) < 0) {rc = 4;goto out;}printf("UEVENT the kernel uevent: \n");}printf("\n");while (!udev_exit) {int fdcount;FD_ZERO(&readfds);if (kernel_monitor != NULL)FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);fdcount = select(udev_monitor_get_fd(kernel_monitor)+1, &readfds, NULL, NULL, NULL);if (fdcount < 0) {if (errno != EINTR)fprintf(stderr, "error receiving uevent message: %m\n");continue;}if ((kernel_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {struct udev_device *device;device = udev_monitor_receive_device(kernel_monitor);if (device == NULL)continue;print_device(device, "UEVENT", env);udev_device_unref(device);}}out:udev_monitor_unref(kernel_monitor);return rc; }int main(int argc, char *argv[]) {struct udev *udev;int rc = 1;udev = udev_new();if (udev == NULL)goto out;udevadm_monitor(udev);goto out;rc = 2;out:udev_unref(udev);return rc; }?
?
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獲取屬性的名稱和值。
?
總結
以上是生活随笔為你收集整理的在linux下使用udev获取热插拔(hotplug)事件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三星S23系列最全爆料:超大杯独享1TB
- 下一篇: linux2.6版及以后内核:支持实时、