我写了一个这样的演示设备驱动程序:
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#define CDEV_NAME "mycdev"
struct my_char_dev {
unsigned char *ptr;
struct semaphore sem;
struct cdev cdev;
};
struct my_char_dev *mycdev;
int my_char_open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
return 0;
}
int my_char_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
return 0;
}
ssize_t my_char_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
return 0;
}
ssize_t my_char_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
{
printk(KERN_INFO "%s\n", __FUNCTION__);
return 0;
}
struct file_operations my_char_fops = {
.owner = THIS_MODULE,
.open = my_char_open,
.release = my_char_release,
.read = my_char_read,
.write = my_char_write,
};
static int __init my_char_init(void)
{
mycdev = kmalloc(sizeof(struct my_char_dev), GFP_KERNEL);
if (!mycdev) {
printk(KERN_INFO "alloc space for device failed\n");
return -ENOMEM;
}
if (!alloc_chrdev_region(&mycdev->cdev.dev, 0, 1, CDEV_NAME))
printk(KERN_INFO "%s alloc region success\n", CDEV_NAME);
else {
printk(KERN_INFO "%s alloc region failed\n", CDEV_NAME);
return -ENOMEM;
}
cdev_init(&mycdev->cdev, &my_char_fops);
if (!cdev_add(&mycdev->cdev, mycdev->cdev.dev, 1))
printk(KERN_INFO "%s add device success\n", CDEV_NAME);
else {
printk(KERN_INFO "%s add device failed\n", CDEV_NAME);
unregister_chrdev_region(mycdev->cdev.dev, 1);
return -ENOMEM;
}
return 0;
}
void __exit my_char_exit(void)
{
cdev_del(&mycdev->cdev);
unregister_chrdev_region(mycdev->cdev.dev, 1);
kfree(mycdev);
printk(KERN_INFO "%s\n", __FUNCTION__);
}
module_init(my_char_init);
module_exit(my_char_exit);
MODULE_AUTHOR("xdd");
MODULE_LICENSE("GPL");
字符串
我在内核中插入了模块,当我cat /proc/devices
时,我可以找到一个mycdev
,我也通过mknod
成功地在/dev/mycdev
下创建了一个设备节点,但是当我试图操作我创建的/dev/mycdev
时,我得到了/dev/mycdev0: No such device or address
。
这就是dmesg的样子:
$ dmesg
[ 235.845810] test: loading out-of-tree module taints kernel.
[ 235.845836] test: module verification failed: signature and/or required key missing - tainting kernel
[ 235.846238] mycdev alloc region success
[ 235.846239] mycdev add device success
型
这是proc/devices
中的设备:
$ cat /proc/devices | grep myc
239 mycdev
型
这是我使用mknod的方式:
$ sudo mknod /dev/mycdev1 c 239 1
型
这是我创建的设备:
$ ls -l /dev/mycdev1
crw-r--r-- 1 root root 239, 1 Nov 14 00:57 /dev/mycdev1
型
我尝试使用cat
来操作我创建的文件:
$ cat /dev/mycdev1
cat: /dev/mycdev1: No such device or address
型
当我删除内核模块使用rmmod
,该模块删除成功,但当我检查/proc/devices
,有一个mycdev
仍然活着:
$ sudo rmmod mycdev_module
$ cat /proc/devices | grep myc
239 mycdev
型
我不知道为什么会这样。
我想知道为什么这个过程会失败。我应该做些什么来纠正我的程序。
1条答案
按热度按时间np8igboo1#
分配的角色设备区域的基本设备号(
dev_t
值)通过以下调用存储在mycdev->cdev.dev
中:字符串
之后,
mycdev->cdev.dev
将通过此调用设置为0:型
在下面对
cdev_add()
的调用中,mycdev->cdev.dev
为0:型
该调用将
mycdev->cdev.dev
设置为第二个参数的值,该值已经是其当前值0。尽管主要设备号0是为“未命名设备”保留的,但这在某种程度上成功了,但显然这不是预期的。
在模块exit函数中,以及在模块int函数中的错误清理代码中,在以下对
unregister_chrdev_region()
的调用中,mycdev->cdev.dev
仍然为0:型
这解释了为什么
alloc_chrdev_region()
注册的角色设备区域在模块退出后仍然注册。这个问题可以通过在调用
alloc_chrdev_region()
之前移动对cdev_init()
的调用来解决。但是,我认为最好将dev_t
的值存储在一个单独的变量中,以将字符设备区域的注册与struct cdev
值的操作解耦。例如,此文件级变量可用于存储已注册字符设备编号区域的基址:
型
然后将
alloc_chrdev_region()
调用更改为:型
然后将
cdev_add()
调用更改为:型
并将两个
unregister_chrdev()
调用(在模块init错误清理代码和模块exit函数中)更改为:型
如果对
alloc_chrdev_region()
或cdev_add()
的调用失败,my_char_init()
中的错误清理代码会泄漏内存。在这些情况下,mycdev
指向的已分配内存永远不会被释放。在这些情况下,该函数也会返回-ENOMEM
,但最好返回失败函数返回的错误号。通常的做法是使用
goto some_label;
以与分配相反的顺序进行清理,如下所示:型
上述版本的函数报告已分配字符设备号区域的基本主设备号和次设备号。主设备号将动态分配;次设备号将为0,由调用
alloc_chrdev_region()
的第二个参数设置。OP使用
mknod
命令创建一个字符专用文件,使用/proc/devices
文件中报告的主设备号和次设备号1。即使驱动程序模块按预期工作,该文件也不会对应于注册的字符设备,因为驱动程序模块仅保留次设备号0。