Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

news2024/11/17 1:27:32

若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134561660

红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…

Linux系统移植和驱动开发专栏

上一篇:《Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo》
下一篇:敬请期待…


前言

  驱动写好后,用户层使用系统函数调用操作相关驱动从而实现与系统内核的关联,本篇主要就是理解清楚驱动如何让用户编程来实现与内核的交互。


杂项设备文件操作集

cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h

  搜索到(vi则直接使用“/”):
  

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        int (*iterate_shared) (struct file *, struct dir_context *);
        __poll_t (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        unsigned long mmap_supported_flags;
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*setfl)(struct file *, unsigned long);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
        unsigned (*mmap_capabilities)(struct file *);
#endif

        int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
                        u64);
        ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
                        u64);
} __randomize_layout;

  例如read函数,那么就是打开驱动使用系统read,打开这个设备驱动的句柄,那么就会调用read函数,其他的以此类推,还比较好理解。


Linux文件操作集的意义

概述

  Linux一切都是文件,都有对应的打开、关闭和读写等相关操作,而这些操作都是使用打开文件后的句柄来表示,那么函数再根据句柄的类型,如打开的是杂项设备驱动,就会去调用杂项设备操作文件字符集里面对应的函数来执行操作了。
  在编程的时候会使用open打开一个设备节点(可以是文件打开,可以打开设备),这时候返回得到设备节点句柄标识fd(失败是-1),然后使用fd去read、write等各种操作则会相当于调用这个设备驱动里面文件操作集的read、write。
  下面是常用的文件操作。

open函数(实现测试)

int (*open) (struct inode *, struct file *);

read函数(实现测试)

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)

write函数(实现测试)

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

poll/select函数(本篇没写)

__poll_t (*poll) (struct file *, struct poll_table_struct *);

ioctl函数(本篇没写)

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

close函数(实现测试)

int (*release) (struct inode *, struct file *);


驱动模板准备

  首先复制之前的registerMiscDev的驱动,改个名字为:testFileOpts:

cd ~/work/drive
cp -arf registerMiscDev testFileOpts
cd testFileOpts
make clean
mv registerMiscDev testFileOpts.c

  在这里插入图片描述

   然后修改makefile里面的(obj-m模块名称改下),模板准备好了

gedit Makefile  

  在这里插入图片描述

  下面基于testFileOpts.c文件进行注册杂项设备,修改.c文件:

gedit testFileOpts.c

  在这里插入图片描述

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

struct file_operations misc_fops = {
  .owner = THIS_MODULE,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");

module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

杂项设备添加常用操作集open函数Demo

  注意,要是调用的函数没有写,则不会报错也不会有其他操作反应,所以并不是所有函数都是必须写的。

步骤一:实现open函数

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile)");
    return 0;
}

  在这里插入图片描述

步骤二:(关键)赋值到文件操作集指针

  在这里插入图片描述

步骤三:编译加载驱动

  先编译试试:
  在这里插入图片描述

  然后加载驱动:

sudo insmod tesFileOpts.ko

  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  这时候,设备节点注册成功了。

步骤四:在程序中调用打开设备节点open

  本步骤是c语言编程,使用linux系统函数打开设备节点:
  新建文件:

vi test.c

  输入代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd;
  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("fialed to open %s\n", devPath);
    return -1;
  } else{
    printf("Succeed to open %s\n", devPath);
  }
  return 0;
}

  在这里插入图片描述

  编译:

gcc test.c

  在这里插入图片描述

  默认输出就是a.out,下面运行一下:
  在这里插入图片描述

  无法运行,是因为ubuntu对设备需要管理员权限,管理员权限运行:
  在这里插入图片描述

  查看内核打印输出(这里出现没有打印输出,查看“入坑一”):
  在这里插入图片描述

  至此,从用户编程层如何对设备结点,然后调用到内核层函数就基本清楚了。


补充其他函数Demo

补充read、write

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile\n)");
    return 0;
}

// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
    printk("int misc_release(struct inode * pInde, struct file * pFile\n)");
    return 0;
}

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

struct file_operations misc_fops = {
  .owner = THIS_MODULE,
  .open = misc_open,
  .release = misc_release,
  .read = misc_read,
  .write = misc_write,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

修改test.c测试驱动源码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd;
  char buf[32] = {0};

  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("Failed to open %s\n", devPath);
    return -1;
  }else{
    printf("Succeed to open %s\n", devPath);
  }

  read(fd, buf, sizeof(buf));
  write(fd, buf, sizeof(buf));

  close(fd);
  printf("exit\n");
  fd = -1;
  return 0;
}

