Linux内核模块开发 第 6 章

news2024/11/9 9:39:22

The Linux Kernel Module Programming Guide

  • Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang
  • 译 断水客(WaterCutter
    在这里插入图片描述

6 字符设备驱动

include/linux/fs.h 中定义了结构体 file_operations ,这个结构体包含指向再设备上执行各种操作的系列函数。结构体的每一字段都对应着驱动中定义的处理请求的函数的地址。

所谓“每一字段对应驱动中…的函数的地址”,即是说 file_operations 中包含一系列的函数指针,指向模块中具体的函数实现。可以把这个结构体理解为设备的操作清单,编写驱动时只需要根据实际需要实现清单中的部分接口就行了。

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 (*iopoll)(struct kiocb *kiocb, bool spin); 
    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 (*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 *file, int mode, loff_t offset, 
        loff_t len); 
    void (*show_fdinfo)(struct seq_file *m, struct file *f); 
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, 
        loff_t, size_t, unsigned int); 
    loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in, 
             struct file *file_out, loff_t pos_out, 
             loff_t len, unsigned int remap_flags); 
    int (*fadvise)(struct file *, loff_t, loff_t, int); 
} __randomize_layout;

某些操作不会在驱动中实现(implemeted by a driver)。例如声卡驱动不需要实现从目录结构中读取的接口,那么这个驱动提供的 file_operations 结构体中的相关指针就可以设为 NULL

GCC 扩展(gcc extension)支持便捷的结构体初始化方式(即内核中常用的乱序初始化),用法形如:

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

或者使用 C99 风格的 designed initilizers 初始化结构体。

file_operations 中包含的用于实现read、write等系统调用的函数,通常被称为 fops

从 3.14 版内核开始, read、write、seek等操作fops 就已经有线程安全的(thread-safe)特定锁(specific lock)保护了,这使得文件位置更新(file position update)是互斥的(mutual exclusion)。所以我们在实现这些操作的时候不需要类似目的的锁(unnecessary locking)。

在计算机中,文件位置更新是指将文件指针移动到文件中的特定位置。

此外,从 5.6 版开始,开发者向内核引入了 proc_ops 结构体,在注册 proc handlers 时不在使用 file_operations 结构体。

在计算机操作系统中,进程处理程序(proc handlers)是一种用于处理进程中断和异常的机制。主要作用是保证进程的正常运行和安全性。当进程发生中断或异常时,进程处理程序可以采取适当的措施来处理这些事件,例如重新启动进程、恢复进程状态、记录日志等。

6.2 File 结构体

每个设备在内核中都由一个 struct file 结构体表示,这个结构体定义在文件 include/linux/fs.h.中。

这个结构体不是用户程序常用的 glibc 中定义的 FILE。另外这个结构体的命名有些误导作用,它指的是抽象的打开的文件,而非用 inode 指代的磁盘文件。

struct file 的指针(instance)通常被称为 filp

驱动基本不会使用include/linux/fs.h. 中定义的各类接口直接覆写(fill) struct file ,只会调用 struct file 中包含的各结构体。

6.3 注册设备

如前所述,用户一般是通过 /dev 目录下的设备文件(device files)访问字符设备的。

主设备号标明驱动处理哪个设备文件,次设备号只用于有多个设备时,驱动分辨正在使用的设备(which device is operating on)。

向系统中添加一个设备意味着将这个设备注册到内核中。在模块初始化的时候会通过调用定义在 include/linux/fs.h 中的 register_chrdev() 函数为设备分配一个主设备号,其原型如下:

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

major 是主设备号,name 是可以在文件 /proc/devices 中看到的设备名,*fops 指向驱动中 file_operations 表的指针。函数返回负数表明设备注册失败。值得一提的是,这个函数不涉及次设备号,因为只有驱动才使用这个属性,内核并不关心次设备号。

现在问题来了,如何才能获取一个没被使用的主设备号呢?最简单的方式是查看 Documentation/admin-guide/devices.txt 然后选一个没有被占用的设备号。但这不是一个好办法,因为这个方法无法操作的互斥性,不能保证后续不会有其他设备使用同样的设备号。正确的答案是向内核请求一个动态的主设备号(ask the kernel to assign you a dynamic major number)。

