Linux驱动适配内核时,对于不同版本内核中有变化函数的处理

一、情景

Linux驱动适配不同内核时,由于内核版本的不同,有些函数可能没有,或者函数直接变化了,高版本可能增加了一些参数。

二、常规处理方案,根据内核版本判断

一般情况我们处理方式是在使用这些函数时,通过宏来判断当前的内核版本,根据版本来决定怎么使用函数,比如:

#if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)generic_fillattr(inode, stat);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0) && LINUX_VERSION_CODE < KERNEL_VERSION(6,3,0)generic_fillattr(mnt_userns, inode, stat);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0) && LINUX_VERSION_CODE < KERNEL_VERSION(6,6,0)generic_fillattr(idmap, inode, stat);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)generic_fillattr(idmap,request_mask,inode, stat);
#endif

这种我们需要准确知道generic_fillattr这个函数在哪个内核版本怎么变化了。但是Linux的内核有那么多,要找到从哪个版本开始变化了,很多时候是个巨大的工作量。

三、以vfs_getattr函数为例,介绍推荐的处理方案,编译时测试并决定

Linux的驱动适配,我们都需要在对应适配的内核中去编译一次,基于这个原理,我们可以在编译时,通过测试脚本,来确定使用的函数在当前内核中是否存在,以及是怎么样的形式。

不同内核版本的vfs_getattr

低版本内核中,接受三个参数:

int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{struct inode *inode = dentry->d_inode;int retval;retval = security_inode_getattr(mnt, dentry);if (retval)return retval;if (inode->i_op->getattr)return inode->i_op->getattr(mnt, dentry, stat);generic_fillattr(inode, stat);return 0;
}

高一点的版本中,mnt和dentry参数直接变成了path结构体,而只接受2个参数:

int vfs_getattr(struct path *path, struct kstat *stat)
{struct inode *inode = path->dentry->d_inode;int retval;retval = security_inode_getattr(path->mnt, path->dentry);if (retval)return retval;if (inode->i_op->getattr)return inode->i_op->getattr(path->mnt, path->dentry, stat);generic_fillattr(inode, stat);return 0;
}

更高的版本中,多传入了2个参数,提供了额外的功能,允许调用者对文件属性获取操作进行更细粒度的控制,
request_mask:用于指定要获取的文件属性的掩码,即希望获取哪些属性的位掩码。
query_flags:用于指定其他查询标志,例如是否忽略安全性检查等。

int vfs_getattr(const struct path *path, struct kstat *stat,u32 request_mask, unsigned int query_flags)
{int retval;retval = security_inode_getattr(path);if (retval)return retval;return vfs_getattr_nosec(path, stat, request_mask, query_flags);
}
测试脚本

我们创建一个名为generate-kernel-function的脚本,包括以下函数:

通用函数:
prefix="KERNEL_API_"while [ "${1:0:1}" = "-" ] ; docase "$1" in-t)shifttmpdir=$1;;-o)shiftoutfile=$1;;esacshift
donerun() {( $@ || return 1 ) >/dev/null 2>&1
}do_test() {local var="$1"local val="$2"[ -n "$var" ]var="${prefix}$var"local src="$tmp_dir/test.c"local obj="$tmp_dir/test.o"cat > "$src"if ( run gcc -o "$obj" -c "$src" ) ; thenout "#define $var ${val:-1}"elif [ -z "$val" ] ; thenout "#undef $var"elseout "// not $var $val"fi
}

do_test 这个函数接受两个参数:

$1:变量名,用于指定要测试的条件。
$2:值,用于指定测试条件的预期值。
函数首先将这两个参数保存到本地变量 var 和 val 中。然后,它检查是否提供了变量名 $var,并在必要时添加前缀。接着,它定义了两个本地变量 src 和 obj,用于指定测试代码的源文件和目标文件的路径。
接下来,函数使用 cat 命令将标准输入中的内容输出到文件 $src 中,以生成测试代码。
接着,函数调用 c_run 函数编译源文件 $src 并生成目标文件 $obj。如果编译成功,则输出 #define $var ${val:-1},其中 ${val:-1} 表示如果 $val 未定义,则使用默认值 1。如果编译失败且 $val 未定义,则输出 #undef $var,表示未定义该变量。否则,输出 // not $var $val,表示未达到预期值。

