学内核之十八:纸上得来终觉浅,绝知此事要躬行

news2024/10/7 8:22:11

目录

0 前言

1 ioremap、vmalloc与原子上下文

2 copy_to_user与进程上下文

3 fasync与指针初始化

4 wait_event_interruptible与条件变量


0 前言

大家都知道,内核开发跟应用开发,体验是完全不同的,尤其是驱动。一方面要掌握扎实的语言基础,另一方面还要对操作系统的基本概念深入理解。然后配合软硬件的一些知识,才能做好驱动的开发任务。

博主最近做一个虚拟设备驱动,再次体验到了什么叫纸上得来终觉浅,绝知此事要躬行。

现实中,大部分开发人员(包括博主本人)都是应用开发者,无论是做产品还是项目,主要都是面向特定的业务。而内核,毕竟是一个通用系统,很多情况下是拿来就用,开箱就用。专门做内核开发的人员还是少数。这其中的大部分,也都是主要做驱动开发的。内核开发者主要分布在芯片厂商、互联网大厂以及爱好者社区这三类地方。芯片厂商面对的主要是移植、驱动,互联网大厂面对的主要是优化,而爱好者,可能二者兼具了。

当然,对于做终端产品的厂家,内核开发也是需要的,但是不同的一点是,往往一个产品的初期,内核部分的工作有一些,系统和硬件定型稳定后,主要工作还是应用空间的业务代码编写。所以,有的公司,这两类工作就是一类人干的。初期干内核,写驱动,调移植,后期做应用。此时,为了上手内核开发,入门办法就是买本书学,边学边做。在计算机编程领域,这也没有什么问题,因为写代码本来就是一门实践技能。但问题就在于,如果学了不用,那么就会似懂非懂。即便是应用层开发也是这样,更不要说内核空间了。所以,一个合格的内核开发人员,所需要的培养周期往往就比较长。

这个引子说的有点离题了。主要是啥意思呢,就是想表达博主也是看过不少内核开发书籍的,结果在开发虚拟设备驱动的过程中,遇到了几个活学活用的问题,让我对“纸上”和“躬行”有了更加切身和深刻的体会,于是在这里记录分享,也希望能对从事这方面工作的读者有所帮助。

先大概说一下需求:内核虚拟驱动需要周期性(高频)采集硬件数据,满足一个块后,将其拷贝到用户空间。大概就是这样一个功能。就是这样简单的功能,实际开发中遇到了几个印象深刻的问题。

1 ioremap、vmalloc与原子上下文

最开始在驱动初始化和open系统调用下来的接口中使用上述接口。后来因为架构调整,将数据映射放到了定时器接口中,结果出现异常。后来思考,ioremap、vmalloc这类的接口,可能会导致睡眠,而内核定时器是在定时中断和软中断的上下文,属于原子上下文,所以不能在定时器中使用上述接口。另外,也不能在定时器接口中使用mutex等锁接口。如果有内存分配需求,则需要GFP_ATOMIC标识,如果需要同步,则需要使用spin_lock自旋锁。

一般大家在中断处理函数中会特别注意这些事项,定时器可能遇到的少,但是一旦有类似需求,就要跟中断一样,按照原子上下文要求来使用。

2 copy_to_user与进程上下文

同上面类似,开始是在系统调用的对接接口中使用上述接口,将数据拷贝到用户空间,没有问题。后来,因为频率控制需求,将其移动到了定时器中,结果内核直接奔溃。想到是原子上下文导致,就重新创建一个内核线程,将其移动到内核线程。此时虽然没有奔溃了,但是拷贝数据不成功。反复测试都是这样。准备调试到copy_to_user内部,看看在哪里出问题了,就在此时,脑海中浮现了答案:内核线程没有用户空间,自然copy_to_user就无用武之地。在感叹内核里面处处是陷阱的同时,也不得不感叹,以前写代码只是浮于表面的了解,没有深入理解和掌握,更没有做到活学活用。好在及时想到,不算浪费太多时间。

另外需要注意copy_to_user的返回值,返回0表示成功,返回具体数值,则表示成功拷贝的字节数,这与大部分接口设计的返回值是不同的。所以千万不能惯性思维,以免出现错误。

3 fasync与指针初始化

