例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码3_内核空间与用户空间的数据拷贝copy_to_user
在mydevice-auto.c源码的基础上进行添加,重命名为copy_to_user.c
(一)添加头文件
#include <linux/uaccess.h> |
(二)定义变量
#define DEVICE_NAME "copy_to_user" // 设备名称
#define BUFFER_SIZE 1024 static char kernel_buffer[BUFFER_SIZE] = "Hello, User! This is kernel_buffer!"; |
(三)在device_read()函数添加拷贝操作,用户空间使用read函数时,会进入此函数。
static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
{ ssize_t bytes_read = 0; // 在这里处理设备读取的操作 printk(KERN_INFO "This is device_read.n"); if (*offset >= BUFFER_SIZE) return 0; if (*offset + length > BUFFER_SIZE) length = BUFFER_SIZE - *offset; if (copy_to_user(buffer, kernel_buffer + *offset, length)) return -EFAULT; *offset += length; bytes_read = length; return bytes_read; } |
offset参数表示当前文件位置的偏移量。它用于跟踪读取或写入操作在文件中的位置。
对于device_read函数来说,offset指向一个loff_t类型的变量,表示当前读取操作的文件偏移量。在读取数据之前,可以通过检查offset来确定从文件的哪个位置开始读取数据。读取完成后,可以通过更新offset来指示下一次读取操作应该从哪个位置开始。
在使用copy_to_user()函数将数据复制到用户空间之前,通常会更新offset的值,以确保下一次读取操作从正确的位置开始。这样可以实现逐步读取大文件或在多次读取中定位到不同位置的功能。
完整的copy_to_user.c示例源码
#include <linux/module.h> // 包含模块相关函数的头文件
#include <linux/fs.h> // 包含文件系统相关函数的头文件 #include <linux/uaccess.h> // 包含用户空间数据访问函数的头文件 #include <linux/cdev.h> //包含字符设备头文件 #include <linux/device.h> #include <linux/uaccess.h> #define DEVICE_NAME "copy_to_user" // 设备名称 static dev_t dev_num; //分配的设备号 struct cdev my_cdev; //字符设备指针 int major; //主设备号 int minor; //次设备号 static struct class *my_class; static struct device *my_device; #define BUFFER_SIZE 1024 static char kernel_buffer[BUFFER_SIZE] = "Hello, User! This is kernel_buffer!"; static int device_open(struct inode *inode, struct file *file) { // 在这里处理设备打开的操作 printk(KERN_INFO "This is device_open.n"); return 0; } static int device_release(struct inode *inode, struct file *file) { // 在这里处理设备关闭的操作 printk(KERN_INFO "This is device_release.n"); return 0; } static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) { ssize_t bytes_read = 0; // 在这里处理设备读取的操作 printk(KERN_INFO "This is device_read.n"); if (*offset >= BUFFER_SIZE) return 0; if (*offset + length > BUFFER_SIZE) length = BUFFER_SIZE - *offset; if (copy_to_user(buffer, kernel_buffer + *offset, length)) return -EFAULT; *offset += length; bytes_read = length; return bytes_read; } static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) { // 在这里处理设备写入的操作 printk(KERN_INFO "This is device_write.n"); return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init mydevice_init(void) { int ret; // 在这里执行驱动程序的初始化操作 // 注册字符设备驱动程序 ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME); if (ret < 0) { printk(KERN_ALERT "Failed to allocate device number: %dn", ret); return ret; } major=MAJOR(dev_num); minor=MINOR(dev_num); printk(KERN_INFO "major number: %dn",major); printk(KERN_INFO "minor number: %dn",minor); my_cdev.owner = THIS_MODULE; cdev_init(&my_cdev,&fops); cdev_add(&my_cdev,dev_num,1); // 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { pr_err("Failed to create classn"); return PTR_ERR(my_class); } // 创建设备节点并关联到设备类 my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, DEVICE_NAME); if (IS_ERR(my_device)) { pr_err("Failed to create devicen"); class_destroy(my_class); return PTR_ERR(my_device); } printk(KERN_INFO "Device registered successfully.n"); return 0; } static void __exit mydevice_exit(void) { // 在这里执行驱动程序的清理操作 // 销毁设备节点 device_destroy(my_class, MKDEV(major, minor)); // 销毁设备类 class_destroy(my_class); // 删除字符设备 cdev_del(&my_cdev); // 注销字符设备驱动程序 unregister_chrdev(0, DEVICE_NAME); printk(KERN_INFO "Device unregistered.n"); } module_init(mydevice_init); module_exit(mydevice_exit); MODULE_LICENSE("GPL"); // 指定模块的许可证信息 MODULE_AUTHOR("Your Name"); // 指定模块的作者信息 MODULE_DESCRIPTION("A simple character device driver"); // 指定模块的描述信息 |
编译
复制7.3.4驱动中的Makefile文件,将其中的mydevice-auto.o修改为copy_to_user.o,效果如下:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
elf@ubuntu:~/work/test/03_内核空间与用户空间的数据拷贝/copy_to_user$ make |
将驱动模块编译成.ko并拷贝到开发板中。
编写测试应用源码copy_to_user.c
#include <stdio.h>
#include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> #include <string.h> #define DEV_NAME "/dev/copy_to_user" #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int reg; int fd = 0; char read_buffer[BUFFER_SIZE]; fd = open (DEV_NAME, O_RDWR); if (fd < 0) { perror("Open "DEV_NAME" Failed!n"); exit(1); } if (read(fd, read_buffer, BUFFER_SIZE) < 0) { printf("Failed to read from device file: %sn", DEV_NAME); close(fd); exit(1); } printf("Read from device file: %sn", read_buffer); close(fd); return 0; } |
编译应用
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
elf@ubuntu:~/work/test/03_内核空间与用户空间的数据拷贝/copy_to_user_app$ $CC copy_to_user.c -o copy_to_user |
将编译好的应用程序拷贝到开发板中。
测试
root@ELF1:~# insmod copy_to_user.ko
major number: 249 minor number: 0 Device registered successfully. root@ELF1:~# ./copy_to_user This is device_open. This is device_read. Read from device file: Hello, UseThis is device_release. r! This is kernel_buffer! root@ELF1:~# rmmod copy_to_user.ko Device unregistered. |
可以看到运行测试程序后,把内核空间中kernel_buffer[]中的数据打印了出来。