201007191601udev-強大的device node管理系統

發現其中有一篇寫到udev framework,裡面詳盡解釋device node在insert kernel module時如何自動建立,並且可隨著使用者更改規則而產生persistent node…等不同於devfs的變化

udev的官方網頁有篇不錯的conference paper,裡面有提到幾個重點,udev的出現為了解決目前在devfs上碰到的3個問題
1.udev能夠根據規則建立device node(這可以解決probing時,device node可能會依probing的順序不同而改變)
2.udev動態建立device node,不會像以前在/dev資料夾下擺一堆多而無用的device node
3.提供user更方便的API存取目前device的資訊,在kernel 2.6以上已提供sysfs這個device管理機制

udev運作的原理很簡單,它透過netlink得知目前kernel有那些module新增了,在收到kernel module新增的netlink訊息之後,它會先掃描user是否有指派device node rule,如果沒有,會自動根據module的major和minor number建立device node.有一點比較特殊的地方是,udev用inotify監聽rule變化的event,所以可以即時改變device node的狀態,udev的架構圖可參考如下

那我做個小實驗,看看udev到底是如何運作的,這個實驗分為兩個部份,一個是character kernel module,而另外一個就是udev的user space program(下載udev-137.tar.gz)

character kernel module寫作的重點在於init時會建立class與create device

  1. charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
  2. if (IS_ERR(charmodule_class)) 
  3. return -EFAULT;
  4. device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);

character kernel moduel移除時會remove class和destroy device

  1. device_destroy(charmodule_class,MKDEV(driver_major, 0));
  2. class_destroy(charmodule_class);

我把udevd和udevadm移植到實驗平台,並且執行指令udevd –d,當我insert character kernel module時,udevd會自動幫我建立device node,整個實驗過程如下圖

character kernel module程式如下

  1. #include <linux/module.h> 
  2. #include <linux/kernel.h> 
  3. #include <linux/init.h> 
  4. #include <linux/major.h> 
  5. #include <linux/device.h> 
  6. #include <linux/poll.h> 
  7. #define dbg(fmt,args...) printk("[%s]:%d => "fmt,__FUNCTION__,__LINE__,##args)
  8. #define DBG() printk("[%s]:%d => \n",__FUNCTION__,__LINE__)
  9. #define DEVICE_NAME "charmodule"
  10. #define STRING_SIZE 256
  11. #define STRING_CHANGE_TIME 2 //seconds
  12. static int driver_major;
  13. static int string_counter=1;
  14. static struct class *charmodule_class;
  15. typedef struct _driver_private_data 
  16. { 
  17. wait_queue_head_t wait;
  18. struct fasync_struct *fasync;
  19. struct timer_list char_timer;
  20. char my_string[256];
  21. int flag;
  22. }driver_private_data;
  23. driver_private_data *gpd;
  24. void MyTimerFunction(unsigned long data);
  25. static ssize_t char_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos) 
  26. { 
  27. int retval=0;
  28. driver_private_data *dpd = file->private_data;
  29. DBG();
  30. if (count<STRING_SIZE) return -EINVAL;
  31. while ((retval + STRING_SIZE)<= count) 
  32. { 
  33. if (copy_to_user(buffer, dpd->my_string, STRING_SIZE)) 
  34. return -EFAULT;
  35. retval += STRING_SIZE;
  36. } 
  37. dpd->flag=0;
  38. return retval;
  39. } 
  40. static ssize_t char_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos) 
  41. { 
  42. return STRING_SIZE;//just a demo, I dont implement this
  43. } 
  44. static int char_open(struct inode *inode, struct file *file) 
  45. { 
  46. driver_private_data *dpd;
  47. DBG();
  48. dpd=(driver_private_data *)file->private_data;
  49. if (!dpd) 
  50. { 
  51. dpd=(driver_private_data *)kmalloc(sizeof(driver_private_data),GFP_ATOMIC);
  52. file->private_data=dpd;
  53. sprintf(dpd->my_string,"string_counter %d\n",string_counter);
  54. dpd->flag=0;
  55. string_counter++;
  56. init_waitqueue_head(&dpd->wait);
  57. gpd=dpd;
  58. init_timer(&dpd->char_timer);
  59. dpd->char_timer.function = MyTimerFunction;
  60. dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
  61. dpd->char_timer.data = (unsigned long) gpd;
  62. add_timer(&dpd->char_timer);
  63. } 
  64. return 0;
  65. } 
  66. static unsigned int char_poll(struct file *file, poll_table *wait) 
  67. { 
  68. driver_private_data *dpd = file->private_data;
  69. int mask=0;
  70. DBG();
  71. poll_wait(file, &dpd->wait, wait);
  72. if (dpd->flag==1) 
  73. mask |= POLLIN | POLLRDNORM;
  74. return mask;
  75. } 
  76. static int char_release(struct inode *inode, struct file *file) 
  77. { 
  78. driver_private_data *dpd;
  79. DBG();
  80. dpd=(driver_private_data *)file->private_data;
  81. del_timer(&dpd->char_timer);
  82. return 0;
  83. } 
  84. static const struct file_operations chardev_fops =
  85. { 
  86. .owner = THIS_MODULE,
  87. .read = char_read,
  88. .write = char_write,
  89. .poll = char_poll,
  90. .open = char_open,
  91. .release = char_release,
  92. };
  93. void MyTimerFunction(unsigned long data) 
  94. { 
  95. driver_private_data *dpd=(driver_private_data *)data;
  96. if (dpd) 
  97. { 
  98. sprintf(dpd->my_string,"string_counter %d\n",string_counter);
  99. string_counter++;
  100. //wake_up_interruptible(&dpd->wait);
  101. dpd->flag=1;
  102. init_timer(&dpd->char_timer);
  103. dpd->char_timer.function = MyTimerFunction;
  104. dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
  105. dpd->char_timer.data = (unsigned long) dpd;
  106. add_timer(&dpd->char_timer);
  107. } 
  108. } 
  109. static int __init charmodule_init(void) 
  110. { 
  111. dbg("char module demo\n");
  112. driver_major=register_chrdev(0, DEVICE_NAME, &chardev_fops);
  113. if (driver_major<0) 
  114. { 
  115. dbg("Register character device failed\n");
  116. return -EFAULT;
  117. } 
  118. else dbg("mknod /dev/%s c %d 0\n",DEVICE_NAME,driver_major);
  119. charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
  120. if (IS_ERR(charmodule_class)) 
  121. return -EFAULT;
  122. device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);
  123. return 0;
  124. } 
  125. static void __exit charmodule_exit(void) 
  126. { 
  127. unregister_chrdev(driver_major, DEVICE_NAME);
  128. device_destroy(charmodule_class,MKDEV(driver_major, 0));
  129. class_destroy(charmodule_class);
  130. dbg("remove driver successfully\n");
  131. } 
  132. module_init(charmodule_init);
  133. module_exit(charmodule_exit);
  134. MODULE_DESCRIPTION("led module");
  135. MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
  136. MODULE_LICENSE("GPL");
  137. MODULE_ALIAS("QT2410:char module");
沒有上一則|日誌首頁|沒有下一則
回應





Powered by Xuite
    沒有新回應!
關鍵字