驱动里有关需求,就是在内核完成任务后,需要通过信号异步通知应用。这个需求通过下面的代码来实现:

if (dev->fasync) {

    kill_fasync(&dev->fasync, SIGIO, POLL_IN);

}

但是,每次跑到这里就触发异常。反复跟踪调试发现,这里的fasync域是一个指针:struct fasync_struct    *fasync;,在调用fasync_helper(fd, filp, mode, &dev->fasync);赋值之前,是没有值的,但是如果我们分配dev内存之后不初始化的话,fasync域可能是一个随机值,这样再判断指针时,就会误判,从而调用kill_fasync,触发异常。这倒不算一个大问题,主要是保持良好的编码习惯,指针变量定义后,给初值,就会避免此类问题。

4 wait_event_interruptible与条件变量

为了让进程睡眠挂起,驱动中使用了上述接口。对应的,唤醒接口为wake_up_interruptible。一般的使用方式时,在内核线程或者系统调用中,使用标题中的接口让进程睡眠,然后在中断或者定时器等判断触发条件的处理中,在条件满足是,使用唤醒接口,让进程重新起来工作。但是,实际测试中发现,调用唤醒后,进程始终未被唤醒。反复检查代码,发现是接口中变量的作用域导致。除非使用全局变量,否则中断或者定时器与进程上下文并不共享内存空间,如果条件变量不为真,wait_event_interruptible是不会退出的。如果你在调试代码中遇到了奇怪的问题,特别是非常肯定代码逻辑正常,但是效果不符合预期,反复检查都没有答案的时候,而且就要怀疑机器是不是有问题的时候,就需要考虑这些因素:拼写是否有误、中英文是否没有区分、文件字符编码是否不对、是不是打开了输入法的全角、是否用错了斜杆方向、单引号双引号使用不对等等。只要坚信机器是没有问题的,问题都在我的身上,那么上述问题都是可以找到的。否则,谁都无法保证会发生什么。

最后注意到,上述两个接口,对wait_queue参数的使用,一个是有取地址符的,一个没有。

就先整理这么多。

 

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

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

相关文章

MySQL 基本轮廓

目录 什么是数据库 主流数据库 基本使用 连接服务器 服务器管理 使用案例 创建数据库 使用数据库 创建数据库表 表中插入数据 查询表中的数据 服务器,数据库,表关系 MySQL架构 什么是数据库 存储数据用文件就可以了,为什么还要弄…

每日一问-ChapGPT-20230409-中医基础-四诊之望诊

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230409-中医基础-四诊之望诊中医中的望闻问切介绍,以及对应的名家望诊的具体细节望诊拓展当日总结每日一问-ChapGPT系列起因 近来看了新闻,看了各种媒体,抖音,官媒&#xff…

【数据库原理 • 四】数据库设计和规范化理论

前言 数据库技术是计算机科学技术中发展最快,应用最广的技术之一,它是专门研究如何科学的组织和存储数据,如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进,最常用的技术。 当前…

jvm调优一:从源码级别了解jvm类加载机制

目录 一、类加载运行全过程 类加载器加载类的过程 二、类加载器和双亲委派机制 类加载器类型 类加载器初始化过程 双亲委派机制 为什么要设计双亲委派机制? 全盘负责委托机制 一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c…

Kube-proxy 使用 iptables 模式时,通过 Service 服务发布入口如何到达 Pod ?

写在前面 被问到这个问题,整理相关的笔记当 kube-proxy 模式设置为 iptables 的时候,通过 SVC 服务发布入口如何到达 Pod?博文内容涉及: 问题简单介绍三种常用的服务发布方式到Pod报文路径解析 当前集群为版本为v1.25.1Demo 演示使…

linux内核结构以及内核模块编程

1、linux内核结构 1.1、单内核与微内核结构 1.1.1、什么是单内核结构和微内核结构 linux操作系统是一个单内核的结构,它的各个子系统之间可以直接调用 比如说文件系统、内存管理、进程管理以及网络系统和进程间通信它们互相之间可以直接调用只有一些核心的代码它…

记录npm的安装过程

