Linux驱动开发之Linux内核中的中断处理与等待队列以及相关API和例程分析

        

目录

中断的特点

Linux中的中断类型

相关API函数

gpio_to_irq()

enable_irq()

disable_irq()

request_irq()

free_irq()

中断的使用

等待队列

DECLARE_WAIT_QUEUE_HEAD()

wait_event_interruptible()

wake_up_interruptible()

中断相关例程

例程分析

源码分享

总结


        中断是计算机中实现异步事件处理的一种关键机制。当中断发生时,CPU会暂停当前的任务,转去运行中断服务例程。中断处理完成后,CPU再返回到原来的任务。这使得中断处理具有很高的实时性和响应速度。在Linux内核中,充分利用了中断机制来响应各种硬件和软件事件。

在Linux操作系统中,中断的本质就是一个数字,要想使用中断,就是对这个数字进行操作,但是内核不允许直接操作这个数字,可以简介的通过函数进行操作,这个函数对中断进行一个转换,转换的对象就是引脚的编号,引脚的编号转换完成之后就可以得到一个中断号,用来进行操作。

中断的特点

  • 快进快出:中断处理必须很快地完成,避免长时间阻塞正常任务。
  • 随机发生:中断可能在任何时候发生,需要能够保存和恢复处理前的上下文。
  • 高优先级:中断处理具有很高的优先级,可以打断正在运行的进程。
  • 准确识别:需要正确区分不同的中断源,调用对应 的服务程序。

Linux中的中断类型

Linux将中断分为以下三类:

  • PPI - 私有外设中断,特定的外设专用中断。
  • SPI - 共享外设中断,可以服务多个外设,数量有限。
  • SGI - 软件生成中断,通过编程的方式触发。

中断号唯一标识一个中断源,用于注册和处理中断。

相关API函数

gpio_to_irq()

功能通过gpio_to_irq函数将引脚编号转换为中断号,用于后续的操作
头文件#include<linux/gpio.h>
原型int gpio_to_irq(unsigned int gpio)
参数unsigned int gpio    引脚编号
返回值成功    中断号失败    负数

enable_irq()

功能使用enable_irq函数使能中断号,使得该中断可以被触发和处理
头文件#include<linux/interrupt.h>
原型void enable_irq(unsigned int irq);
参数unsigned int irq    中断号
返回值无

disable_irq()

功能失能中断号
头文件#include<linux/interrupt.h>
原型void disable_irq(unsigned int irq);
参数unsigned int irq    中断号
返回值无

request_irq()

功能使用request_irq函数向内核注册中断号,指定中断服务函数、中断的触发方式、中断的名字等参数
头文件#include<linux/interrupt.h>
原型static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev)    
参数unsigned int irq,         中断号irq_handler_t handler,   typedefirqreturn_t (*irq_handler_t)(int, void *);中断服务函数unsigned long flags,     中断的触发的方式IRQF_TRIGGER_RISING      上升沿IRQF_TRIGGER_FALLING     下降沿IRQF_TRIGGER_HIGH         高电平IRQF_TRIGGER_LOW          低电平const char *name,       中断的名字void *dev              传给中断服务函数的参数 一般写 NULL   
返回值成功 0失败 负数

free_irq()

功能使用free_irq函数取消中断的注册,释放中断号和中断服务函数
头文件#include<linux/interrupt.h>
原型void free_irq(unsigned int irq, void *dev_id)
参数undigned int irq    中断号void *dev_id    跟注册中断的函数的最后一位保持一致
返回值无

中断的使用

在Linux中使用中断的一般流程是:

  1. 将GPIO引脚号转换为中断号。
  2. 注册中断服务函数。
  3. 使能中断号。
  4. 在中断服务函数中编写处理逻辑。
  5. 根据需要禁用或重新启用中断。
  6. 当不再需要时,取消中断号注册。

采用这种方式,可以很好地响应异步外部事件,提高系统实时性。

等待队列

        由于中断具有异步性和随机性,有时需要利用等待队列使内核进入睡眠,等待某事件发生后再被唤醒继续执行。常用的等待队列相关函数有:

DECLARE_WAIT_QUEUE_HEAD()

功能定义等待队列的结构体
原型#define DECLARE_WAIT_QUEUE_HEAD(name) \wait_queue_head_tname = __WAIT_QUEUE_HEAD_INITIALIZER(name)
参数name:    等待队列的结构体的名字

wait_event_interruptible()

