Linux 驱动 中断(二)

中断下半部

在 Linux 内核中,中断下半部(也称为中断下半场)是指在中断服务程序(Top Half)执行完毕后,在上下文之外延迟执行的一些操作。中断下半部通常用于处理那些不适合在中断上下文中立即执行的任务,以保持中断服务程序的快速响应。

中断下半部可以通过以下几种机制来实现:

1、Tasklet,一种内核机制,用于在延迟上下文中执行轻量级的任务。Tasklet 通常在中断服务程序的上下文中调度,并在延迟上下文中执行。是在禁止所有中断的情况下进行的,因此它们可以安全地访问共享的数据结构和资源。

2、工作队列(Workqueue),工作队列是一种内核机制,用于在延迟上下文中异步执行较为耗时的任务。工作队列可以在系统的后台执行,并且可以并发执行多个任务。是由内核线程来完成的,因此可以执行任意复杂度的操作,并且不会阻塞其他内核活动。

3、软中断(SoftIRQ),软中断是一种内核机制,用于在延迟上下文中执行一些较为复杂或耗时的任务。软中断是在内核中断上下文之外的一种执行机制。

代码实现:

1、Tasklet

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)

tasklet_init 是 Linux 内核中用于初始化 Tasklet 结构体的函数。

  • t 是要初始化的 Tasklet 结构体指针。
  • func 是 Tasklet 的处理函数,即中断下半部的处理逻辑。
  • data 是传递给处理函数的参数。

static inline void tasklet_schedule(struct tasklet_struct *t)

tasklet_schedule 是 Linux 内核中用于调度 Tasklet 来执行任务的函数,在中断处理触发后合适的位置执行;

  • t 是要调度执行的 Tasklet 结构体指针。
void tasklet_kill(struct tasklet_struct *t);

tasklet_kill 是 Linux 内核中用于终止 Tasklet 的函数。

