#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
struct device_test {
struct cdev cdev_test;
dev_t dev_num;
struct class *class;
struct device *device;
int major;
int minor;
char kbuf[32];
};
struct device_test dev1;
static int cdev_test_open(struct inode *inode, struct file *file)
{
file->private_data = &dev1;
printk("This is cdev_test_open\n");
return 0;
}
static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
struct device_test *test_dev = (struct device_test *)file->private_data;
if (size > sizeof(test_dev->kbuf) - 1) {
size = sizeof(test_dev->kbuf) - 1;
}
if (copy_from_user(test_dev->kbuf, buf, size) != 0) {
printk("copy_from_user error\n");
return -EFAULT;
}
test_dev->kbuf[size] = '\0'; // Ensure null termination
printk("copy_from_user test_dev->kbuf %s\n", test_dev->kbuf);
return size;
}
static ssize_t cdev_test_read(struct file *file, char __user *buf, size_t size, loff_t *off)
{
struct device_test *test_dev = (struct device_test *)file->private_data;
ssize_t len = strlen(test_dev->kbuf);
if (len > size) {
len = size;
}
if (copy_to_user(buf, test_dev->kbuf, len) != 0) {
printk("copy_to_user error\n");
return -EFAULT;
}
printk("This is cdev_test_read\n");
return len;
}
static int cdev_test_release(struct inode *inode, struct file *file)
{
printk("This is cdev_test_release\n");
return 0;
}
static struct file_operations cdev_test_ops = {
.owner = THIS_MODULE,
.open = cdev_test_open,
.read = cdev_test_read,
.write = cdev_test_write,
.release = cdev_test_release,
};
static int __init modulecdev_init(void)
{
int ret;
ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name");
if (ret < 0) {
printk("alloc_chrdev_region error\n");
return ret;
}
printk("alloc_chrdev_region ok\n");
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk("major is %d\n", dev1.major);
printk("minor is %d\n", dev1.minor);
cdev_init(&dev1.cdev_test, &cdev_test_ops);
dev1.cdev_test.owner = THIS_MODULE;
ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
if (ret < 0) {
printk("cdev_add error\n");
unregister_chrdev_region(dev1.dev_num, 1);
return ret;
}
dev1.class = class_create(THIS_MODULE, "test_class");
if (IS_ERR(dev1.class)) {
printk("class_create error\n");
cdev_del(&dev1.cdev_test);
unregister_chrdev_region(dev1.dev_num, 1);
return PTR_ERR(dev1.class);
}
dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test_device");
if (IS_ERR(dev1.device)) {
printk("device_create error\n");
class_destroy(dev1.class);
cdev_del(&dev1.cdev_test);
unregister_chrdev_region(dev1.dev_num, 1);
return PTR_ERR(dev1.device);
}
return 0;
}
static void __exit modulecdev_exit(void)
{
device_destroy(dev1.class, dev1.dev_num);
class_destroy(dev1.class);
cdev_del(&dev1.cdev_test);
unregister_chrdev_region(dev1.dev_num, 1);
printk("Goodbye, world\n");
}
module_init(modulecdev_init);
module_exit(modulecdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangsan");
MODULE_DESCRIPTION("A simple character device module");
MODULE_VERSION("V1.0");
关于为什么在Linux驱动模块中,test_dev在cdev_test_read和cdev_test_write函数中都可以使用,是否是一个全局变量。需要结合代码来解释这个问题。
首先,在代码中,有一个结构体struct device_test的实例dev1被定义为全局变量。不过,在读写函数中,test_dev实际上是从file->private_data获取的,这应该是在open函数中设置的。用户可能混淆了全局变量和通过private_data传递的指针。
然后,示例代码使用了全局变量data,这可能让我们认为test_dev也是全局的。但实际上,代码中,test_dev是通过file->private_data来传递的,而不是全局变量。需要解释清楚这个机制,以及为什么不同的函数可以访问同一个结构体实例。
需要指出,在open函数中,file->private_data被设置为&dev1,这样在read和write函数中,可以通过file->private_data获取到同一个结构体指针。而dev1本身是全局变量,但test_dev在读写函数中是局部指针变量,指向这个全局变量。
可能误解test_dev的作用域,认为它在不同函数中重复定义,但实际上它是通过private_data传递的指针。需要分点说明:1. private_data的作用;2. dev1作为全局变量的角色;3. 读写函数如何通过private_data访问同一实例。同时,引用搜索结果中的例子,说明其他驱动也可能使用类似方法,比如通过全局缓冲区data,但实现方式不同。
在Linux驱动开发中,`test_dev`在`cdev_test_read`和`cdev_test_write`中的可访问性并非因为全局变量,而是通过**文件操作上下文传递机制**实现的。以下是具体原理分点说明:
---
### 1. **`private_data`字段的桥梁作用**
在`cdev_test_open`函数中,通过`file->private_data = &dev1`将全局变量`dev1`的地址绑定到文件指针的私有数据字段。
• 所有后续的文件操作函数(如`read`/`write`)都可以通过`file->private_data`获取到同一实例的指针。
• 这种设计避免了全局变量的直接暴露,同时实现了跨函数的数据共享。
---
### 2. **全局变量`dev1`的定位**
代码中定义的`struct device_test dev1`确实是一个全局变量:
• 全局变量的作用域覆盖整个驱动模块,但直接操作全局变量会增加并发风险(如竞态条件)。
• 通过`private_data`间接访问,实际上是驱动开发中推荐的**数据封装**方式,既能共享设备实例,又通过文件指针隔离了不同进程的访问。
---
### 3. **读写函数中的`test_dev`本质**
在`cdev_test_read`和`cdev_test_write`中:
```c
struct device_test *test_dev = (struct device_test *)file->private_data;
```
• 此处的`test_dev`是**局部指针变量**,仅指向全局变量`dev1`的内存地址。
• 所有读写操作最终都作用于`dev1`这一全局实例,但通过指针间接操作提高了安全性。
---
### 对比搜索结果的实现
搜索结果中的示例驱动通过全局变量`data`直接操作缓冲区,而当前代码则通过`private_data`传递设备实例:
• **相同点**:均需共享设备状态或缓冲区。
• **差异点**:当前代码通过`private_data`实现了更规范的上下文传递,避免了全局变量的滥用。
---
### 总结
`test_dev`在读写函数中的可访问性依赖于:
1. 全局变量`dev1`作为设备实例的存储载体;
2. `file->private_data`实现跨函数指针传递;
3. 局部指针变量`test_dev`的解引用操作。
这种设计既符合Linux驱动开发规范,又兼顾了数据共享的安全性。