创建设备节点问题
来源:互联网 发布:js获取自定义属性data 编辑:程序博客网 时间:2024/05/21 10:25
在原先的文章中将了怎样创建一个设备节点供读写使用,Linux用户空间与内核空间交互方法,现在回过头去看,发现当时很多代码写法都有问题,在此作为一个反面教材来讲一讲。
原先代码
static int sample_init(void) { /* 初始化 sample_dev 结构体 */ sample_dev = kzalloc(sizeof(struct sample), GFP_KERNEL); if (!sample_dev) return ERR_PTR(-ENOMEM); /* 注册字符设备,主设备号设置为0表示由系统自动分配主设备号 */ sample_dev->sample_major = register_chrdev(0, "sample", &sample_fops); /* 创建sample_class类 */ sample_class = class_create(THIS_MODULE, "sample_class"); /* 在sample_class类下创建sample_dev设备,这之后可以生成 /dev/sample_dev 的设备节点 */ sample_device = device_create(sample_class, NULL, MKDEV(sample_dev->sample_major, 0), NULL, "sample_dev"); init_waitqueue_head(&sample_dev->write_queue); init_waitqueue_head(&sample_dev->read_queue); sample_dev->completed_in_req = 1; // 一上来标记可以写 return 0;}static void sample_exit(void) { unregister_chrdev(sample_dev->sample_major, "sample"); device_unregister(sample_device); class_destroy(sample_class);}
ERR_PTR用法错误
在这里如果为 sample_dev
申请内存失败,直接返回 –ENOMEM
即可,但是这里是返回 ERR_PTR(-ENOMEM)
。ERR_PTR
是将一个error number转换为void *
的指针。因为此处是 sample_init()
返回值是int型的,所以不能用 ERR_PTR
进行转换。ERR_PTR
的定义如下:
static inline void *ERR_PTR(long error) { return (void *) error; }
未对 register_chrdev()
返回值做判断
register_chrdev()
会调用 __register_chrdev()
函数,该函数原型如下:
/** * __register_chrdev() - create and register a cdev occupying a range of minors * @major: major device number or 0 for dynamic allocation * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: name of this range of devices * @fops: file operations associated with this devices * * If @major == 0 this functions will dynamically allocate a major and return * its number. * * If @major > 0 this function will attempt to reserve a device with the given * major number and will return zero on success. * * Returns a -ve errno on failure. * * The name of this device has nothing to do with the name of the device in * /dev. It only helps to keep track of the different owners of devices. If * your module name has only one type of devices it's ok to use e.g. the name * of the module here. */int __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops)
之前碰到一个问题,热插拔设备每次创建节点供应用层读写使用,当拔插多次之后,调用申请字符设备的设备号总是失败,后面发现由于在拔掉的时候已申请的设备号没有unregister,导致原先申请的设备号没有释放掉,当申请的设备号到达Kernel指定的最大字符设备数后,后面要申请设备号总是会失败。
字符设备个数限制由宏 CHRDEV_MAJOR_HASH_SIZE
来决定。定义如下:
// fs/char_dev.c#define CHRDEV_MAJOR_HASH_SIZE 255static struct char_device_struct { struct char_device_struct *next; unsigned int major; unsigned int baseminor; int minorct; char name[64]; struct cdev *cdev; /* will die */} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
另外,如果申请设备号失败,必须释放之前申请到的资源,这里必须释放 sample_dev
,否则,久而久之,有可能造成内存泄露。修改后的代码为:
/* 注册字符设备,主设备号设置为0表示由系统自动分配主设备号 */ret = register_chrdev(0, "sample", &sample_fops);if (ret < 0) { kfree(sample_dev); return ret;}ret = sample_dev->sample_major;
未对 device_create() 返回值做判断
如果device_create()
创建设备失败,那么必须先unregister前面申请的major,然后在kfree
申请的内存。否则,如果没释放设备号,那么当达到CHRDEV_MAJOR_HASH_SIZE
,其他驱动再次申请设备号肯定会失败。修改后如下:
/* 在sample_class类下创建sample_dev设备,这之后可以生成 /dev/sample_dev 的设备节点 */sample_device = device_create(sample_class, NULL, MKDEV(sample_dev->sample_major, 0), NULL, "sample_dev");if (IS_ERR(sample_device)) {ret = PTR_ERR(sample_device);class_destroy(sample_class); unregister_chrdev(sample_dev->sample_major, "sample"); kfree(sample_dev); return ret;}
IS_ERR
、ERR_PTR
、PTR_ERR
的定义在 include/linux/err.h
文件下。
#include <linux/compiler.h> #include <asm/errno.h> /* * Kernel pointers have redundant information, so we can use a * scheme where we can return either an error code or a dentry * pointer with the same return value. * * This should be a per-architecture thing, to allow different * error and pointer decisions. */ #define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L) static inline void *ERR_PTR(long error) { return (void *) error; } static inline long PTR_ERR(const void *ptr) { return (long) ptr; } static inline long IS_ERR(const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } #endif /* _LINUX_ERR_H */
释放资源顺序应与申请时相反
在 sample_init()
中,申请的资源顺序如下: kazlloc
-> register_chrdev
-> class_create
-> device_create
那么,在 sample_exit()
中,释放资源的顺序应该如下: device_unregister
-> class_destroy
-> unregister_chrdev
-> kfree
因此,最终的修改应为:
static int sample_init(void) { int ret; /* 初始化 sample_dev 结构体 */ sample_dev = kzalloc(sizeof(struct sample), GFP_KERNEL); if (!sample_dev) return -ENOMEM; /* 注册字符设备,主设备号设置为0表示由系统自动分配主设备号 */ ret = register_chrdev(0, "sample", &sample_fops); if (ret < 0) { kfree(sample_dev); return ret; } ret = sample_dev->sample_major; /* 创建sample_class类 */ sample_class = class_create(THIS_MODULE, "sample_class"); /* 在sample_class类下创建sample_dev设备,这之后可以生成 /dev/sample_dev 的设备节点 */ sample_device = device_create(sample_class, NULL, MKDEV(sample_dev->sample_major, 0), NULL, "sample_dev"); if (IS_ERR(sample_device)) { ret = PTR_ERR(sample_device); class_destroy(sample_class); unregister_chrdev(sample_dev->sample_major, "sample"); kfree(sample_dev); return ret; } init_waitqueue_head(&sample_dev->write_queue); init_waitqueue_head(&sample_dev->read_queue); sample_dev->completed_in_req = 1; // 一上来标记可以写 return 0;}static void sample_exit(void) { device_unregister(sample_device); class_destroy(sample_class); unregister_chrdev(sample_dev->sample_major, "sample"); kfree(sample_dev);}
- 创建设备节点问题
- mini2440 使用mdev创建设备节点问题
- 自动创建设备节点
- 自动创建设备节点
- 自动创建设备节点
- 创建设备节点mknod
- 自动创建设备节点
- 自动创建设备节点
- 创建设备节点
- 设备节点权限问题
- 设备节点权限问题
- 《Linux设备节点创建》手动与自动创建设备节点
- 创建设备节点之mknod
- android中创建设备节点
- 自动创建设备文件节点
- 自动创建设备节点 device_create
- Linux自动创建设备节点
- 设备驱动-----自动创建节点
- 文件的基本操作总结
- HTML第二节点学习笔记
- 推荐系统(Recommender Systems)
- POJ-1155-TELE
- Objective-C (iOS)实现TCP反向代理(Port forward隧道)
- 创建设备节点问题
- 第二节点习题
- codeforces 96A Football
- 如何使用spring的jdbcTemplate的in
- 【一图看懂】计算机视觉识别简史:从 AlexNet、ResNet 到 Mask RCNN
- HQL数据查询
- 深度学习框架:TensorFlow(人生若只如初见)
- Self Dividing Numbers
- 解决wireshark检测不到网卡的问题(Windows)