首页 > 编程语言 >字符设备驱动程序

字符设备驱动程序

时间:2024-05-22 22:19:10浏览次数:36  
标签:字符 驱动程序 dev 内存 file scull 设备

字符设备驱动程序

  • 大多简单的硬件设备都依赖于字符设备驱动程序
  • 参考例程:scull驱动程序
  • 注: 本笔记的内核以4.9.88版本为主

scll设计

  • 设计驱动程序的第一步: 定义驱动程序能够提供的机制,即实现设备的抽象
  • 源代码实现:
    • scull0 ~ scull3: 由全局(多次打开共享数据)且持久(设备关闭后打开数据不会丢失)的内存区域;
    • scullpipe0 ~ scullpipe3: FIFO管道,实现了不借助于中断的阻塞/非阻塞读写;
    • scullsingle、scullpriv、sculluid、scullwuid: 其与scull0类似,但是实现了单进程限制(scullsingle)、控制台/会话私有(scullpriv)、限制单用户打开(sculluid、scullwuid)并返回错误(sculluid)或阻塞(scullwuid)。

主次设备号

  • 设备文件(/dev):在ls -l输出的第一列中,字符设备为“c”,块为“b”;
  • 主次设备号位置:修改日期前的两个数
  • 一般而言,主设备号表示驱动程序,次设备号指向实现设备(但是其实现实中没有这么严格,可以通过划分主次设备号来实现分区)

设备号内部表达

  • <linux/types.h>中,32位数,前12位主设备号,后20位次设备号。
    typedef __u32 __kernel_dev_t;
    typedef __kernel_dev_t		dev_t;
    
  • 主次设备号(int)和内部表达的转换:MAJOR(dev_t dev); MINOR(dev_t dev);MKDEV(int major, int minor);

分配与释放设备号;

  • 驱动程序注册设备获得设备号:声明:<linux/fs.h>,实现<fs/char_dev.c>
    • int register_chrdev_region(dev_t from, unsigned count, const char *name)
      param:起始设备号、设备号范围、设备名称
    • int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
      param:设备号返回值、次设备号基值、设备号范围、设备名称
  • 驱动程序析构时需要释放设备号:
    • void unregister_chrdev_region(dev_t from, unsigned count)
      param:次设备号基值、设备号范围

动态分配主设备号

重要的数据结构

  • file_opearations、file、inode
  • 定义位置:<linux/fs.h>

file_opearations

  • 其本质是一种fops
  • __user前缀表示其为用户空间地址,要使用copy from user去完成
  • 核心数据结构:
    • *owner:防止模块操作中释放,一般等于THIS_MODULE
    • llseek:修改读写位置;
    • read:读、read_iter:异步读(4.1版本后)
    • write:写、write_iter:异步写(4.1后)
    • poll:poll、epoll和select后端实现(查询读写阻塞情况)
    • unlocked_ioctl:不持有文件所的ioctl(执行设备特定指令)提高并发效果、compat_ioctl(向后兼容的ioctl,如54位兼容32位)
    • mmap:设备内存映射;
    • open:打开设备文件,可以不是实现,但是系统不会通知应用程序
    • flush:进程关闭设备文件描述符副本时,执行并等待设备操作,如果为NULL,内核会忽略用户请求;
    • release:释放file结构体,和open对应;
    • fsync:刷新数据(常用的是内存数据刷入磁盘)
    • fasync:异步通知;
    • lock:文件锁定;
    • sendpage:发送数据到文件
    • get_unmapped_area:获取在进程地址空间合适位置,便于底层设备内存段的映射(通常由内存管理实现);
    • check_flags:查看fcntl的标志;
    • flock:文件锁(整个文件)
    • splice_write、splice_read:零拷贝读写
    • 其他:setlease、fallocate、show_fdinfo、mmap_capabilities、copy_file_range、clone_file_range、dedupe_file_range;

file

  • 注:其是一个内核结构,不会出现在用户空间中;
  • 其代表了一个打开的文件,在open时被创建,close被销毁。其指针为filp
  • 重要成员:
    • f_mode:模式(读/写)
    • f_pos:当前读写位置;
    • f_flags:文件标志、阻塞\非阻塞等
    • *f_op操作结构体,其允许方法重载;
    • *private_data:调用时保存状态信息,常用于open和其他函数之间的信息传递
    • *f_inode:所对应的目录项结构

