===========================================
本文系作者原创, 欢迎大家转载!
转载请注明出处:netwalker.blog.chinaunix.net
===========================================
3.使用udev在/dev/下动态生成设备文件的方式
- #include<linux/kernel.h>
- #include<linux/module.h>
- #include<linux/types.h>
- #include<linux/fs.h>
- #include<linux/cdev.h>
- #include<linux/pci.h>
- #include<linux/moduleparam.h>
- #include<linux/init.h>
- #include<linux/string.h>
- #include<asm/uaccess.h>
- #include<asm/unistd.h>
- #include<asm/uaccess.h>
- MODULE_LICENSE("GPL"); /*此处如果不加的话加载的时候会出错*/
- int init_module(void);
- void cleanup_module(void);
- static int device_open(struct inode*, struct file*);
- static int device_release(struct inode*, struct file*);
- static ssize_t device_read(struct file*, char *, size_t, loff_t*);
- static ssize_t device_write(struct file*, const char*, size_t, loff_t*);
- #define SUCCESS 0
- #define DEVICE_NAME "chardev"
- #define BUF_LEN 80
- static int major;
- static int Device_open = 0;
- static char msg[BUF_LEN];
- static char *msg_ptr;
- static struct cdev *my_cdev;
- static struct class *my_class;
- dev_t devid ;
- static struct file_operations fops =
- {
- .read = device_read,
- .write = device_write,
- .open = device_open,
- .release = device_release,
- };
- int init_module(void)
- {
- int err;
- alloc_chrdev_region(&devid, 0, 1, "chardev");
- major = MAJOR(devid);
- my_cdev = cdev_alloc();
- cdev_init(my_cdev, &fops);
- my_cdev->owner = THIS_MODULE;
- err = cdev_add(my_cdev, devid, 1);
- if (err)
- {
- printk(KERN_INFO "I was assigned major number %d.\n", major);
- return -1;
- }
- my_class = class_create(THIS_MODULE, "chardev_class1");
- if (IS_ERR(my_class))
- {
- printk(KERN_INFO "create class error\n");
- return -1;
- }
- class_device_create(my_class, NULL, devid, NULL, "chardev" "%d", MINOR(devid));
- printk("major number is %d\n", MAJOR(devid));
- return SUCCESS;
- }
- void cleanup_module(void)
- {
- cdev_del(my_cdev);
- class_device_destroy(my_class, devid);
- class_destroy(my_class);
- printk("cleanup done\n");
- }
- ......
my_cdev = cdev_alloc();cdev_init(my_cdev, &fops);
注意到上面两句话,申请cdev设备,然后调用cdev_init,然后通过cdev_add添加到内核链表中,没错啊,顺理成章,但是如果你仔细看过LDD3,那么你一定没有见过书上这样用,它的用法是:
- cdev = cdev_alloc();
- cdev->owner = THIS_MODULE;
- cdev->ops = fops;
问题的根本就是,一个用了cdev_init,一个没有。比较一下和cdev_alloc和cdev_init的代码:
- struct cdev *cdev_alloc(void)
- {
- struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
- if (p) {
- INIT_LIST_HEAD(&p->list);
- kobject_init(&p->kobj, &ktype_cdev_dynamic);
- }
- return p;
- }
- void cdev_init(struct cdev *cdev, const struct file_operations *fops)
- {
- memset(cdev, 0, sizeof *cdev);
- INIT_LIST_HEAD(&cdev->list);
- kobject_init(&cdev->kobj, &ktype_cdev_default);
- cdev->ops = fops;
- }
注意到红色部分,这里需要了解一下内核kobject的工作机制,它们像容器一样对内核中的设备进行统一的管理,可以参考网络中的相关文章,这里不做详解。
注意到kobject_init的第二个参数struct kobj_type *ktype,其中的release函数会在cdev_del时被调用:
- static struct kobj_type ktype_cdev_default = {
- .release = cdev_default_release,
- };
- static struct kobj_type ktype_cdev_dynamic = {
- .release = cdev_dynamic_release,
- };
继续追踪这两个函数的实现:
- static void cdev_default_release(struct kobject *kobj)
- {
- struct cdev *p = container_of(kobj, struct cdev, kobj);
- cdev_purge(p);
- }
- static void cdev_dynamic_release(struct kobject *kobj)
- {
- struct cdev *p = container_of(kobj, struct cdev, kobj);
- cdev_purge(p);
- kfree(p);
- }
注意到红色部分的kfree。回顾前文,如果使用cdev_alloc动态申请设备空间,此时释放函数被安装为cdev_dynamic_release,到这里还都没错,然后调用cdev_init,此时释放函数被替换为了cdev_default_release,最终模块卸载的时候,一段内存就这样偷偷溜走了!
你可以尝试在内核中搜索cdev_alloc,然后看看使用它们的地方有没有接着使用cdev_init,很遗憾,没有,如果有,那么快点报告该驱动的维护者吧,他应该看看LDD3了! ^^
世界又安静了!
阅读(3087) | 评论(0) | 转发(4) |