使用alloc_chrdev_region后/dev无字符驱动设备条目问题
字符驱动使用alloc_chrdev_region后/dev无设备条目的问题
我正在开发一个简单的Linux字符驱动,知道获取主设备号有两种常见方式:alloc_chrdev_region(包含register_chrdev_region)和register_chrdev。最开始用register_chrdev的时候,不仅能成功获取主设备号,还能通过class_create和device_create自动在/dev目录下生成对应的设备条目。但换成alloc_chrdev_region配合cdev_init、cdev_add的新接口后,/dev里就没自动生成设备文件了,手动用mknod创建后驱动倒是能正常工作。以下是我的相关代码:
#include<linux/module.h> #include<linux/init.h> #include<linux/fs.h> #include<linux/device.h> #include<linux/kernel.h> #include<linux/slab.h> #include<linux/uaccess.h> #include<linux/stat.h> #include<linux/cdev.h> #include <linux/version.h> #include <linux/types.h> #include <linux/kdev_t.h> #define DEVICE_NAME "myCharDevice" #define MODULE_NAME "myCharDriver" #define CLASS_NAME "myCharClass" MODULE_LICENSE("GPL"); MODULE_AUTHOR("YASH BHATT"); MODULE_VERSION(".01"); static char *bufferMemory; static int bufferPointer; static int bufferSize = 15; static dev_t myChrDevid; static struct cdev *myChrDevCdev; static struct class *pmyCharClass; static struct device *pmyCharDevice; int majorNumber = 0; static int charDriverOpen(struct inode *inodep, struct file *filep); static int charDriverClose(struct inode *inodep, struct file *filep); static ssize_t charDriverWrite(struct file *filep, const char *buffer, size_t len, loff_t *offset); static ssize_t charDriverRead(struct file *filep, char *buffer, size_t len, loff_t *offset); static int charDriverEntry(void); static void charDriverExit(void); static ssize_t attrShowData(struct device*, struct device_attribute*, char*); static ssize_t attrStoreData(struct device*, struct device_attribute*, const char*, size_t); static ssize_t attrShowBuffer(struct device*, struct device_attribute*, char*); static ssize_t attrStoreBuffer(struct device*, struct device_attribute*, const char*, size_t); /* The following function is called when the file placed on the sysfs is accessed for read*/ static ssize_t attrShowData(struct device* pDev, struct device_attribute* attr, char* buffer) { printk(KERN_INFO "MESG: The data has been accessed through the entry in sysfs\n"); if (bufferPointer == 0) { printk(KERN_WARNING "There is no data to read from buffer!\n"); return -1; } strncpy(buffer, bufferMemory, bufferPointer); /* Note : Here we can directly use strncpy because we are already in kernel space and do not need to translate address*/ return bufferPointer; } static ssize_t attrStoreData(struct device* pDev, struct device_attribute* attr, const char* buffer, size_t length) { printk(KERN_INFO "Writing to attribute\n"); bufferPointer = length; strncpy(bufferMemory, buffer, length); return length; } static ssize_t attrShowBuffer(struct device* pDev, struct device_attribute* attr, char* buffer) { int counter; int temp = bufferSize; char bufferSizeArray[4] = {0}; counter = 3; //printk(KERN_INFO "Buffer = %d\n",bufferSize % 10); do { bufferSizeArray[counter] = '0' + (bufferSize % 10); //printk(KERN_INFO "Character at %d is : %c\n",counter,bufferSizeArray[counter]); bufferSize /= 10; counter--; } while(counter != -1); strncpy(buffer, bufferSizeArray, 4); bufferSize = temp; /* Note : Here we can directly use strncpy because we are already in kernel space and do not need to translate address*/ return 4; } static ssize_t attrStoreBuffer(struct device* pDev, struct device_attribute* attr, const char* buffer, size_t length) { int counter; bufferPointer = length; //printk(KERN_INFO "Length : %d With first char %c\n",length,buffer[0]); bufferSize = 0; for (counter = 0; counter < length-1 ; counter++) { bufferSize = (bufferSize * 10) + (buffer[counter] - '0') ; } //printk(KERN_INFO "Buffer size new : %d\n",bufferSize); return length; } /* These macros converts the function in to instances dev_attr_<_name>*/ /* Defination of the macro is as follows : DEVICE_ATTR(_name, _mode, _show, _store) */ /* Note the actual implementation of the macro makes an entry in the struct device_attribute. This macro does that for us */ static DEVICE_ATTR(ShowData, S_IRWXU, attrShowData, attrStoreData); // S_IRUSR gives read access to the user static DEVICE_ATTR(Buffer, S_IRWXU, attrShowBuffer, attrStoreBuffer); // S_IRUSR gives read access to the user static struct file_operations fops = { .open = charDriverOpen, .release = charDriverClose, .read = charDriverRead, .write = charDriverWrite, }; static int __init charDriverEntry() { int returnValue; //majorNumber = register_chrdev(0, DEVICE_NAME, &fops); returnValue = alloc_chrdev_region(&myChrDevid, 0, 1, DEVICE_NAME); /* This function takes 4 arguments - dev_t address, sta
问题原因
register_chrdev是较旧的字符驱动注册接口,它内部会自动完成一些与设备节点创建相关的关联工作;而alloc_chrdev_region + cdev_init + cdev_add是更灵活的新接口,它只负责设备号分配和字符设备对象的注册,不会自动触发/dev设备节点的创建——你需要手动确保class和device的创建流程完整且正确。
从你的代码片段来看,可能存在以下几个问题:
- 代码被截断,不确定你是否在
alloc_chrdev_region之后正确初始化并添加了cdev对象 - 可能漏掉了
class_create和device_create的调用,或者调用时参数错误(比如没有正确传递分配到的dev_t)
解决步骤
按照新接口的标准流程,你需要在驱动入口函数中补充以下步骤:
初始化并添加cdev对象
在alloc_chrdev_region成功后,添加:// 分配cdev内存 myChrDevCdev = cdev_alloc(); if (!myChrDevCdev) { printk(KERN_ERR "Failed to allocate cdev\n"); unregister_chrdev_region(myChrDevid, 1); return -ENOMEM; } // 绑定file_operations到cdev cdev_init(myChrDevCdev, &fops); // 将cdev添加到内核 returnValue = cdev_add(myChrDevCdev, myChrDevid, 1); if (returnValue < 0) { printk(KERN_ERR "Failed to add cdev\n"); cdev_del(myChrDevCdev); unregister_chrdev_region(myChrDevid, 1); return returnValue; }创建class和device以生成/dev节点
继续在入口函数中添加:// 创建设备类 pmyCharClass = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(pmyCharClass)) { printk(KERN_ERR "Failed to create class\n"); cdev_del(myChrDevCdev); unregister_chrdev_region(myChrDevid, 1); return PTR_ERR(pmyCharClass); } // 创建设备节点,这一步会自动在/dev下生成对应文件 pmyCharDevice = device_create(pmyCharClass, NULL, myChrDevid, NULL, DEVICE_NAME); if (IS_ERR(pmyCharDevice)) { printk(KERN_ERR "Failed to create device\n"); class_destroy(pmyCharClass); cdev_del(myChrDevCdev); unregister_chrdev_region(myChrDevid, 1); return PTR_ERR(pmyCharDevice); }在出口函数中清理资源
确保在驱动卸载时释放所有资源:static void __exit charDriverExit(void) { // 销毁设备节点 device_destroy(pmyCharClass, myChrDevid); // 销毁设备类 class_destroy(pmyCharClass); // 删除cdev对象 cdev_del(myChrDevCdev); // 释放设备号 unregister_chrdev_region(myChrDevid, 1); // 释放buffer内存(如果之前用kmalloc分配的话) kfree(bufferMemory); printk(KERN_INFO "Char driver unloaded successfully\n"); } module_init(charDriverEntry); module_exit(charDriverExit);
关键注意点
- 每一步操作都要检查返回值,出错时要按顺序回滚已分配的资源,避免内存泄漏或资源残留
device_create的第三个参数必须是你通过alloc_chrdev_region获取到的完整dev_t(包含主、次设备号),这样udev才能正确识别并生成/dev节点- 确保你的驱动模块有足够的权限(一般需要root权限加载),否则
class_create和device_create可能会失败
内容的提问来源于stack exchange,提问作者yashC
相关产品推荐
相关产品推荐

