Chinaunix首页 | 论坛 | 博客
  • 博客访问: 412235
  • 博文数量: 70
  • 博客积分: 87
  • 博客等级: 民兵
  • 技术积分: 446
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-10 22:18
文章分类

全部博文(70)

文章存档

2020年(3)

2018年(1)

2017年(4)

2016年(1)

2015年(3)

2014年(11)

2013年(22)

2012年(25)

我的朋友

分类: LINUX

2013-02-23 21:03:01

===========================================
本文系作者原创, 欢迎大家转载!
转载请注明出处:netwalker.blog.chinaunix.net
===========================================
 
《创建字符设备的三种方法》这篇文章已经在网上广为流传,你可以在http://blogold.chinaunix.net/u1/36290/showart_415402.html找到这篇文章的一份拷贝,从浏览量上看定有许多人参考过这篇文章,但是很遗憾,它的第三种方法有一个严重的问题,内存泄露!虽然大多数时候它跑的很好,而内存泄露导致系统崩溃也是几无可能,但是这是一个模板,如果大多数人在写驱动时参考了这篇文章,并且不巧选择了第三种,那么潜在危险就有点大了,好了言归正传。
 
     3.使用udev在/dev/下动态生成设备文件的方式
  1. #include<linux/kernel.h>
  2. #include<linux/module.h>
  3. #include<linux/types.h>
  4. #include<linux/fs.h>
  5. #include<linux/cdev.h>
  6. #include<linux/pci.h>
  7. #include<linux/moduleparam.h>
  8. #include<linux/init.h>
  9. #include<linux/string.h>
  10. #include<asm/uaccess.h>
  11. #include<asm/unistd.h>
  12. #include<asm/uaccess.h>
  13. MODULE_LICENSE("GPL"); /*此处如果不加的话加载的时候会出错*/
  14. int init_module(void);
  15. void cleanup_module(void);
  16. static int device_open(struct inode*, struct file*);
  17. static int device_release(struct inode*, struct file*);
  18. static ssize_t device_read(struct file*, char *, size_t, loff_t*);
  19. static ssize_t device_write(struct file*, const char*, size_t, loff_t*);
  20. #define SUCCESS 0
  21. #define DEVICE_NAME "chardev"
  22. #define BUF_LEN 80

  23. static int major;
  24. static int Device_open = 0;
  25. static char msg[BUF_LEN];
  26. static char *msg_ptr;
  27. static struct cdev *my_cdev;
  28. static struct class *my_class;
  29. dev_t devid ;
  30. static struct file_operations fops =
  31. {
  32.         .read = device_read,
  33.         .write = device_write,
  34.         .open = device_open,
  35.         .release = device_release,
  36. };
  37. int init_module(void)
  38. {
  39.         int err;
  40.         alloc_chrdev_region(&devid, 0, 1, "chardev");
  41.         major = MAJOR(devid);
  42.         my_cdev = cdev_alloc();
  43.         cdev_init(my_cdev, &fops);
  44.         my_cdev->owner = THIS_MODULE;
  45.         err = cdev_add(my_cdev, devid, 1);
  46.         if (err)
  47.         {
  48.                 printk(KERN_INFO "I was assigned major number %d.\n", major);
  49.                 return -1;
  50.         }
  51.         my_class = class_create(THIS_MODULE, "chardev_class1");
  52.         if (IS_ERR(my_class))
  53.         {
  54.                 printk(KERN_INFO "create class error\n");
  55.                 return -1;
  56.         }
  57.         class_device_create(my_class, NULL, devid, NULL, "chardev" "%d", MINOR(devid));
  58.         printk("major number is %d\n", MAJOR(devid));
  59.         return SUCCESS;
  60. }
  61. void cleanup_module(void)
  62. {
  63.         cdev_del(my_cdev);
  64.         class_device_destroy(my_class, devid);
  65.         class_destroy(my_class);
  66.         printk("cleanup done\n");
  67. }
  68. ......

my_cdev = cdev_alloc();cdev_init(my_cdev, &fops);

注意到上面两句话,申请cdev设备,然后调用cdev_init,然后通过cdev_add添加到内核链表中,没错啊,顺理成章,但是如果你仔细看过LDD3,那么你一定没有见过书上这样用,它的用法是:

  1. cdev = cdev_alloc();
  2.  cdev->owner = THIS_MODULE;
  3.  cdev->ops = fops;

问题的根本就是,一个用了cdev_init,一个没有。比较一下和cdev_alloc和cdev_init的代码:

  1. struct cdev *cdev_alloc(void)
  2. {
  3.     struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
  4.     if (p) {
  5.         INIT_LIST_HEAD(&p->list);
  6.         kobject_init(&p->kobj, &ktype_cdev_dynamic);
  7.     }
  8.     return p;
  9. }

 

  1. void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  2. {
  3.     memset(cdev, 0, sizeof *cdev);
  4.     INIT_LIST_HEAD(&cdev->list);
  5.     kobject_init(&cdev->kobj, &ktype_cdev_default);
  6.     cdev->ops = fops;
  7. }

 

注意到红色部分,这里需要了解一下内核kobject的工作机制,它们像容器一样对内核中的设备进行统一的管理,可以参考网络中的相关文章,这里不做详解。

注意到kobject_init的第二个参数struct kobj_type *ktype,其中的release函数会在cdev_del时被调用:

  1. static struct kobj_type ktype_cdev_default = {
  2.     .release    = cdev_default_release,
  3. };

  4. static struct kobj_type ktype_cdev_dynamic = {
  5.     .release    = cdev_dynamic_release,
  6. };

 

继续追踪这两个函数的实现:

  1. static void cdev_default_release(struct kobject *kobj)
  2. {
  3.     struct cdev *p = container_of(kobj, struct cdev, kobj);
  4.     cdev_purge(p);
  5. }

  6. static void cdev_dynamic_release(struct kobject *kobj)
  7. {
  8.     struct cdev *p = container_of(kobj, struct cdev, kobj);
  9.     cdev_purge(p);
  10.     kfree(p);
  11. }

注意到红色部分的kfree。回顾前文,如果使用cdev_alloc动态申请设备空间,此时释放函数被安装为cdev_dynamic_release,到这里还都没错,然后调用cdev_init,此时释放函数被替换为了cdev_default_release,最终模块卸载的时候,一段内存就这样偷偷溜走了!

你可以尝试在内核中搜索cdev_alloc,然后看看使用它们的地方有没有接着使用cdev_init,很遗憾,没有,如果有,那么快点报告该驱动的维护者吧,他应该看看LDD3了! ^^

世界又安静了!

阅读(468) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~