向函数 register_chrdev() 传参数 0 ,它的返回值就是 dynamic major number。这个办法的弊端在于,因为不确定设备注册时会获得哪个动态设备号,也就不能提前创建设备文件。又三种解决方案:

  1. 驱动打印输出主设备号,然后手动创建设备文件
  2. 新注册的设别会显示在 /proc/devices 文件中,可以通过读这个文件获取主设备号,然后手动/脚本创建设备文件
  3. 驱动在成功注册设备后,通过 device_create() 函数创建设备文件,并在 cleanup_module() 函数中调用函数 device_destroy()

不过,register_chrdev() 函数会占用一些和主设备号相关的次设备号,推荐使用 cdev interface 注册字符设备以减少对次设备号的浪费。

使用 cdev interface 注册字符设备分两步走。

第一步,调用 register_chrdev_region() 或者 alloc_chrdev_region() 注册一系列设备号(register a range of device numbers)。

int register_chrdev_region(dev_t from, unsigned count, const char *name); 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

如果指定设备号,使用 register_chrdev_region(),否则使用 alloc_chrdev_region()

第二步,使用下面的方法为字符设备初始化结构体 struct cdev,并将它和第一步注册的 device number 关联起来(associate it with the device numbers)。

struct cdev *my_dev = cdev_alloc(); 
my_cdev->ops = &my_fops;

上面是 cdev 单独存在的情况,更常规的情况是,设备驱动的 fops 中包含这个结构体,那就要用到 cdev_init() 函数了,原型如下:

void cdev_init(struct cdev *cdev, const struct file_operations *fops);

完成初始化后,可用 cdev_add() 函数将字符设备添加到系统中,函数原型如下:

int cdev_add(struct cdev *p, dev_t dev, unsigned count);

上述各种用法,可以在第 9 章中找到使用范例。

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

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

相关文章

深度学习论文分享(三)Look More but Care Less in Video Recognition(NIPS2022)

深度学习论文分享(三)Look More but Care Less in Video Recognition(NIPS2022) 前言Abstract1. Introduction2 Related Work2.1 Video Recognition2.2 Redundancy in Data(数据冗余) 3 Methodology3.1 Arc…

Apache 虚拟主机企业应用

企业真实环境中, 一台服务器发布单个网站非常浪费资源,所以一台 web 服务器上会发布多个网站少则2~3个多则 30多个网站 在一台服务器上发布多网站,也称之为部署多个虚拟主机, Web 虚拟主机配置方法有以下 种: 1、基于单…

基于机器学习的内容推荐算法及其心理学、社会学影响闲谈

基于机器学习的内容推荐算法目前在各类内容类APP中使用的非常普遍。在购物、时尚、新闻咨询、学习等领域,根据用户的喜好,进行较为精准的用户画像与内容推荐。此类算法不但可以较为准确的分析用户的特征,如年龄、性别等,还能通过长…

QT项目实战(视频播放器)

文章目录 前言一、QMediaPlayer二、QVideoWidget三、QAudioOutput四、播放器代码实现五、最终效果总结 前言 本篇文章将使用QT6.4来实现一个简单视频播放器,在QT中使用一个视频播放器还是非常简单的。那么下面就让我们一起来实现这个视频播放器吧。 一、QMediaPla…

深度学习应用篇-计算机视觉-图像增广[1]:数据增广、图像混叠、图像剪裁类变化类等详解

【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍:【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化…

基于VGG16实现宝石图像分类任务(acc 84%)--paddle paddle

作业:补充网络定义部分,使用卷积神经网络实现宝石分类 要求:1.补充完成CNN的网络结构定义方法实现宝石识别 2.可尝试不同网络结构、参数等力求达到更好的效果 卷积神经网络 卷积神经网络是提取图像特征的经典网络,其结构一般包…

【hello C++】类和对象(下)

目录 1. 再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit关键字 2. static成员 2.1 概念 2.2 特性 3. 友元 3.1 友元函数 3.2 友元类 4. 内部类 5.匿名对象 6.拷贝对象时的一些编译器优化 7. 再次理解类和对象 1. 再谈构造函数 1.1 构造函数体赋值 在创建对象…

Spring Boot项目使用 jasypt 加密组件进行加密(例如:数据库、服务的Key、等等进行加密)

🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…

【018】C++的指针数组和数组指针