查看输出

  在这里插入图片描述


入坑

入坑一:内核未打印open函数

问题

  程序打开设别节点,未打印open函数

原因

  打开函数没有赋值给文件操作集。

解决

  在这里插入图片描述

入坑二:dmesg少了close的release打印

问题

  在这里插入图片描述

测试

  在这里插入图片描述

  在这里插入图片描述

  研究了dmesg,就是没出来,这个不清楚了,后来问了驱动大佬,提醒是可能是换行的问题,后加上可以了。

解决方法

  在这里插入图片描述
  在这里插入图片描述


上一篇:《Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo》
下一篇:敬请期待…


本文章博客地址:https://hpzwl.blog.csdn.net/article/details/134561660

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1262190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenVINO异步Stable Diffusion推理优化方案

文章目录 Stable Diffusion 推理优化背景技术讲解&#xff1a;异步优化方案思路&#xff1a;异步推理优化原理OpenVINO异步推理Python API同步和异步实现方式对比 oneflow分布式调度优化优势&#xff1a;实现思路 总结&#xff1a; Stable Diffusion 推理优化 背景 2022年&am…

山西电力市场日前价格预测【2023-11-29】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-11-29&#xff09;山西电力市场全天平均日前电价为275.28元/MWh。其中&#xff0c;最高日前电价为415.78元/MWh&#xff0c;预计出现在17:45。最低日前电价为0.00元/MWh&#xff0c;预计出…

Make Pixels Dance: High-Dynamic Video Generation论文解析

高动态视频生成的新进展 Make Pixels Dance: High-Dynamic Video Generation高动态视频生成的新进展前言视频生成模式摘要论文十问实验数据集定量评估指标消融研究 训练和推理技巧训练技术推理技术 更多的应用 Make Pixels Dance: High-Dynamic Video Generation 高动态视频生…

MySQL在Docker容器中的性能损失分析与优化策略

文章目录 1. Docker容器对MySQL性能的潜在影响1.1. IO性能1.2. 网络性能1.3. 资源隔离 2. 优化策略2.1. 使用本地数据卷2.2. 配置合理的容器网络2.3. 限制容器资源2.4. 使用容器编排工具 3. 性能测试与监控4. 结论 &#x1f389;MySQL在Docker容器中的性能损失分析与优化策略 ☆…

sqli-labs靶场详解(less17-less22)

目录 less-17 less-18 less-19 less-20 less-21 less-22 less-17 修改密码关卡 服务器后端 账号密码都存在数据库中 使用UPDATE进行修改密码 尝试username处 尝试好久尝试不出来应该是对用户名进行了过滤 于是对password进行注入 判断注入点 passwdadmin 报错&#xff1a…

MySQL使用函数和存储过程实现:向数据表快速插入大量测试数据