测试函数:
do_test "VFS_GETATTR_ARGS" 2 <<END
#include <linux/fs.h>
int test(struct path path, struct kstat exec_file_stat, u32 request_mask, unsigned int query_flags) {return vfs_getattr(&path, &exec_file_stat);
}
ENDdo_test "VFS_GETATTR_ARGS" 3 <<END
#include <linux/fs.h>
int test(struct vfsmount *path, struct dentry *dentry, struct kstat exec_file_stat) {return vfs_getattr(path, dentry, &exec_file_stat);
}
ENDdo_test "VFS_GETATTR_ARGS" 4 <<END
#include <linux/fs.h>
int test(struct path path, struct kstat exec_file_stat, u32 request_mask, unsigned int query_flags) {return vfs_getattr(&path, &exec_file_stat, 0, 0);
}
END
编译脚本中调用,和实际的使用

在编译的Makefile文件中,我们可以通过:

generate-kernel-function -t `pwd`/tmp -o kernel-function.h

调用测试函数,并根据测试的结果生成宏,并输出到的一个头文件中,而在实际使用vfs_getattr函数的地方,我们通过判断哪个宏存在,就可以正确的调用对应的函数:

#if (2 == KERNEL_API_VFS_GETATTR_ARGS)
#define our_vfs_getattr(path, exec_file_stat) vfs_getattr(path, exec_file_stat)
#elif (3 == KERNEL_API_VFS_GETATTR_ARGS)
#define our_vfs_getattr(path, exec_file_stat) vfs_getattr((path)->mnt, (path)->dentry, exec_file_stat)
#elif (4 == KERNEL_API_VFS_GETATTR_ARGS)
#define our_vfs_getattr(path, exec_file_stat) vfs_getattr(path, exec_file_stat, 0, 0)
#else
#error The number of arguments to vfs_getattr is neither 2 nor 3 nor 4
#endifstatic int get_exec_eigenvalue(const char* exe_path, uint64_t* eigenvalue) {struct kstat exec_file_stat;our_nameidata_t nd;if (our_nd_from_name(exe_path, 0, &nd) == 0) {if (our_vfs_getattr(&(nd.path), &exec_file_stat)) {
}

四、是否有某个函数测试的例子

测试函数
do_test "HAVE_PATH_LOOKUP" <<END
#include <linux/fs.h>
#include <linux/namei.h>
void test(const char *path) {struct nameidata nd;path_lookup(path, LOOKUP_FOLLOW, &nd);
}
END
c代码中使用
#ifdef KERNEL_API_HAVE_PATH_LOOKUPrc = path_lookup(mnt, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd);
#elserc = kern_path(mnt, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &(nd.path));
#endif

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

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

相关文章

Imagination:RISC-V CPU的重要力量

根据SHD集团最近发布的报告显示&#xff0c;RISC-V正全速发展中。通过分析从2021年到2030年这十年间RISC-V核在不同应用和功能领域的潜在市场&#xff0c;作者Rich Wawrzyniak得出结论称&#xff0c;到2030年&#xff0c;22.3%的SoC将包含RISC-V CPU&#xff0c;RISC-V的收入预…

Docker网络+原理+link+自定义网络

目录 一、理解Docker网络 1.1 运行tomcat容器 1.2 查看容器内部网络地址 1.3 测试连通性 二、原理 2.1 查看网卡信息 2.2 再启动一个容器测试网卡 2.3 测试tomcat01 和tomcat02是否可以ping通 2.4 只要删除容器&#xff0c;对应网桥一对就没了 2.5 结论 三、…

蓝牙耳机潜水时可以用吗?适合潜水的四大游泳耳机分享

在科技日新月异的今天&#xff0c;我们越来越依赖各种电子设备来丰富我们的生活。无论是运动、工作还是休闲娱乐&#xff0c;耳机都成为了我们不可或缺的伙伴之一。然而&#xff0c;当谈到水上活动时&#xff0c;许多人可能会对蓝牙耳机是否能在水下使用感到困惑。虽然市面上有…

前端实现图片绕指定圆心转动,且图片自身不转动的功能

前端实现图片绕指定圆心转动&#xff0c;且图片自身不转动的功能、 div>img div: 利用css的animation 去做循环 用transform: rotate 去做旋转 然后利用transform-origin: bottom left;指定旋转圆心位置 img 在div旋转的同时 做反向旋转 <template><div class&qu…

SpringBoot第三课-日志

1.日志分类 2.默认使用 默认使用logback与slf4j作为底层默认日志 但是由于日志是系统启动就需要使用&#xff0c;所以与其他的自动配置不同&#xff0c;自动配置是后来使用的&#xff0c;而日志是使用监听器配置好的。 ApplicationListener 3.日志级别 1.级别介绍 SpringB…

刷题第11天

代码随想录刷题第11天 | 二叉树前中后序遍历 前序遍历 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x…

学AI,3种人,3种学法

