Linux内核编程(二)杂项设备模型驱动编写

本文目录

  • 一、知识点
    • 1. Linux设备分类
    • 2. 设备号
    • 3. Linux 字符设备的几种编程模型
  • 二、杂项设备模型API
    • 1. 杂项设备结构体
    • 2. 注册杂项设备
    • 3. 注销杂项设备
    • 4. copy_from_user
    • 5. copy_to_user
  • 三、字符设备编程

  
查看:内核驱动程序编写环境搭建。

一、知识点

1. Linux设备分类

在 Linux 操作系统下有三类设备:一是字符设备,二是块设备,三是网络设备。

   ●字符设备特点:是一个顺序的数据流设备,对这种设备的读写是按字符进行的,而且这些字符是连续地形成一个数据流。他不具备缓冲区,所以对这种设备的读写是实时的。比如串口,i2c,等。

   ●块设备特点: 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到缓冲区。 ddr 内存条,sd 卡,u 盘,emmc,nandflash 这种,它们的传输是有缓冲的。

   ●网络设备特点:络设备是面向数据包的接收和发送而设计的。它并不对应于文件系统(/dev 目录下)的节点,而是由系统分配一个唯一的名字(如 eth0)。以太网,wifi,蓝牙,2/3/4G 模块。一种器件可以同时是 2 种设备,例如蓝牙,它是网络设备,也是字符设备。

   除了网络设备外,字符设备和块设备都是通过文件系统的系统调用接口 open()、close()、write()、read()等函数既可以访问,应用程序可以通过打开设备文件来访问该设备。

2. 设备号

   每个设备文件都都有其文件属性,表示是字符设备(c)还是块设备(b)。
   每个设备文件都有一个设备号,由两部分组成:主设备号和次设备号。其中主设备号用于标识驱动程序,次
设备号用于标识使用同一个设备驱动程序的不同的硬件设备,比如有两个串口,就可以用从设备号来区分他们。

3. Linux 字符设备的几种编程模型

   在目前版本 Linux 3.5 版本内核中,字符设备驱动程序的编程模型有三种:杂项设备驱动模型早期经典字符设备驱动模型Linux 2.6 版本中新出现的 Linux 2.6 标准字符设备驱动模型。本章将具体介绍杂项设备驱动模型来编写字符设备。其余两个模型配置复杂,不常用。

二、杂项设备模型API

   在Linux操作系统中,杂项设备是一类设备文件,用于处理各种非标准或非主要类别的设备。杂项设备通常不属于特定的主要设备类别(如块设备、字符设备等),但它们提供了一种灵活的方式来支持各种特殊硬件或虚拟设备。 头文件:#include <linux/miscdevice.h>

主设备号:固定是 10。
次设备号:0~255, 当传递 255 时候表示自动分配次设备号。

1. 杂项设备结构体

在Linux内核中,struct miscdevice结构体用于定义和管理杂项设备。

struct miscdevice {
    int minor;     //次设备号,使用MISC_DYNAMIC_MINOR表示系统自动分配其次设备号。
    const char *name;  //设备名称,通常会在/dev目录下创建一个对应的设备文件。
    const struct file_operations *fops;//指向文件操作结构的指针,该结构定义了设备文件的操作函数,如打开、关闭、读、写、ioctl等。
  
    ///* 下面的成员是供内核使用 ,驱动编写不需要理会,我们只需要配置上面三个成员即可*
    struct device *parent;
    struct device *this_device;
    const struct attribute_group **groups;
    const char *nodename;
    umode_t mode;
};

   在杂项设备结构体中struct file_operations 是一个非常重要的结构体,它定义了文件操作接口。这些接口允许用户空间程序与内核模块进行交互,通过这些操作可以对设备进行读写、打开、关闭、控制等操作。我们用到哪个就实现哪个函数就行。