一、访问官网(https://nodejs.org/en),下载nodejs并安装: 然后一路点击next直到安装完成,环境变量已经自动添加好了: 通过设置环境变量,改变本地仓库地址: 可以看到,…

一条更新语句的执行流程又是怎样的呢?

当一个表上有更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表T上所有缓存结果都清空。这也就是我们一般不建议使用查询缓存的原因。 接下来,分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用ID这个索引。然…

LNMP网站框架搭建(yum方式安装)

1. nginx 的yum安装 1.1 搭建nginx相关的yum源 注意:本次安装所获得的软件包都是来源于httpd源(都是由该软件包厂商提供)。所以切记不能像往常一样直接使用本地源去安装一切包 vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx…

Linux--tty

Linux 终端(TTY) TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代。不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TTY 也泛指计算机的终端(terminal)设…

【CSS】更改用户界面样式 ① ( 更改鼠标样式 | 更改鼠标样式应用场景 | 代码示例 )

文章目录一、更改鼠标样式二、更改鼠标样式代码示例三、更改鼠标样式应用场景一、更改鼠标样式 为对象元素设置 cursor 样式 , 可以更改鼠标移动到该元素上的显示样式 ; cursor 样式常用属性值 : default : 默认鼠标样式 , 白色箭头鼠标 ;pointer : 小手形状 ;move : 移动 - …

C++——初始化列表 | explicit关键字 | static成员

文章目录💐专栏导读💐文章导读🌷初始化列表🌺初始化列表的形式🌺初始化列表的注意事项🌷explicit关键字🌺单参数构造函数🌺多参数构造函数🌷static成员🌺stat…

SprigBoot学习笔记(五)

监控 监控的意义 可视化监控平台 监控原理 自定义监控指标 监控的意义 监控服务状态是否宕机 监控服务运行指标(内存、虚拟机、线程、请求等) 监控日志 管理服务(服务下线) 监控的实施方式 显示监控信息的服务器:用于获取服务信息,并显示对应的信息 运行的服务:启动时主动…

Node【五】内置模块 【http模块】

文章目录🌟前言🌟http模块🌟 1.引入http模块🌟 2.创建服务🌟 3.添加头信息🌟 4.搭建一个简单的服务器:🌟 5.Request对象🌟 6.Response对象🌟 7.练习&#xff…

【力扣周赛】第340场周赛

【力扣周赛】第340场周赛6361:对角线上的质数题目描述解题思路6360:等值距离和题目描述解题思路6361:对角线上的质数 题目描述 描述:给你一个下标从 0 开始的二维整数数组 nums ,返回位于 nums 至少一条 对角线 上的…

webgl-原生纹理贴图

踩坑: 1、图片不显示:图片分辨率为非2的幂次方,图片不能被渲染。图形变成黑方块 2的N次幂:1 2 4 8 16 32 64 128 256 512 1024 2048 4096…… 2、几何图形配置映射方式,顶点坐标和纹理坐标对应需要注意,构…

并行分布式计算 并行计算机体系结构

文章目录并行分布式计算 并行计算机体系结构并行计算机结构模型SIMD 单指令多数据流PVP 并行向量处理机SMP 对称多处理机MPP 大规模并行处理机DSM 分布式共享存储多处理机COW 工作站集群总结并行计算机访存模型UMA 均匀存储访问模型NUMA 非均匀存储访问模型COMA 全高速缓存存储…

OpenCV实战之广角相机拍照算法

拍照是手机中的一项重要功能,目前常见的千元机中都包含数个相机模组,能够实现虚化拍照、美颜、广角拍照、夜景等功能。手机是一个拥有巨大销量的电子产品,因此成为图像处理算法的一个重要落地场景。很多AI公司聚焦于此,如虹软、旷…

Docker已经创建运行启动的容器,如何修改容器中的环境变量env使长期有效

1.查看Docker Root目录 docker info | grep Docker Root[rootjenkins ~]# docker info | grep ‘Docker Root’ Docker Root Dir: /data/docker 2.查到容器的长id(container id) 方式一: docker inspect pdmaas | grep "Id"方式…

scikit-learn决策树算法笔记总结

1. scikit-learn决策树算法类库介绍 scikit-learn决策树算法类库内部实现是使用了调优过的CART树算法,既可以做分类,又可以做回归。分类决策树的类对应的是DecisionTreeClassifier,而回归决策树的类对应的是DecisionTreeRegressor。两者的参…