在刘润的文章《都说要学AI&#xff0c;但3种人&#xff0c;3种学法》中&#xff0c;作者针对当前AI热潮下不同人群的学习需求&#xff0c;提出了三种不同的学习策略&#xff0c;觉得很有启发性&#xff0c;重点总结如下&#xff0c;原始文章较长&#xff0c;链接在文章结尾。 随…

使用采购管理软件构建更高效的采购模式

采购流程是企业整个采购部门的关键要素。无论企业规模大小&#xff0c;构建采购流程的模式都将直接影响企业控制成本、管理风险和保持流程弹性的能力。 下面我们将解释采购模式的类型、优缺点&#xff0c;以及如何确定哪种模式最适合你的采购部门。 集中采购的优缺点 在集中采…

Windows的自动更新和自带的杀毒软件怎么弄掉!

关闭Windows系统更新 Windows系统更新是为了保持设备的平稳和安全运行,保持操作系统安全、稳定及获取新功能修复已知问题并修补安全漏洞的重要过程。如果您想要临时或永久关闭Windows系统的自动更新,可以采用以下几种方式。不过,请务必意识到,禁用系统更新可能会导致您的系…

什么是VRRP?

目录 什么是VRRP&#xff1f; 一、VRRP的定义 VRRP的三种状态 VRRP决定哪个路由器是Master。 二、VRRP的作用 三、VRRP名词解释 1、Virtual Router 2、VRRP Router 四、华为VRRP实验配置基础 检查基于IPv4的VRRP基本功能配置结果 随着网络的快速普及和相关应用的日益深…

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(三)

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;前导&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;一&#xff09; Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验&#xff08;二&#xff09; 八、板级验证 1.验证内容 通过电脑…

5. Java内存模型JMM

文章目录 计算机硬件存储体系基于计算机存储结构的 JMM Java 内存模型 JavaMemoryModelJMM规范下的三大特性原子性可见性有序性 多线程对变量的读写过程读取过程 多线程先行发生原则 happens-beforex,y 的 case 说明happens-before 原则说明happens-before 大原则happens-befor…

如何转行成为产品经理?

转行NPDP也是很合适的一条发展路径&#xff0c;之后从事新产品开发相关工作~ 一、什么是NPDP&#xff1f; NPDP 是产品经理国际资格认证&#xff0c;美国产品开发与管理协会&#xff08;PDMA&#xff09;发起的&#xff0c;是目前国际公认的唯一的新产品开发专业认证&#xff…

fasta文件与fastq文件相互转化Python脚本

fa文件与fq文件互相转换 今天分享的内容是fasta文件与fastq文件的基本知识&#xff0c;以及通过Python实现两者互相转换的方法。 测序数据公司给的格式通常是fq.gz&#xff0c;也就是fastq文件&#xff0c;计算机的角度来说&#xff0c;生物的序列属于一种字符串&#xff0c;就…

CVHub | 万字长文带你全面解读视觉大模型(建议收藏!)

本文来源公众号“CVHub”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;万字长文带你全面解读视觉大模型 0 导读 众所周知&#xff0c;视觉系统对于理解和推理视觉场景的组成特性至关重要。这个领域的挑战在于对象之间的复杂关系…

形参化类 ‘Result‘ 的原始使用

在编程中&#xff0c;特别是在面向对象编程&#xff08;OOP&#xff09;中&#xff0c;当我们说“形参化类”或“参数化类”&#xff0c;我们实际上是指泛型&#xff08;Generics&#xff09;的概念。泛型允许在定义类、接口和方法时使用类型参数。这样&#xff0c;你可以创建可…

统计某个字段有哪些值

统计某个字段有哪些值 POST mgb_test_alias/_search {"size": 0,"aggs": {"gameid_list": {"terms": {"field": "tag.abc","size": 100000}}},"track_total_hits":true }

前端实现点击复制内容到粘贴板功能

Document.execCommand() Document.execCommand()是操作剪贴板的传统方法&#xff0c;各种浏览器都支持。 复制时&#xff0c;先选中文本&#xff0c;然后调用document.execCommand(‘copy’)&#xff0c;选中的文本就会进入剪贴板。 当需要选中的内容是文本输入标签(input、te…

算法练习第十六天| 104.二叉树的最大深度、559. N 叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

优先掌握递归的方式 104.二叉树的最大深度 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode le…

大数据开发(Spark面试真题-卷一)

大数据开发&#xff08;Spark面试真题&#xff09; 1、什么是Spark Streaming&#xff1f;简要描述其工作原理。2、什么是Spark内存管理机制&#xff1f;请解释其中的主要概念&#xff0c;并说明其作用。3、请解释一下Spark中的shuffle是什么&#xff0c;以及为什么shuffle操作…