C 指针数组和数组指针 引言一、指针数组1.1、数值的指针数组1.2、字符的指针数组1.3、二维字符数组 二、指针的指针三、数组指针3.1、数组首元素地址和数组首地址3.2、数组指针的使用示例3.3、二维数组和数组指针的关系 四、多维数组的物理存储总结 引言 💡 作者简介…

从0实现基于Alpha zero的中国象棋AI(会分为多个博客,此处讲解蒙特卡洛树搜索)

从0实现基于Alpha zero的中国象棋AI 0.0、前言 ​ 题主对于阿尔法狗的实现原理好奇,加上毕业在即,因此选择中国象棋版的阿尔法zero,阿尔法zero是阿尔法狗的升级版。在完成代码编写的历程中,深刻感受到深度学习环境的恶劣&#x…

零门槛快速创业:GPT和AI工具的秘密武器

在不到一周的时间里,David创建了一个按需印刷的Etsy商店,该商店具有引人注目的标识和大量独特的文字和艺术。 我最近花了大约一周的时间来建立Etsy店面。在本文中,我将向你展示我如何(可能更有趣的是,在哪里&#xff…

YOLOv5:TensorRT加速YOLOv5模型推理

YOLOv5:TensorRT加速YOLOv5模型推理 前言前提条件相关介绍TensorRT加速YOLOv5模型推理YOLOv5项目官方源地址将训练好的YOLOv5模型权重转换成TensorRT引擎YOLOv5 best.pt推理测试TensorRT Engine推理测试小结 参考 前言 由于本人水平有限,难免出现错漏&am…

笔试强训8

作者:爱塔居 专栏:笔试强训 作者简介:大三学生,希望和大家一起进步 day13 一. 单选 1.下列关于视图的说法错误的是: A 视图是从一个或多个基本表导出的表,它是虚表B 视图一经定义就可以和基本表一样被查询…

Python遍历网格中每个点

遍历网格中每个点 1. 问题描述2. Python实现2.1 网格参数初始化2.2 遍历赋值2.3 矩阵赋值1. 问题描述 最近需要实现一个对矩阵赋值并对矩阵表示的网格参数进行测试的任务,写了一段代码提供参考。 假设网格的长宽均为 2. Python实现 2.1 网格参数初始化 首先定义好需要划分…

【小呆的力学笔记】非线性有限元的初步认识【三】

文章目录 1.2.2 基于最小势能原理的线性有限元一般格式1.2.2.1 离散化1.2.2.2 位移插值1.2.2.3 单元应变1.2.2.4 单元应力1.2.2.5 单元刚度矩阵1.2.2.6 整体刚度矩阵1.2.2.7 处理约束1.2.2.8 求解节点载荷列阵1.2.2.9 求解位移列阵1.2.2.10 计算应力矩阵等 1.2.2 基于最小势能原…

基于深度学习的高精度推土机检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度推土机检测识别系统可用于日常生活中检测与定位推土机目标,利用深度学习算法可实现图片、视频、摄像头等方式的推土机目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训…

通过location实现几秒后页面跳转

location对象属性 location对象属性 返回值location.href获取或者设置整个URLlocation.host返回主机(域名)www.baidu.comlocation.port 返回端口号,如果未写返回空字符串location.pathname返回路径location.search返回参数location.hash返回…

【SCADA】关于KingSCADA仿真驱动的应用

大家好,我是雷工! 在有些时候我们需要用到虚拟仿真的数据,例如在效果演示时为了有良好的动态效果。在KingSCADA软件中可以通过Simulate驱动作为虚拟设备实现这一功能需求。 下面为大家演示该功能的应用: 一、KingIOServer工程设计…

Go实现跨域Cors中间件

概述 本版本主要实现cors中间件 github 地址:Sgin 欢迎star,将会逐步实现一个go web框架 内容 通过建造者模式创建我们的跨域中间件Cors \ 我们了解到,当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加…

StableDiffusion入门教程

目录 介绍模型的后缀ckpt模型&#xff1a;safetensors模型文件夹VAE 模型在哪下载Hugging face:<https://huggingface.co/models>下载SD官方模型文生图模型标签介绍 C站&#xff1a;<https://civitai.com/>筛选模型的类型CheckPoint Type &#xff08;模型的类型&a…