struct file_operations {
    struct module *owner; // 指向模块的指针,通常设置为 THIS_MODULE,用于模块引用计数
    loff_t (*llseek) (struct file *, loff_t, int); // 移动文件位置指针,类似于用户空间的 lseek
    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 *); // 迭代读操作,适用于较新的文件IO框架
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); // 迭代写操作,适用于较新的文件IO框架
    int (*iterate) (struct file *, struct dir_context *); // 文件目录迭代,用于读取目录内容
    int (*iterate_shared) (struct file *, struct dir_context *); // 文件目录迭代的共享版本
    unsigned int (*poll) (struct file *, struct poll_table_struct *); // 轮询操作,用于非阻塞IO
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); // 设备控制操作,无需大内核锁
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long); // 32位系统上与 ioctl 兼容的操作
    int (*mmap) (struct file *, struct vm_area_struct *); // 内存映射操作,将设备内存映射到用户空间
    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); // 异步IO操作的设置
    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 (*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); // 将数据从文件读入管道
    int (*setlease)(struct file *, long, struct file_lock **, void **); // 设置文件租约
    long (*fallocate)(struct file *, int, loff_t, loff_t); // 为文件分配空间
    void (*show_fdinfo)(struct seq_file *m, struct file *); // 显示文件描述符的信息
};

2. 注册杂项设备

返回值:0:表示注册成功;负数:注册失败

int misc_register(struct miscdevice * misc)
//传入杂项设备结构体。

3. 注销杂项设备

不需要判断返回值。

int misc_deregister(struct miscdevice *misc)
//传入杂项设备结构体。

4. copy_from_user

   是 Linux 内核中用于从用户空间拷贝数据到内核空间的函数。这个函数是确保内核空间和用户空间之间数据安全传输的重要机制之一。因为直接访问用户空间内存是不安全的,内核必须通过这些函数来处理数据的传递,以确保不会因为非法内存访问导致系统崩溃。

unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
//to: 指向内核空间的目标缓冲区。
//from: 指向用户空间的源缓冲区。
//n: 要拷贝的字节数。

5. copy_to_user

   用于将数据从内核空间拷贝到用户空间。这个函数用于内核模块和设备驱动程序中,以安全的方式将数据传递给用户空间。

unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
//to: 指向用户空间的目标缓冲区。
//from: 指向内核空间的源缓冲区。
//n: 要拷贝的字节数。

三、字符设备编程

编写杂项设备驱动节点给用户使用,编写应用层app.c测试驱动功能是否实现。

hello.c

#include <linux/module.h>     //模块
#include <linux/kernel.h>     //内核
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#define KBUF_SIZE (1024)
#define MIN(x,y) ( (x)<(y)? (x):(y) )      //判断哪个小,选哪个
char kernel_buff[KBUF_SIZE];

int misc_open(struct inode * node, struct file * fp)
{
    return 0;
}

int misc_release(struct inode *node, struct file *fp)
{
	return 0;
}

ssize_t misc_read(struct file *fp, char __user *ubuf , size_t size, loff_t *offset)
{
	int ret;
	int length=strlen(kernel_buff);
	ret=copy_to_user(ubuf, kernel_buff, length);
	if(ret !=0){
		 pr_err("copy_to_user error\r\n");
		 return -EINVAL;
	}   
	return length;   //返回成功读取的字节数
}

ssize_t misc_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *offset)
{
   int ret;
   ret=copy_from_user(kernel_buff, ubuf, MIN(KBUF_SIZE,size));
   if(ret !=0){
     pr_err("copy_from_user error\r\n");
	 return -EINVAL;
   }
    pr_err("%s\r\n",kernel_buff);         //打印出用户写来的数据
	return MIN(KBUF_SIZE,size);    //返回成功写入的字节数
}

//描述一个文件操作集
const struct file_operations misc_fops={
  .owner=THIS_MODULE,     //文件操作集的拥有者是 本模块。
  .open =misc_open,       //应用层对本模块的驱动节点open时触发的函数
  .release=misc_release,  //应用层对本模块的驱动节点close时触发的函数
  .read=misc_read,        //应用层对本模块的驱动节点read时触发的函数
  .write=misc_write,      //应用层对本模块的驱动节点write时触发的函数
  
};

//描述一个杂项设备
struct miscdevice misc={
 .minor=MISC_DYNAMIC_MINOR, //由内核自动分配次设备号,misc的主设备号为10.
 .name ="qjl",             //在/dev下生成的驱动节点的名称,不能有空格!
 .fops =&misc_fops,
};

static int __init misc_init(void)
{
   int ret;
   ret=misc_register(&misc);  //向内核注册一个杂项设备
   	if(ret <0){
      pr_err("misc_register error\r\n");
	  return -1;
   }

   return 0;
}

static void __exit misc_exit(void)
{
   misc_deregister(&misc);  //从内核注销一个杂项设备
}

module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