inode结构:

  • 对文件系统的信息,其在文件系统表示实际文件(可能被多个file所指向)
  • 重要成员:
    • i_rdev:设备文件的设备编号;
    • *i_cdev:表示字符设备的内核结构,其与块设备、管道等同时放在一个联合体中以节省内存
  • 从inode中获取设备号:iminor宏和imajor宏

字符设备注册:

open和release

open:

  • 完成工作:初始化设备,检查特定错误/首次打开应该初始化/更新f_ops指针/分配填写filp->private_data中数据结构;
  • 可以使用宏container_of() 将传入inode中的cdev提取出来并提取到设备结构体中,之后将dev指针放入private_data中;
  • 如果使用register, 应该检查inode中主次设备号是否与预期一致(使用iminor宏和imajor宏);
  • 对于scull而言,由于其不维护打开计数,只维护使用计数,因此没有设备初始化;同时以写方式打开时,其长度被截为0;

release:

  • 完成工作: 释放open分配的内容(filp->private_data中)/最后一次关闭操作时关闭设备
  • 驱动程序
  • scull:无,其没有需要关闭的硬件。

scull的内存使用(本质是对内存的管理)

  • scull的设备:其使用的内存区域,长度可变;
  • 使用Linux内存管理核心函数kmalloc与kfree,其位置在<linux/slab.h>
  • 对大型页面的分配要使用分配页面的相应操作
  • 设备:指针链表,其指向scull_qset结构,一个scull指针指向一个指针数组(1000个),数组指针指向一个内存区域(量子),大小为4000字节。其结构类似链表+页表的形式。量子大小可以通过修改宏(编译)、修改设置(加载),使用iotcl(运行)。
  • 量子大小的默认策略:大多数情况下只有几kb传递;
  • 代码结构:使用scull_qset结构体(二级指针+链表指针)作为链表节点,使用scull_dev保存设备信息+链表头结点。

read和write

-参数: 文件指针,用户缓存,数据长度,偏移量

  • 注意: 在访问用户缓存时,要使用copy_to_user和copy_from_user实现,保护操作系统,其定义在<asm/uaccsee.h>
  • 注意: 要求访问用户空间的任何函数都必须是可重入的,且能够并发执行,其必须能够处于能够合法休眠的状态。这是由于用户空间进程调度决定的。
  • 在已知参数已经检查过时,可以调用内核版本(加入__前缀);
  • read和write返回值:成功传输字节数,这要求驱动程序必须记住错误的发生。用户空间可以通过访问errno变量查看出错原因;

read

  • 返回值:等于count-成功完成;正的但是小于count-未读取完全,需要重新读取;0-已经到达文件尾;负值-发生错误<linux/errno.h>中实现
  • scull实现:每次调用只处理单一数据量子,超出设备大小返回0,当进程A读取设备时,进程B写入,进程A读取会被截断并返回0。

write

  • 返回值:等于count-成功完成;正的但是小于count-未写入完全,需要重新写入;0-什么也没写入(阻塞);负值-发生错误<linux/errno.h>中实现
  • scull实现:每次调用只处理单一数据量子,并在写入完成后对文件大小进行更新;写入时使用kmalloc申请空间并使用memset进行初始化;注意在写入时候,该程序会将大于一个量子数据缩小为单一量子大小(可能会影响数据完整性)

readv和writev(现在已经脱离了fop结构体,由vfs_readv来实现,最终会调用read_iter和write_iter)

  • 输入:文件指针 + iovec传输数据块(定义在<linux/uio.h>文件中,其包括用户空间起始地址和长度) + 数据块数量 + 偏移量
  • 驱动程序实现:其iovec的数据被iov_iter中的联合体接收,其他被kiocb结构体接收;

参考文献:

ldd3源代码的各版本实现:https://github.com/duxing2007/ldd3-examples-3.x/tree/linux-4.9.y/scull
内核新的ioctl方式--unlocked_ioctl和compat_ioctl(解决error:unknown field 'ioctl' specified in initializer):
https://blog.csdn.net/gatieme/article/details/71437163
Linux flock()函数--文件锁:https://blog.csdn.net/wteruiycbqqvwt/article/details/112672627
编译驱动时error: ‘struct file’ has no member named ‘f_dentry’:
https://blog.csdn.net/hn2zzzz1996/article/details/79496282
Linux x86_64系统调用简介:https://evian-zhang.github.io/introduction-to-linux-x86_64-syscall/index.html