功能阻塞内核,直到满足指定的条件才解除阻塞
头文件#include <linux/sched.h>
原型#define wait_event_interruptible(wq,condition)                            \({                                                                            \int__ret = 0;                                                        \if(!(condition))                                                     \__wait_event_interruptible(wq,condition, __ret);        \__ret;                                                                    \})    
参数wq    等待队列的头condition    条件    0  休眠  1 唤醒
返回值无

wake_up_interruptible()

功能解除等待队列的阻塞,唤醒等待队列中的进程
头文件#include<linux/sched.h>
原型#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
参数x    等待队列的结构体指针
返回值无

等待队列可以防止空轮询,减少CPU资源占用,是Linux内核编程中非常重要的机制。

中断相关例程

例程分析

        该例程使用了中断来监控按键状态变化,通过定时器和等待队列在按键按下时解除阻塞并通知用户空间。同时,还提供了对应的文件操作函数供用户空间使用。

全局变量

  • ​dev_t mydev​​: 设备号。
  • ​struct cdev mycdev​​: 字符设备结构体。
  • ​struct file_operations myfops​​: 文件操作结构体。
  • ​struct class *myclass​​: 类结构体。
  • ​unsigned int irq_num[4], myirq​​: 中断号和当前中断号。
  • ​int cond​​: 条件变量,用于在读取函数中阻塞解除。

函数声明

  • ​myfunc​​: 中断服务函数,当中断触发时调用。
  • ​myopen​​​, ​​myclose​​​, ​​myread​​​, ​​mypoll​​: 对应打开、关闭、读取和轮询操作的函数。

myopen

  • 将GPIO引脚转换为中断号。
  • 使能中断号。
  • 注册中断号并设置中断服务函数。
  • 打印日志表示设备已打开。

myclose

  • 取消中断的注册并禁用中断。

myread

  • 读取GPIO引脚的状态,如果按键按下,则将按键号传递给用户空间。
  • 当条件符合时解除阻塞并唤醒等待队列。

定时器函数 my_timer_fun

  • 当定时器激活时,检查中断号,如果对应的按键按下,则设置条件变量为1,唤醒等待队列。

mypoll

  • 使用​​poll​​系统调用进行轮询等待。
  • 当条件满足时,返回​​POLLIN​​。

模块初始化和退出

  • ​mykey_init​​: 初始化函数,在模块加载时执行,包括设备号分配、字符设备注册、类结构体创建以及设备文件创建等操作。
  • ​mykey_exit​​: 退出函数,在模块卸载时执行,注销设备文件、类结构体,删除字符设备等操作。

源码分享

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/poll.h>dev_t mydev;
struct cdev mycdev;
struct file_operations myfops;
struct class *myclass;
unsigned int irq_num[4], myirq;
int cond;
struct timer_list mytimer;//创建一个等待队列
DECLARE_WAIT_QUEUE_HEAD(mywait);irqreturn_t myfunc(int num, void *args)
{myirq = num;//更新激活定时器mod_timer(&mytimer, jiffies + msecs_to_jiffies(15));return 0;
}int myopen (struct inode *inode, struct file *file)
{int ret, i;//gpio转中断号for(i = 2; i < 6; i++){irq_num[i - 2] = gpio_to_irq(EXYNOS4_GPX3(i));if(irq_num[i - 2] < 0){printk("中断号转换失败\n");return -1;}}//使能中断号for(i = 0; i < 4; i++){enable_irq(irq_num[i]);}//向内核注册一个中断号for(i = 0; i < 4; i++){ret = request_irq(irq_num[i], myfunc, IRQF_TRIGGER_FALLING, "keytest", NULL);if(ret < 0){printk("中断号注册失败\n");return -1;}}printk("open执行完毕\n");return 0;
}int myclose (struct inode *inode, struct file *file)
{int i;//取消中断的注册for(i = 0; i < 4; i++){free_irq(irq_num[i], NULL);disable_irq(irq_num[i]);}return 0;
}ssize_t myread (struct file *file, char __user *buf, size_t size, loff_t *loff)
{int value, ret, i;
//	wait_event_interruptible(mywait, cond);printk("阻塞解除\n");for(i = 2; i < 6; i++){value = gpio_get_value(EXYNOS4_GPX3(i));if(value == 0){i = i - 1;ret = copy_to_user(buf, &i, 4);break;}}cond = 0;return 0;
}//定时器回调函数
void my_timer_fun(unsigned long data){int i;for(i = 0; i < 4; i++){if(myirq == irq_num[i]){if(gpio_get_value(EXYNOS4_GPX3(i + 2)) == 0){printk("按键 %d 按下\n", i + 1);cond = 1;wake_up_interruptible(&mywait);break;}}}
}//poll轮询函数
unsigned int mypoll (struct file *file, struct poll_table_struct *p){poll_wait(file, &mywait, p);if(cond == 1){cond = 0;return POLLIN;}return 0;
}static int __init mykey_init(void)
{//初始化定时器mytimer.expires  = jiffies + 2 * HZ;mytimer.function = my_timer_fun;init_timer(&mytimer);//申请设备号alloc_chrdev_region(&mydev, 0, 1, "mykey");printk("申请到的设备号 %d\n", mydev);printk("主设备号 %d\n", MAJOR(mydev));printk(
"次设备号 %d\n", MINOR(mydev));//初始化核心结构体myfops.owner = THIS_MODULE;myfops.open = myopen;myfops.release = myclose;myfops.read = myread;myfops.poll = mypoll;cdev_init(&mycdev, &myfops);//向设备注册核心结构体cdev_add(&mycdev, mydev, 1);//创建类结构体myclass = class_create(THIS_MODULE, "mykey");if(myclass == NULL){printk("创建类结构体失败\n");return -1;}//创建设备文件device_create(myclass, NULL, mydev, NULL, "mykey");return 0;
}static void __exit mykey_exit(void)
{//注销设备文件device_destroy(myclass, mydev);//注销类结构体class_destroy(myclass);//取消注册cdev_del(&mycdev);//释放设备好unregister_chrdev_region(mydev, 1);
}module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");

