linux自动创建设备节点

刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod [OPTION]... NAME TYPE [MAJOR MINOR]命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev(mdev)

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。

struct class和class_create(…) 定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。

在3.2.0内核版本中,struct class定义在头文件include/linux/device.h中:

  1. struct class {
  2. const char *name;
  3. struct module *owner;
  4. struct class_attribute *class_attrs;
  5. struct device_attribute *dev_attrs;
  6. struct bin_attribute *dev_bin_attrs;
  7. struct kobject *dev_kobj;
  8. int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
  9. char *(*devnode)(struct device *dev, mode_t *mode);
  10. void (*class_release)(struct class *class);
  11. void (*dev_release)(struct device *dev);
  12. int (*suspend)(struct device *dev, pm_message_t state);
  13. int (*resume)(struct device *dev);
  14. const struct kobj_ns_type_operations *ns_type;
  15. const void *(*namespace)(struct device *dev);
  16. const struct dev_pm_ops *pm;
  17. struct subsys_private *p;
  18. };
  1. #define class_create(owner, name) \
  2. ({ \
  3. static struct lock_class_key __key; \
  4. __class_create(owner, name, &__key); \
  5. })
第一个参数指定类的所有者是哪个模块,第二个参数指定类名。 在class.c中,还定义了class_destroy(…)函数,用于在模块卸载时删除类。
device_create(…)定义在core.c中
  1. struct device *device_create(struct class *class, struct device *parent,
  2. dev_t devt, void *drvdata, const char *fmt, ...)
  3. {
  4. va_list vargs;
  5. struct device *dev;
  6. va_start(vargs, fmt);
  7. dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
  8. va_end(vargs);
  9. return dev;
  10. }
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。 
下面通过改写LDD3中scull源码来实现设备文件scull【0~3】的自动创建。

首先应该加上头文件 #include <linux/device.h> 
其次在scull_init_module中

  1. 626 int scull_init_module(void)
  2. 627 { 
  3. 628 +-- 7 lines: int result, i;
  4. ------------------------------------------------------------------------------
  5. 635 if (scull_major) { 
  6. 636 dev = MKDEV(scull_major, scull_minor);
  7. 637 result = register_chrdev_region(dev, scull_nr_devs, "scull");
  8. 638 +-- 8 lines: } else {
  9. ------------------------------------------------------------------------------
  10. 646 }
  11. 647 
  12. 648 +-- 4 lines: allocate the devices -- we can't have them static, as the number
  13. ------------------------------------------------------------------------------
  14. 652 scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
  15. 653 +-- 6 lines: if (!scull_devices) {
  16. ------------------------------------------------------------------------------
  17. 659 //create the device class
  18. 660 my_class = class_create(THIS_MODULE,"scull"); //类名为 scull
  19. 661 if(IS_ERR(my_class)) 
  20. 662 {
  21. 663 printk("Err: failed in creating class.\n");
  22. 664 return -1; 
  23. 665 }
然后在scull_setup_cdev函数中

  1. 605 static void scull_setup_cdev(struct scull_dev *dev, int index)
  2. 606 {
  3. 607 int err, devno = MKDEV(scull_major, scull_minor + index);
  4. 608 
  5. 609 cdev_init(&dev->cdev, &scull_fops);
  6. 610 dev->cdev.owner = THIS_MODULE;
  7. 611 dev->cdev.ops = &scull_fops;
  8. 612 err = cdev_add (&dev->cdev, devno, 1);
  9. 613 /* Fail gracefully if need be */
  10. 614 if (err)
  11. 615 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
  12. 616 //create the device file
  13. 617 int result = device_create(my_class,NULL,devno,NULL,"scull%d",index); //设备名为scull[0~3]
  14. 618 if (result<0) 
  15. 619 {
  16. 620 printk (KERN_WARNING "hello: can't get major number %d\n", scull_major);
  17. 621 return result;
  18. 622 }
  19. 623 }
这样在加载该驱动时,就会自动生成相应的设备文件。

在移除模块时,要相应的删除自动创建的设备和类

  1. 591 void scull_cleanup_module(void)
  2. 592 {
  3. 593 int i;
  4. 594 dev_t devno = MKDEV(scull_major, scull_minor);
  5. 595 
  6. 596 /* Get rid of our char dev entries */
  7. 597 if (scull_devices) {
  8. 598 for (= 0; i < scull_nr_devs; i++) {
  9. 599 scull_trim(scull_devices + i);
  10. 600 cdev_del(&scull_devices[i].cdev);
  11. 601 device_destroy(my_class,MKDEV(scull_major, scull_minor+i));
  12. 602 }
  13. 603 kfree(scull_devices);
  14. 604 }
  15. 605 
  16. 606 #ifdef SCULL_DEBUG /* use proc only if debugging */
  17. 607 scull_remove_proc();
  18. 608 #endif
  19. 609 
  20. 610 /* cleanup_module is never called if registering failed */
  21. 611 unregister_chrdev_region(devno, scull_nr_devs);
  22. 612 class_destroy(my_class);
  23. 613 
  24. 614 /* and call the cleanup functions for friend devices */
  25. 615 scull_p_cleanup();
  26. 616 scull_access_cleanup();
  27. 617 
  28. 618 }


这里涉及到一个问题值得思考,在手动创建设备文件的时候,指明了设备文件的类型,但是自动创建的时候没有指定是字符设备还是块设备,这个内核是通过什么样地机制来做具体判断的值得思考一下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值