标签:字符,驱动程序,dev,内存,file,scull,设备
From: https://www.cnblogs.com/David-Dong/p/18184353

相关文章

  • 经常出差用哪些办公软件记录工作?可多设备同步使用的便签笔记软件
    对于许多职场人士来说,出差已成为工作常态。在旅途中,如何高效处理工作,确保信息不遗漏,成为了一个不小的挑战。那么,对于经常需要移动办公的我们,哪款办公软件才是最佳选择呢?可多设备同步使用的便签笔记软件是哪款?答案就是——敬业签,一款强大而便捷的便签笔记软件。它的强大之处在于其......
  • 逗号分开的字符串,统计个数从高到底排序
    usesWinapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,System.RegularExpressions, functionCompareStrings(List:TStringList;Index1,Index2:Integer):Integer;beginResult:=StrToInt(List.ValueF......
  • 总结全网C#取随机数方法(整型,浮点型,字符串)
    原文链接:https://blog.csdn.net/m0_65636467/article/details/127770112C#取随机数(Random篇)一、整数随机数//10以内的随机整数Randomrd=newRandom();intn=ran.Next(10);//1-100的随机整数intp=rd.Next(1,100);//大于等于1小于100的整数intNext(intmi......
  • 力扣-1209. 删除字符串中的所有相邻重复项 II
    1.题目题目地址(1209.删除字符串中的所有相邻重复项II-力扣(LeetCode))https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string-ii/题目描述给你一个字符串 s,「k倍重复项删除操作」将会从s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的......
  • 3GBJ5016A-ASEMI工业自动化设备3GBJ5016A
    编辑:ll3GBJ5016A-ASEMI工业自动化设备3GBJ5016A型号:3GBJ5016A品牌:ASEMI封装:3GBJ-A最大重复峰值反向电压:1600V最大正向平均整流电流(Vdss):50A功率(Pd):大功率芯片个数:5引脚数量:5类型:整流扁桥、整流桥正向浪涌电流:600A正向电压:1.10V最大输出电压(RMS):封装尺寸:如图工作温......
  • 零代码零硬件玩转华为云IoT,基于设备联动实时监控设备
    本文分享自华为云社区《一键守护,实时洞察:华为云IoT设备联动,智能感知设备状态变化,精准触发告警通知【零代码零硬件玩转华为云IoT】》,作者:周周的奇妙编程。前言在前面我们已经体验过了设备接入、设备控制和数据长效存储三个方面的内容。(踏云而行:五步带你运用设备模拟器接入华为云I......
  • 使用Chrome 开发者工具提取对应的字符串
    最近在查看一个API的数据,效果很好,但是里面只有一部分我想要的内容 如果是简单一点的可以直接获取如下比如我想要提取返回的代码中关键的字符串:"video":"这里的内容"//定义一个正则表达式来匹配'"video":"链接"'格式的字符串varregex=/"video":\s*"([^"]+)"/gi;......
  • MySQL varchar 单字段的最大字符长度是多少
    MySQLvarchar字段的最大字符长度是多少MySQL行记录的存储结构:变长字段长度列表NULL值列表记录头信息row_idtrx_idroll_ptr列1列2列n每个变长字段值的长度(倒序),根据变长字段的长度而定每个允许为NULL字段的标志位(倒序),每个NULL字段占1位(5字节)隐藏字段(6字节)......
  • MySQL varchar 单字段的最大字符长度是多少
    MySQLvarchar单字段的最大字符长度是多少‍MySQL一行记录除了TEXT,BLOB类型的列,其余的字段长度加起来不能超过65535字节;mysql>CREATETABLEtest(`name`VARCHAR(65535)NULL)ENGINE=InnoDBDEFAULTCHARACTERSET=asciiROW_FORMAT=COMPACT;ERROR1118(42000......
  • 视频汇聚/云存储/安防监控EasyCVR接入GB28181设备未回复ack信息的原因排查
    安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。用户反馈,设备通过国标GB28181注册到平台后视频无法播放,于是请求我们排查情况。对用......