实现过程 1.创建表 CREATE TABLE user_info (id INT(11) NOT NULL AUTO_INCREMENT,name VARCHAR(20) DEFAULT NULL,age INT(3) DEFAULT NULL,pwd VARCHAR(20) DEFAULT NULL,phone_number VARCHAR(11) DEFAULT NULL,email VARCHAR(255) DEFAULT NULL,address VARCHAR(255) DEF…

【TinyALSA全解析(二)】wav和pcm音频文件格式详解

wav和pcm音频文件格式详解 一、本文的目的二、wav和pcm格式文件介绍三、pcm格式文件解析四、wav文件内容解析4.1 文件内容描述4.2 实战分析 五、如何在各种音频格式之间进行转换 /******************************************************************************************…

技术SEO的基础知识和 10 个最佳实践

你有没有想过导致某些网站在搜索结果中排名比其他网站更好的因素&#xff1f;针对搜索引擎进行优化是关键&#xff08;SEO&#xff09;。SEO&#xff0c;即搜索引擎优化&#xff0c;是一种用于提高网站在搜索引擎中的知名度的方法。技术搜索引擎优化&#xff08;SEO&#xff09…

用CHAT总结费曼学习法的关键

问CHAT&#xff1a;费曼学习法的关键 CHAT回复&#xff1a;费曼学习法是由著名物理学家理查德费曼所发明的一种学习方法&#xff0c;旨在以深入理解为目标&#xff0c;帮助自己学习新的知识和技能。 费曼学习法有四个关键步骤&#xff1a; 1. 学习&#xff1a;首先&#xff0…

服务器运行情况及线上排查问题常用命令

一、top命令 指令行&#xff1a; top返回&#xff1a; 返回分为两部分 &#xff08;一&#xff09;系统概览&#xff0c;见图知意 以下是几个需要注意的参数 1、load average&#xff1a; 系统负载&#xff0c;即任务队列的平均长度。三个数值分别为 1分钟、5分钟、15分…

VSCode Vue 开发环境配置

Vue是前端开发中的重要工具与框架&#xff0c;可以保住开发者高效构建用户界面。 Vue2官方文档&#xff1a;https://v2.cn.vuejs.org/ Vue3官方文档&#xff1a;https://cn.vuejs.org/ Vue的安装和引用 Vue2的官方安装指南&#xff1a;https://v2.cn.vuejs.org/v2/guide/ins…

数据分析工具比较:Excel vs Python vs R

写在开头 在数据分析的世界里,选择合适的工具至关重要。本篇博客将深入比较常用的数据分析工具,包括Excel、Python和R,以帮助读者更好地选择适合自己需求的工具。 1.Excel:经典易用的电子表格 优势: 用户友好: Excel是大多数人熟悉的电子表格工具,使用简单,无需编程…

汽车电子 -- 车载ADAS之RCTA(后方横向来车预警 )

国际标准: RCTA: GB/T XXXXX—XXXX 乘用车后部交通穿行提示系统性能要求及试验方法 一、后方横向来车预警RCTA&#xff08; Rear Cross Traffic Assist&#xff09; 在车辆倒车时&#xff0c;实时监测车辆后方横向接近的其它道路使用者&#xff0c;并在可能发生碰撞危险时发…

稳定视频扩散数据管理解密【stable video diffusion】

Stability AI 最近于 2023 年 11 月 21 日推出了其最新模型—稳定视频扩散&#xff08;SVD&#xff09;。视频生成模型的这一突破取决于数据管理的关键作用。 除了模型检查点之外&#xff0c;他们还发布了一份技术报告。 让我们在 Stability AI 的技术报告和一些引人注目的示例…

仿东郊到家预约按摩小程序开发;

在这个快节奏的现代社会&#xff0c;人们对便捷、高效的服务需求日益增大。正因如此&#xff0c;到家预约系统上门按摩小程序应运而生&#xff0c;它结合了互联网技术和传统按摩服务&#xff0c;不仅满足了人们对便捷按摩服务的需求&#xff0c;还为商家提供了全新的商业价值。…

2、XFP 与 SFP+:有什么区别?

在光纤网络领域&#xff0c;光模块是促进数据顺利传输的重要组件。市场继续接受10G XFP和10G SFP等10G光模块&#xff0c;促使人们对XFP与SFP进行更仔细的审视。他们有什么区别&#xff1f;XFP和SFP的定义是什么&#xff1f;他们的应用场景又如何呢&#xff1f;在下文中寻找所有…

Vue3-Eslint配置代码风格

prettier风格配置 官网&#xff1a;https://prettier.io Eslint&#xff1a;代码纠错&#xff0c;关注于规范 prettier&#xff1a;专注于代码格式化的插件&#xff0c;让代码更加美观 两者各有所长&#xff0c;配合使用优化代码 生效前提&#xff1a; 1&#xff09;禁用…

基于JavaWeb+SSM+Vue校园综合服务小程序系统的设计和实现

基于JavaWebSSMVue校园综合服务小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 摘 要 I Abstract II 第一章 绪 论 1 1.1选题背景 2 1.2研究现状 3 1.3研究内容 …

C/C++ Zlib实现文件压缩与解压

在软件开发和数据处理中&#xff0c;对数据进行高效的压缩和解压缩是一项重要的任务。这不仅有助于减小数据在网络传输和存储中的占用空间&#xff0c;还能提高系统的性能和响应速度。本文将介绍如何使用 zlib 库进行数据的压缩和解压缩&#xff0c;以及如何保存和读取压缩后的…

打游戏NVIDIA怎么设置性能最好?

打游戏NVIDIA怎么设置性能最好&#xff1f;当前很多用户都在Win10电脑上畅玩游戏&#xff0c;所以想知道NVIDIA控制面板最佳设置方法&#xff0c;更好地发挥NVIDIA控制面板性能&#xff0c;用户就能享受更棒的游戏乐趣。接下来小编给大家详细介绍NVIDIA显卡游戏最佳设置步骤教程…