举个例子:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>#include <linux/interrupt.h>#define GPIO_BUTTON_PIN 40static unsigned int irq_num;struct tasklet_struct key_drv_tasklet;static void key_drv_tasklet_handler(unsigned long data) {printk("Tasklet executed in delayed context\n");
}// 中断处理函数
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{int value = gpio_get_value(GPIO_BUTTON_PIN);if (!value) {printk("====> key press\n");} else {printk("====> key up\n");}tasklet_schedule(&key_drv_tasklet);return IRQ_HANDLED;
}static int __init key_drv_init(void) {int ret;// 获取中断号// 请求 GPIO 引脚ret = gpio_request(GPIO_BUTTON_PIN, "button_gpio");if (ret) {printk(KERN_ERR "Failed to request GPIO pin\n");return ret;}// 设置 GPIO 引脚方向为输入ret = gpio_direction_input(GPIO_BUTTON_PIN);if (ret) {printk(KERN_ERR "Failed to set GPIO pin direction\n");gpio_free(GPIO_BUTTON_PIN);return ret;}// 将 GPIO 映射到 IRQirq_num = gpio_to_irq(GPIO_BUTTON_PIN);if (irq_num < 0) {printk(KERN_ERR "Failed to map GPIO to IRQ\n");gpio_free(GPIO_BUTTON_PIN);return irq_num;}tasklet_init(&key_drv_tasklet, key_drv_tasklet_handler, 0);// 请求中断ret = request_irq(irq_num, key_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "key_irq", NULL);if (ret) {printk(KERN_ERR "Failed to register IRQ handler\n");gpio_free(GPIO_BUTTON_PIN);return ret;}return 0;
}static void __exit key_drv_exit(void) {free_irq(irq_num, NULL);tasklet_kill(&key_drv_tasklet);gpio_free(GPIO_BUTTON_PIN);
}module_init(key_drv_init);
module_exit(key_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("dengcaixiang");
MODULE_DESCRIPTION("Simple key driver");

执行结果:

2、工作队列

INIT_WORK(work, func)

INIT_WORK 是 Linux 内核中用于初始化工作队列中的工作项的宏

  • work 是要初始化的 struct work_struct 结构体指针。
  • func 是要与工作项关联的处理函数的函数指针。
static inline bool schedule_work(struct work_struct *work)

schedule_work 用于将一个工作项(struct work_struct)添加到工作队列中以延迟执行。

  • work:指向要添加到工作队列中的工作项的指针,类型为 struct work_struct *。该工作项必须是预先初始化的,并且在调用 schedule_work() 函数之后,内核将负责安排该工作项的执行。

举个例子:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>#include <linux/workqueue.h>
#include <linux/interrupt.h>#define GPIO_BUTTON_PIN 40static unsigned int irq_num;static struct work_struct key_drv_workqueue;static void key_drv_work_handler(struct work_struct *work) {printk("Workqueue executed in process context\n");
}// 中断处理函数
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{int value = gpio_get_value(GPIO_BUTTON_PIN);if (!value) {printk("====> key press\n");} else {printk("====> key up\n");}schedule_work(&key_drv_workqueue);return IRQ_HANDLED;
}static int __init key_drv_init(void) {int ret;// 获取中断号// 请求 GPIO 引脚ret = gpio_request(GPIO_BUTTON_PIN, "button_gpio");if (ret) {printk(KERN_ERR "Failed to request GPIO pin\n");return ret;}// 设置 GPIO 引脚方向为输入ret = gpio_direction_input(GPIO_BUTTON_PIN);if (ret) {printk(KERN_ERR "Failed to set GPIO pin direction\n");gpio_free(GPIO_BUTTON_PIN);return ret;}// 将 GPIO 映射到 IRQirq_num = gpio_to_irq(GPIO_BUTTON_PIN);if (irq_num < 0) {printk(KERN_ERR "Failed to map GPIO to IRQ\n");gpio_free(GPIO_BUTTON_PIN);return irq_num;}INIT_WORK(&key_drv_workqueue, key_drv_work_handler);// 请求中断ret = request_irq(irq_num, key_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "key_irq", NULL);if (ret) {printk(KERN_ERR "Failed to register IRQ handler\n");gpio_free(GPIO_BUTTON_PIN);return ret;}return 0;
}static void __exit key_drv_exit(void) {free_irq(irq_num, NULL);gpio_free(GPIO_BUTTON_PIN);
}module_init(key_drv_init);
module_exit(key_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("dengcaixiang");
MODULE_DESCRIPTION("Simple key driver");

执行结果:

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

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

相关文章

win11部署自己的privateGpt(2024-0304)

什么是privateGpt? privategpt开源项目地址 https://github.com/imartinez/privateGPT/tree/main 官方文档 https://docs.privategpt.dev/overview/welcome/welcome PrivateGPT是一个可投入生产的人工智能项目&#xff0c;利用大型语言模型&#xff08;LLMs&#xff09;的…

【MATLAB】 CEEMD信号分解+FFT傅里叶频谱变换组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 展示出图效果 1 CEEMD信号分解算法 CEEMD 分解又叫互补集合经验模态分解&#xff0c;英文全称为 Complementary Ensemble Empirical Mode Decomposition。 CEEMD是对EEMD的改进&#xff0c;它在EEMD的基础上引入了一个…

【数据结构】复杂度详解

目录 &#xff08;一&#xff09;算法的复杂度 &#xff08;二&#xff09;时间复杂度 &#xff08;1&#xff09;练笔解释&#xff1a; i&#xff0c;示例1 ii&#xff0c;示例2 iii&#xff0c;二分查找 iv&#xff0c;斐波那契 &#xff08;三&#xff09;空间复杂度…

AI-数学-高中-34概率-古典概率模型

原作者视频&#xff1a;【概率】【一数辞典】3古典概型_哔哩哔哩_bilibili 等可能性&#xff1a;每个样本点出现的可能性是相同的。 随机事件A的发生事件A的样本点数k / 样板空间总样本点数n。 示例1&#xff1a; 示例2&#xff1a;

Vue.js的单向数据流:让你的应用更清晰、更可控

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Linux 操作系统概述

GNU计划 GNU --"GNUs Not UNIX" 建立一个自由、开放的UNIX操作系统&#xff08;Free UNIX&#xff09; GNU 通用公共许可证&#xff08;General Public License&#xff0c;GPL&#xff09; ”四项基本自由“ 按照自己的意愿自由地运行该软件自由地学习并根据…

掘根宝典之C语言字符串输入函数(gets(),fgets(),get_s())

字符串输入前的注意事项 如果想把一个字符串读入程序&#xff0c;首先必须预留该字符串的空间&#xff0c;然后用输入函数获取该字符串 这意味着必须要为字符串分配足够的空间。 不要指望计算机在读取字符串时顺便计算它的长度&#xff0c;然后再分配空间(计算机不会这样做&a…

Carbondata编译适配Spark3

背景 当前carbondata版本2.3.1-rc1中项目源码适配的spark版本最高为3.1,我们需要进行spark3.3版本的编译适配。 原始编译 linux系统下载源码后&#xff0c;安装maven3.6.3&#xff0c;然后执行&#xff1a; mvn -DskipTests -Pspark-3.1 clean package会遇到一些网络问题&a…

阿里云服务器2核4G租用价格_2核4G支持人数新能测评

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

Python实现CCI工具判断信号:股票技术分析的工具系列(5)

Python实现CCI工具判断信号&#xff1a;股票技术分析的工具系列&#xff08;5&#xff09; 介绍算法解释 代码rolling函数介绍完整代码data代码CCI.py 介绍 在股票技术分析中&#xff0c;CCI (商品路径指标&#xff09;是一种常用的技术指标&#xff0c;用于衡量股价是否处于超…

算法沉淀——动态规划之完全背包问题(leetcode真题剖析)

算法沉淀——动态规划之完全背包问题 01.【模板】完全背包02.零钱兑换03.零钱兑换 II04.完全平方数 完全背包问题是背包问题的一种变体&#xff0c;与01背包问题不同&#xff0c;它允许你对每种物品进行多次选择。具体来说&#xff0c;给定一个固定容量的背包&#xff0c;一组物…

2.3 shl,shr,inc,dec,xchg,neg指令,中断int指令

汇编语言 1. shl左移指令 shl是逻辑左移指令&#xff0c;它的功能是将一个reg或内存单元中的数据向左移位&#xff1b;将最后移出的一位写入cf中&#xff1b;最低位用0补充shl&#xff1a;shift left例如&#xff1a;0100 1000b 往左移一位&#xff0c;变成10010000b&#xf…