字符设备驱动开发基础—从驱动中进行读写操作

news/2024/11/15 0:48:42/

往期回顾:

字符设备驱动开发基础—静态/动态注册设备号,使用cdev注册驱动-CSDN博客

字符设备驱动基础—sys文件系统,udev介绍,驱动模块在内核空间注册设备-CSDN博客

本文主要介绍的是 如何通过驱动进行设备的读写操作

文章目录

    • 内核空间与用户空间进行数据交互
    • 驱动示例代码
      • 关键解析
    • 用户程序示例代码
    • 实现效果

内核空间与用户空间进行数据交互

内核空间:操作系统内核运行的空间,拥有最高的权限,可以直接访问硬件资源和管理系统资源。

用户空间:用户应用程序运行的空间,权限受限,无法直接访问硬件资源,必须通过系统调用与内核交互。

交互方式:

  • 系统调用(System Call):用户空间的应用程序通过系统调用请求内核执行某些操作,比如文件操作、进程管理、内存分配等。系统调用是用户空间进入内核空间的主要途径。
    • 例子:open(), read(), write(), fork()

系统调用

  • 定义:系统调用是用户程序与操作系统内核进行交互的接口,通过系统调用,用户程序可以请求内核执行特定的服务。
  • 过程:
    1. 用户程序发起系统调用请求。
    2. 处理器从用户模式切换到内核模式。
    3. 内核执行相应的服务程序。
    4. 内核模式切换回用户模式,返回结果给用户程序。

调用框架图:

注:本文只是探讨数据交互过程,没有涉及到操作寄存器,控制硬件,只是涉及到用户空间与内核空间的数据交互

在这里插入图片描述

