過年前跑去書店打發無聊的時間,看到這個月又出了兩本跟linux device driver相關的書籍,隨手拿起來翻翻,發現其中有一篇寫到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
- charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(charmodule_class))
- return -EFAULT;
- device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);
character kernel moduel移除時會remove class和destroy device
- device_destroy(charmodule_class,MKDEV(driver_major, 0));
- class_destroy(charmodule_class);
我把udevd和udevadm移植到實驗平台,並且執行指令udevd –d,當我insert character kernel module時,udevd會自動幫我建立device node,整個實驗過程如下圖

character kernel module程式如下
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/major.h>
- #include <linux/device.h>
- #include <linux/poll.h>
-
- #define dbg(fmt,args...) printk("[%s]:%d => "fmt,__FUNCTION__,__LINE__,##args)
- #define DBG() printk("[%s]:%d => \n",__FUNCTION__,__LINE__)
- #define DEVICE_NAME "charmodule"
- #define STRING_SIZE 256
- #define STRING_CHANGE_TIME 2 //seconds
-
- static int driver_major;
- static int string_counter=1;
- static struct class *charmodule_class;
-
- typedef struct _driver_private_data
- {
- wait_queue_head_t wait;
- struct fasync_struct *fasync;
- struct timer_list char_timer;
- char my_string[256];
- int flag;
-
- }driver_private_data;
-
- driver_private_data *gpd;
- void MyTimerFunction(unsigned long data);
-
- static ssize_t char_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
- {
- int retval=0;
- driver_private_data *dpd = file->private_data;
-
- DBG();
- if (count<STRING_SIZE) return -EINVAL;
- while ((retval + STRING_SIZE)<= count)
- {
- if (copy_to_user(buffer, dpd->my_string, STRING_SIZE))
- return -EFAULT;
- retval += STRING_SIZE;
- }
- dpd->flag=0;
- return retval;
- }
-
- static ssize_t char_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos)
- {
- return STRING_SIZE;//just a demo, I dont implement this
- }
-
- static int char_open(struct inode *inode, struct file *file)
- {
-
- driver_private_data *dpd;
-
- DBG();
- dpd=(driver_private_data *)file->private_data;
- if (!dpd)
- {
- dpd=(driver_private_data *)kmalloc(sizeof(driver_private_data),GFP_ATOMIC);
- file->private_data=dpd;
- sprintf(dpd->my_string,"string_counter %d\n",string_counter);
- dpd->flag=0;
- string_counter++;
- init_waitqueue_head(&dpd->wait);
- gpd=dpd;
- init_timer(&dpd->char_timer);
- dpd->char_timer.function = MyTimerFunction;
- dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
- dpd->char_timer.data = (unsigned long) gpd;
- add_timer(&dpd->char_timer);
- }
- return 0;
- }
- static unsigned int char_poll(struct file *file, poll_table *wait)
- {
- driver_private_data *dpd = file->private_data;
- int mask=0;
-
- DBG();
- poll_wait(file, &dpd->wait, wait);
- if (dpd->flag==1)
- mask |= POLLIN | POLLRDNORM;
- return mask;
- }
-
- static int char_release(struct inode *inode, struct file *file)
- {
- driver_private_data *dpd;
-
- DBG();
- dpd=(driver_private_data *)file->private_data;
- del_timer(&dpd->char_timer);
- return 0;
- }
-
-
-
-
- static const struct file_operations chardev_fops =
- {
- .owner = THIS_MODULE,
- .read = char_read,
- .write = char_write,
- .poll = char_poll,
- .open = char_open,
- .release = char_release,
- };
-
- void MyTimerFunction(unsigned long data)
- {
- driver_private_data *dpd=(driver_private_data *)data;
- if (dpd)
- {
- sprintf(dpd->my_string,"string_counter %d\n",string_counter);
- string_counter++;
- //wake_up_interruptible(&dpd->wait);
- dpd->flag=1;
- init_timer(&dpd->char_timer);
- dpd->char_timer.function = MyTimerFunction;
- dpd->char_timer.expires = jiffies + STRING_CHANGE_TIME*HZ;
- dpd->char_timer.data = (unsigned long) dpd;
- add_timer(&dpd->char_timer);
- }
- }
-
- static int __init charmodule_init(void)
- {
-
- dbg("char module demo\n");
- driver_major=register_chrdev(0, DEVICE_NAME, &chardev_fops);
- if (driver_major<0)
- {
- dbg("Register character device failed\n");
- return -EFAULT;
- }
- else dbg("mknod /dev/%s c %d 0\n",DEVICE_NAME,driver_major);
- charmodule_class=class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(charmodule_class))
- return -EFAULT;
- device_create(charmodule_class, NULL, MKDEV(driver_major, 0), DEVICE_NAME);
-
- return 0;
- }
-
- static void __exit charmodule_exit(void)
- {
- unregister_chrdev(driver_major, DEVICE_NAME);
- device_destroy(charmodule_class,MKDEV(driver_major, 0));
- class_destroy(charmodule_class);
- dbg("remove driver successfully\n");
- }
- module_init(charmodule_init);
- module_exit(charmodule_exit);
-
- MODULE_DESCRIPTION("led module");
- MODULE_AUTHOR("Joey Cheng<jemicheng@gmail.com>");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("QT2410:char module");
Regular expression-跟brainfuck差不多的東西 (2009-11-13 15:37)
Reading file in kernel-簡單但實用 (2009-10-13 15:18)
ttysnoop-沒有螺絲起子的日子 (2009-08-25 10:49)
Linux file system for dummies-只花你45分鐘 (2009-08-19 15:40)
OPENSSL-TCP SSL初心者之路 (2009-07-16 15:16)
fw_printenv-control u-boot enviroment variables under linux (2009-05-12 16:35)
NAPI與pure interrupt driver的效能比較 (2009-04-29 19:06)
usermode helper-來自kernel的呼喚 (2009-04-21 16:19)
rpcapd與netcat-測試嵌入式系統的好工具 (2009-04-09 17:06)