app.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
   int fd;
   ssize_t w_size;
   ssize_t r_size;
   char w_buff[128] = "hello world!";   // 向内核写入的数据
   char r_buff[128] = {0};  // 读取的数据缓冲区,初始化为0

   fd = open("/dev/qjl", O_RDWR);
   if (fd < 0) {
      perror("open error");
      return -1;
   }
   
   w_size = write(fd, w_buff, strlen(w_buff));
   printf("w_size: %ld\n", w_size);

   // 移动文件指针到文件开头
 //  lseek(fd, 0, SEEK_SET);

   r_size = read(fd, r_buff, sizeof(r_buff) - 1); // 预留一个字节用于 null 终止符
   if (r_size < 0) {
      perror("read error");
      close(fd);
      return -1;
   }
   
   r_buff[r_size] = '\0';  // 确保字符串以 null 终止
   printf("r_size: %ld  data: %s\n", r_size, r_buff);
   
   close(fd);
   return 0;
}

insmod hello.ko
在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高考结束嗨一夏,AOC显示器暑期购机指南来了!

摘要&#xff1a;AOC显示器&#xff0c;暑期狂欢&#xff0c;近在眼前&#xff01; 历经三年紧张备战&#xff0c;高考已然画下句号。过去的时间里&#xff0c;你们挥洒汗水&#xff0c;刻苦学习&#xff0c;未来的时间里&#xff0c;也将迎来短暂的休憩嗨皮时光。这个暑假里&…

SaaS产品运营 | 千万不能踏入的PLG模式的六大误区

随着科技的迅速发展和市场竞争的日益激烈&#xff0c;越来越多的公司开始尝试采用PLG&#xff08;Product Led Growth&#xff0c;即产品驱动增长&#xff09;模式来推动其业务的发展。然而&#xff0c;尽管PLG模式在促进增长方面具有显著优势&#xff0c;但在实践中也容易出现…

一文学会Spring 实现事务,事务的隔离级别以及事务的传播机制

目录 一.Spring (Spring Boot) 实现事务 1.通过代码的方式手动实现事务 (手动档的车) 2.通过注解的方式实现声明式事务 (自动挡的车) 二.事务的4大特性(ACID) 三.事务的隔离级别 ①Mysql的事务隔离级别: ②Spring的事务隔离级别: 四.事务的传播机制 ①事务传播机制的概…

ASCII码表介绍

一、ASCII码是什么 ASCII&#xff08;American Standard Code for Information Interchange&#xff0c;美国信息交换标准代码&#xff09;是基于拉丁字母的一套电脑编码系统。它可分为基于7位二进制数的标准版本和基于8位二进制数的扩展版本&#xff0c;标准版本主要用于显示现…

[AI Stability] 开源AI新利器:Stable Diffusion 3 Medium震撼发布!文本到图像再升级!

Stable Diffusion 3 Medium(SD3) 开源了&#xff0c;我们来看下。 关键要点 Stable Diffusion 3 Medium 是 Stability AI 迄今为止最先进的文本到图像开源模型。该模型的体积小巧&#xff0c;非常适合在消费级 PC 和笔记本电脑上运行&#xff0c;也适合在企业级 GPU 上运行。…

精彩回顾!安全智能体的前沿技术研究与实践

&#xff08;关注“安全极客”&#xff0c;回复“智能体”下载第一期系列专题PPT&#xff01;&#xff09; 近日&#xff0c;安全极客和Wisemodel社区联合发起并主办了“AISecurity”系列第1期&#xff1a;大模型与网络空间安全前沿探索线下活动。在这次活动中&#xff0c;云起…

读取CSV文件生成RDD去掉标题行

文章目录 1. 创建CSV文件2. 上传CSV文件3. 读取CSV文件生成RDD4. 去掉标题行生成新RDD5. 查看新生成的RDD 1. 创建CSV文件 执行命令&#xff1a;vim scores.csv 在WPS里查看CSV文件 2. 上传CSV文件 执行命令&#xff1a;hdfs dfs -put scores.csv /park 3. 读取CSV文件生…

「漏洞复现」I Doc View 在线文档预览 qJvqhFt.json 任意文件读取漏洞(XVE-2024-2115)

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

【 EI会议 | 西南大学主办 | 往届均已实现检索】第三届神经形态计算国际会议(ICNC 2024)

第三届神经形态计算国际会议&#xff08;ICNC 2024) 2024 3rd International Conference on Neuromorphic Computing (ICNC 2024) 一、重要信息 大会官网&#xff1a;www.ic-nc.org&#xff08;点击投稿/参会/了解会议详情&#xff09; 会议时间&#xff1a;2024年12月13-15…