驱动示例代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>#define DEVICE_NAME "my_char_device"#define BUF_LEN 80
static char message[BUF_LEN];
static short message_len;static int major_number;
static struct class *my_class = NULL;
static struct device *my_device = NULL;
static struct cdev mydev; // 声明 cdev 结构体static int device_open(struct inode *inode, struct file *file) {printk(KERN_INFO "Device opened\n");return 0;
}static int device_release(struct inode *inode, struct file *file) {printk(KERN_INFO "Device closed\n");return 0;
}static ssize_t device_read(struct file *file, char __user *user_buffer, size_t len, loff_t *offset) {int bytes_to_copy = message_len - *offset;if (bytes_to_copy < 0) bytes_to_copy = 0;if (bytes_to_copy > len) bytes_to_copy = len;if (bytes_to_copy == 0) return 0;if (copy_to_user(user_buffer, message + *offset, bytes_to_copy) != 0) {return -EFAULT;}*offset += bytes_to_copy;printk(KERN_INFO "Sent %d characters to the user\n", bytes_to_copy);return bytes_to_copy;
}static ssize_t device_write(struct file *file, const char __user *user_buffer, size_t len, loff_t *offset) {int bytes_to_copy = len;if (bytes_to_copy > BUF_LEN - 1) bytes_to_copy = BUF_LEN - 1;if (copy_from_user(message, user_buffer, bytes_to_copy) != 0) {return -EFAULT;}message[bytes_to_copy] = '\0';message_len = bytes_to_copy;printk(KERN_INFO "Received %d characters from the user\n", bytes_to_copy);return bytes_to_copy;
}static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,
};static int __init test_init(void)
{int retval;dev_t dev;printk(KERN_INFO "module init success\n");// 1. 动态分配主次设备号retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);if (retval < 0){printk(KERN_ERR "Failed to allocate major number\n");goto fail_alloc_chrdev_region;}major_number = MAJOR(dev);printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev));// 2. 初始化 cdev 结构体并添加到内核cdev_init(&mydev, &fops);retval = cdev_add(&mydev, dev, 1);if (retval < 0){printk(KERN_ERR "Failed to add cdev\n");goto fail_cdev_add;}// 3. 创建设备类my_class = class_create(THIS_MODULE, "my_class");if (IS_ERR(my_class)){printk(KERN_ERR "Failed to create class\n");retval = PTR_ERR(my_class);goto fail_class_create;}// 4.  申请设备,内核空间就会通知用户空间的udev 进行创建设备,驱动程序本身自己是创建不了文件的!my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME);if (IS_ERR(my_device)){printk(KERN_ERR "Failed to create device\n");retval = PTR_ERR(my_device);goto fail_device_create;}printk(KERN_INFO "my_char_device: module loaded\n");return 0;fail_device_create:class_destroy(my_class);
fail_class_create:cdev_del(&mydev);
fail_cdev_add:unregister_chrdev_region(dev, 1);
fail_alloc_chrdev_region:return retval;
}static void __exit test_exit(void)
{dev_t dev = MKDEV(major_number, 0);if (my_device)device_destroy(my_class, dev);if (my_class)class_destroy(my_class);cdev_del(&mydev);unregister_chrdev_region(dev, 1);printk(KERN_INFO "my_char_device: module unloaded\n");
}module_init(test_init);
module_exit(test_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");

关键解析

Linux理念:一切皆为文件,因此设备也是一种文件,也可以直接进行读写操作。

device_opendevice_release:设备打开和关闭时的处理函数。

device_read:从设备读取数据并复制到用户空间。

device_write:从用户空间写入数据到设备。

文件操作函数实现

static int device_open(struct inode *inode, struct file *file) {printk(KERN_INFO "Device opened\n");return 0;
}static int device_release(struct inode *inode, struct file *file) {printk(KERN_INFO "Device closed\n");return 0;
}static ssize_t device_read(struct file *file, char __user *user_buffer, size_t len, loff_t *offset) {int bytes_to_copy = message_len - *offset;if (bytes_to_copy < 0) bytes_to_copy = 0;if (bytes_to_copy > len) bytes_to_copy = len;if (bytes_to_copy == 0) return 0;if (copy_to_user(user_buffer, message + *offset, bytes_to_copy) != 0) {return -EFAULT;}*offset += bytes_to_copy;printk(KERN_INFO "Sent %d characters to the user\n", bytes_to_copy);return bytes_to_copy;
}static ssize_t device_write(struct file *file, const char __user *user_buffer, size_t len, loff_t *offset) {int bytes_to_copy = len;if (bytes_to_copy > BUF_LEN - 1) bytes_to_copy = BUF_LEN - 1;if (copy_from_user(message, user_buffer, bytes_to_copy) != 0) {return -EFAULT;}message[bytes_to_copy] = '\0';message_len = bytes_to_copy;printk(KERN_INFO "Received %d characters from the user\n", bytes_to_copy);return bytes_to_copy;
}

文件操作结构体

定义文件操作结构体,将文件操作函数指针绑定到对应的操作函数。

static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,
};

本文重点使用的函数:copy_to_usercopy_from_user

copy_to_user 用于将数据从内核空间复制到用户空间。函数原型如下:

int copy_to_user(void __user *to, const void *from, unsigned long n);
  • to:目标用户空间地址。
  • from:源内核空间地址。
  • n:要复制的字节数。

如果复制成功,返回 0;如果失败,返回未能复制的字节数。

copy_from_user 用于将数据从用户空间复制到内核空间。函数原型如下:

int copy_from_user(void *to, const void __user *from, unsigned long n);
  • to:目标内核空间地址。
  • from:源用户空间地址。
  • n:要复制的字节数。

如果复制成功,返回 0;如果失败,返回未能复制的字节数。

用户程序示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>int main(int argc, char *argv[]) {int fd;char buf[80] = {0};// 检查参数数量if (argc < 2) {fprintf(stderr, "Usage: %s <r|w> [data_to_write]\n", argv[0]);return -1;}// 打开设备节点fd = open("/dev/my_char_device", O_RDWR);if (fd < 0) {perror("open error");return -1;}if (strcmp(argv[1], "w") == 0) {// 写操作if (argc < 3) {fprintf(stderr, "Please provide data to write.\n");close(fd);return -1;}strncpy(buf, argv[2], sizeof(buf) - 1);if (write(fd, buf, sizeof(buf)) < 0) {perror("write error");close(fd);return -1;}printf("Data written: %s\n", buf);} else if (strcmp(argv[1], "r") == 0) {// 读操作if (read(fd, buf, sizeof(buf)) < 0) {perror("read error");close(fd);return -1;}printf("Data read: %s\n", buf);} else {fprintf(stderr, "Invalid operation. Use 'r' for read or 'w' for write.\n");close(fd);return -1;}// 关闭设备节点close(fd);return 0;
}