总结

        中断机制和等待队列在Linux内核中发挥着重要作用,它们的合理利用可以构建出高实时性和高效性的系统。中断编程涉及内核低层操作,需要谨慎处理,但掌握后可以大大提升系统的异步处理能力。

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

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

相关文章

linux实用技巧:ubuntu18.04安装samba服务器实现局域网文件共享

Ubuntu安装配置Samba服务与Win10共享文件 Chapter1 Ubuntu18.04安装配置Samba服务与Win10共享文件一、什么是Samba二、安装Samba1、查看是否有安装samba2、安装samba 三、配置Samba服务1、创建共享目录&#xff08;以samba_workspaces为例&#xff09;2、为samba设置登录用户3、…

独立站的个性化定制:提升用户体验的关键

随着电子商务的竞争加剧&#xff0c;用户体验成为了企业赢得市场的关键因素之一。独立站作为企业品牌形象和产品展示的重要平台&#xff0c;其个性化定制的程度直接影响着用户体验。本文将探讨独立站的个性化定制如何提升用户体验&#xff0c;并通过代码示例说明实现个性化定制…

学习动态规划不同路径、最小路径和、打家劫舍、打家劫舍iii

学习动态规划|不同路径、最小路径和、打家劫舍、打家劫舍iii 62 不同路径 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量dp[i][j] dp[i-1][j] dp[i][j-1] import java.util.Arrays;/*** 路径数量* 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量…

计算机网络-动态路由

网络层协议&#xff1a;ip&#xff0c;ospf&#xff0c;rip&#xff0c;icmp共同组成网络层体系 ospf用于自治系统内部。 一个路由器或者网关需要能够支持多个不同的路由协议&#xff0c;以适应不同的网络环境。特别是在连接不同自治系统的边缘路由器或边界网关的情况下&#…

MongoDB聚合:$merge 阶段(2)

$merge的用途是把聚合管道产生的结果写入指定的集合&#xff0c;有时候可以用$merge来做物化视图。下面是$merge的一些例子。 举例 按需物化视图&#xff1a;创建集合 当输出集合不存在时&#xff0c;$merge将自动创建。首先在zoo数据库的salaries集合中填充员工和部门历史数…

Golang解决跨域问题【OPTIONS预处理请求】

Golang解决跨域问题 前置知识&#xff1a;跨域问题产生条件及原因 跨域是是因为浏览器的同源策略限制&#xff0c;是浏览器的一种安全机制&#xff0c;服务端之间是不存在跨域的。 所谓同源指的是两个页面具有相同的协议、主机和端口&#xff0c;三者有任一不相同即会产生跨域…

Autosar MCAL-RH850P1HC Mcu配置

文章目录 McuModuleConfigurationCvm Diag Lock BitCvm Out Mask DiagCvm Out Mask FbistCvm Output FilterCvm Reset EnableNumber Of Mcu ModesRam SectorsReset SettingSw Reset TriggerMcuClockSettingConfigClock Setting Id

数据库原理与应用快速复习(期末急救)

文章目录 第一章数据库系统概述数据、数据库、数据库管理系统、数据定义、数据组织、存储和管理、数据操纵功能、数据库系统的构成数据管理功能、数据库管理的3个阶段以及特点数据库的特点、共享、独立、DBMS数据控制功能数据库的特点 数据模型两类数据模型、逻辑模型主要包括什…

2023 IoTDB Summit:天谋科技 CTO 乔嘉林《IoTDB 企业版 V1.3: 时序数据管理一站式解决方案》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

【Web2D/3D】CSS3的2D/3D转换、过渡、动画(第一篇)

1. 前言 本篇开始介绍Web2D和3D相关基础知识&#xff0c;会从CSS3的2D/3D转换、过渡、动画&#xff0c;讲到Canvas 2D图形绘制&#xff0c;再到SVG&#xff0c;最后到WebGL。 坐标系&#xff1a;左上点是坐标原点(0,0)&#xff0c;x轴正方向向右&#xff0c;y轴正方向向下&…

STL——查找算法

算法简介&#xff1a; find ——//查找元素find_if ——//按条件查找元素adjacent_find ——//查找相邻重复元素binary_search ——//二分查找法count ——//统计元素个数count_if ——//按条件统计元素个数 1.find 函数原型&#xff1a; find(iterator beg, iterator end,…

(学习打卡1)重学Java设计模式之设计模式介绍

前言&#xff1a;听说有本很牛的关于Java设计模式的书——重学Java设计模式&#xff0c;然后买了(*^▽^*) 开始跟着小傅哥学Java设计模式吧&#xff0c;本文主要记录笔者的学习笔记和心得。 打卡&#xff01;打卡&#xff01; 设计模式介绍 一、设计模式是什么&#xff1f; …

【Matlab】基于遗传算法优化BP神经网络 (GA-BP)的数据时序预测

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88682033 一&#xff0c;概述 基于遗传算法优化BP神经网络 (GA-BP) 的数据时序预测是一种常用的机器学习方法&#xff0c;用于预测时间序列数据的趋势和未来值。 在使用这种方法之前&#xff0c;需要将时间序…

Linux:apache优化(4)—— 隐藏版本号

运行环境 yum -y install apr apr-devel cyrus-sasl-devel expat-devel libdb-devel openldap-devel apr-util-devel apr-util pcre-devel pcre gcc make zlib-devel 源码包配置 ./configure --prefix/usr/local/httpd --enable-cgi --enable-rewrite --enable-so --enabl…

oracle-检查点队列

检查点队列也在buffer cache上&#xff0c;和LRU&#xff0c;CBC。。。一样&#xff0c;也在一个链上。 检查点队列链也链的是脏块&#xff0c;也就是脏块不仅链在LRUW上&#xff0c;也在这里。 在LRUW上是按照冷热排列。而检查点队列链是按照脏块的第一次脏的时间顺序排序。 R…

20231230 SQL基础50题打卡

20231230 SQL基础50题打卡 570. 至少有5名直接下属的经理 表: Employee ---------------------- | Column Name | Type | ---------------------- | id | int | | name | varchar | | department | varchar | | managerId | int | -----------…

2024年单片机毕业设计选题物联网计算机电气电子类

博主八年毕业设计辅导经验&#xff0c;安全 可靠。 题目一&#xff1a;基于单片机的PM2.5空气质量检测仪器 选 1.用到ADC0832模数转换芯片&#xff0c;数据更加精准。 2.使用夏普传感器的GP2Y1010AUOF粉尘传感器实时检测空气中的PM2.5值并通过1602显示出来&#xff0c;检测…

【Spark精讲】一文讲透SparkSQL聚合过程以及UDAF开发

SparkSQL聚合过程 这里的 Partial 方式表示聚合函数的模式&#xff0c;能够支持预先局部聚合&#xff0c;这方面的内容会在下一节详细介绍。 对应实例中的聚合语句&#xff0c;因为 count 函数支持 Partial 方式&#xff0c;因此调用的是 planAggregateWithoutDistinct 方法&a…

conda环境下nvrtc: error: invalid value for --gpu-architecture解决方法

1 问题描述 在运行视频处理的模型过程中&#xff0c;出现如下异常&#xff1a; nvrtc: error: invalid value for --gpu-architecture (-arch)nvrtc compilation failed: #define NAN __int_as_float(0x7fffffff) #define POS_INFINITY __int_as_float(0x7f800000) #define N…

用python画最简单的图案,用python画小猫简单代码

本篇文章给大家谈谈用python画小猫简单100行代码&#xff0c;以及用python画最简单的图案&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 Source code download: 本文相关源码 from turtle import * #两个函数用于画心 defcurvemove():for i in range(200): …