在线的、完全免费的、提供回放的技术传播方面的大会:Adobe DITA World 2024

▲ 搜索“大龙谈智能内容”关注公众号▲ 最近美国苹果公司召开了WWDC24&#xff0c;国内不少人熬夜观看。 对于我来说&#xff0c;我更关注在美国召开的另外一个会&#xff0c;它就是Adobe DITA world。 一年一度的Adobe DITA world号称是全球最大的DITA营销和技术传播专业人…

【docker 不希望每次sudo docker cmd】

一、背景 ubuntu系统下安装好了docker 不希望每次sudo docker cmd&#xff0c;这样每次多输入很多字&#xff0c;比较麻烦 二、原理 在 Ubuntu 等 Linux 发行版上&#xff0c;使用 Docker 命令时常常需要使用 sudo 命令&#xff0c;这是因为 Docker 的服务是以 root 权限运行…

基于STM32和人工智能的智能交通管理系统

目录 引言环境准备智能交通管理系统基础代码实现&#xff1a;实现智能交通管理系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景&#xff1a;智能交通管理与优化问题解决方案与优化收尾与总结 1. 引言 随着城市化进程的加快&#xff0…

Three.js做了一个网页版的我的世界

前言 笔者在前一阵子接触到 Three.js 后, 发现了它能为前端 3D 可视化 / 动画 / 游戏方向带来的无限可能, 正好最近在与朋友重温我的世界, 便有了用 Three.js 来仿制 MineCraft 的想法, 正好也可以通过一个有趣的项目来学习一下前端 3D 领域 介绍 游戏介绍 相信大家对我的世…

Web基础和HTTP协议

1、Web基础 &#xff08;1&#xff09;域名概述 域名空间结构 域名注册 2、网页 &#xff08;1&#xff09;网页概述 网页 纯文本格式文件 编写语言为HTML 在用户的浏览器中被“翻译”成网页形式显示出来 网站 由一个一个页面构成的&#xff0c;是多个网页的结合体 主页…

ChatGPT-4o引领医学革命:临床科研创新与效率的新纪元

2024年5月12日&#xff0c;更强版本的ChatGPT-4o上线&#xff0c;文本、语音、图像等多模态交互方式使其在各行各业的应用呈现了更多的可能性。因此&#xff0c;帮助广大临床医学相关的医院管理人员、医生、学生、科研人员更加熟练地掌握ChatGPT-4o在临床医学日常生活、工作与学…

Tabby:一款革新的Mac/Win现代化终端模拟器

在信息技术日新月异的今天&#xff0c;终端操作已成为众多开发者、系统管理员和技术爱好者的日常必备工具。然而&#xff0c;传统的终端模拟器往往功能单一、界面陈旧&#xff0c;无法满足用户对于高效、便捷操作体验的追求。Tabby应运而生&#xff0c;作为一款现代化、功能强大…

WebGL渲染引擎优化方向 -- 内存管理的优化

作者&#xff1a;caven chen 对此系列感兴趣还可以看前文&#xff1a; WebGL渲染引擎优化方向 -- 加载性能优化 WebGL渲染引擎优化方向——渲染帧率的优化 前言 WebGL 是一种强大的图形渲染技术&#xff0c;可以在浏览器中快速渲染复杂的 3D 场景。但是&#xff0c;由于 W…

先导小型五轴联动数控加工中心

先导小型五轴联动加工中心可以作为学校或培训机构的教学工具&#xff0c;帮助学生了解数控加工的基本原理和操作方法。它特别适用于机械、自动化、工业设计等相关专业的学生进行实践操作和课程项目。 小型五轴联动加工中心是一种能够同时控制五个自由度进行联动的加工设备。这五…

VBA实现关闭Excel自动计算,关闭屏幕刷新

Excel代码提速神器 涉及到提取表格大量数据操作&#xff0c;复制粘贴多个单元格时&#xff0c;尽量避免一个个单元格提取&#xff0c;或者一行行一列列提取数值&#xff0c;设计大量IO流操作非常浪费时间。尽量找出数据之间的规律&#xff0c;批量选中复制粘贴&#xff0c;找到…

字符集相关变量理解

建表 创建一个新表&#xff0c;想让他的字符集是 gbk&#xff0c;怎么弄? 尝试1&#xff1a; 失败&#xff01;原因&#xff1a; set names gbk; 等价于&#xff1a;set character_set_client gbk; set character_set_connection gbk; set character_set_results gbk;尝…