实现了简单对设备的读写测试, 主要是调用了 系统调用 的 open read write 接口

实现效果

加载编译后的模块到内核,在dev目录生成了 my_char_device 设备文件

在这里插入图片描述

运行编译好的用户程序

在这里插入图片描述

同样的也可以直接使用cat 和echo命令

在这里插入图片描述


http://www.ppmy.cn/news/1504208.html

相关文章

为什么可以使用@Resource,但不推荐使用@Autowired

DI注入的三种方式 1、属性注入 属性注入应该是我们用的最多的一种&#xff0c;即通过Autowired注解,该注解默认是按照ByType方式&#xff08;按类型&#xff09;注入Bean&#xff0c;默认情况下必须要求依赖对象必须存在&#xff0c;如果要允许null值&#xff0c;可以设置它的r…

Flink-StarRocks详解:第四部分StarRocks分区管理,数据压缩(第54天)

文章目录 前言2.3.3 管理分区2.3.3.1 增加分区2.3.3.2 删除分区2.3.3.3 恢复分区2.3.3.4 查看分区 2.3.4 设置分桶2.3.4.1 随机分桶&#xff08;自 v3.1&#xff09;2.3.4.2 哈希分桶2.3.4.2.1 优点2.3.4.2.2 如何选择分桶键2.3.4.2.3 注意事项 2.3.4.3 确定分桶数量 2.3.5 最佳…

浦语提示词工程实践

一、任务 背景问题&#xff1a;近期相关研究发现&#xff0c;LLM在对比浮点数字时表现不佳&#xff0c;经验证&#xff0c;internlm2-chat-1.8b (internlm2-chat-7b)也存在这一问题&#xff0c;例如认为13.8<13.11。 任务要求&#xff1a;利用LangGPT优化提示词&#xff0c…

Bugku-ctf-web-eval

<?phpinclude "flag.php";$a $_REQUEST[hello];eval( "var_dump($a);");show_source(__FILE__); ?> include "flag.php"; 这行代码尝试包含一个名为flag.php的文件。这个文件可能包含一些配置信息或其他代码&#xff0c;但是没有提供这…

安装 pytorch-lightning 和pytorch

&#xff08;1&#xff09;要注意版本对齐&#xff1a;[env]pytorch_lightning和pytorch对应版本问题_pytorch-lightning版本对应-CSDN博客 &#xff08;2&#xff09;要注意安装的是什么 pip install pytorch-lightning has been deprecated and will stop being updated Ju…

网络空间资产测绘:为安全防护“画出”实时“地图”

网络空间已成为继海、陆、空、天之后的“第五疆域”&#xff0c;对其空间布局进行摸排并形成“地图”&#xff0c;是维护网络空间安全的基础性工作。近日在2024全球数字经济大会上发布的DayDayMap全球网络空间资产测绘平台&#xff0c;能为用户提供全面、精准、实时的全球网络空…

STKMATLAB 卫星编队覆盖分析纯代码实现

任务描述 设置卫星编队&#xff08;沿航迹编队&#xff0c;大斜视角&#xff0c;幅宽100km&#xff0c;下视角30&#xff0c;斜视角26&#xff09;&#xff0c;设置分析区域&#xff08;中国全境&#xff09;&#xff0c;设置FigureOfMerit&#xff08;展示覆盖率&#xff09;…

目标检测ultralytics-YOLOv8学习笔记(持续更新)

目录 YOLOV8官方文档示例命令格式各项参数解释总结 上述命令输出性能指标输出类别性能指标速度指标输出总结 IOU阈值1. mAP502. mAP50-95区别总结&#xff1a;实际应用中的选择&#xff1a;示例&#xff1a; 精度与召回率精度&#xff08;Precision&#xff09;召回率&#xff…