前言

中断在驱动中是非常常用的,无论是外部的GPIO中断,还是SPI,I2C等发送或接收中断,都是必不可少的。所以今天来看看Linux中的中断处理。

中断分类

上面我们根据中断来源,屏蔽方式和中断入口对中断进行了简单的分类。

中断控制器

PIC: 可编程中断控制器

GIC: Generic Interrupt Controller, 通用中断控制器。(常用)

GIC是目前最常见的一种中断控制器,它在多核CPU中特别常见。它对中断做了细分:

  • SGI: Software Generated Interrupt, 软件产生的中断,可以用于多核的核间通信。一个CPU可以通过写GIC的寄存器给另外一个CPU产生中断。(中断号0~15)

  • PPI: Private Peripheral Interrupt, 某个CPU私有外设的中断,这类外设的中断只能发给绑定的那个CPU。(中断号16~31)

  • SPI: Shared Peripheral Interrupt, 共享外设的中断,这类中断可以路由到任何一个CPU。(中断号32~1019)

这些更详细的内容可以到内核源码中的Documentation中找到更详细的介绍。

中断处理框架

为了在中断执行时间尽量短和中断处理需完成的工作尽量大之间找到平衡点,Linux将中断处理程序分解为两个半部: 顶半部和底半部。

顶半部用于完成尽量少的比较紧急的功能。底半部完成耗时操作。顶半部不会被中断打断,底半部可以。

如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。

驱动中断编程

//申请中断
/*
参数说明:irq:要申请的硬件中断号handler:中断处理函数,顶半部flags:中断触发方式name:中断名称dev:传递给中断处理函数的私有数据
*/
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)//释放中断
const void *free_irq(unsigned int irq, void *dev_id)//使能和屏蔽某个中断
void disable_irq(unsigned int irq) //需要等待目前中断处理完成
void disable_irq_nosync(unsigned int irq) //不需要等待目前中断处理完成
void enable_irq(unsigned int irq)//使能和屏蔽所有中断
#define local_irq_enable()  do { raw_local_irq_enable(); } while (0)
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)

上面是Linux提供的一些接口函数,比较简单。

底半部机制

Linux实现底半部的机制主要有tasklet, 工作队列, 软中断和线程irq。

1. tasklet

要使用tasklet只要使用下面的宏将tasklet和其处理函数关联在一起即可。

#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

然后在需要调用tasklet的时候调用tasklet_schedule函数就能使系统在适当的时候进行调度运行。

static inline void tasklet_schedule(struct tasklet_struct *t)

tasklet程序模板

//定义tasklet和底半部函数并关联
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);//中断处理底半部
void xxx_do_tasklet(unsigned long)
{......
}
//中断处理顶半部
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{......tasklet_schedule(&xxx_tasklet);......
}
//设备驱动模块加载函数
int __init xxx_init(void)
{//申请中断result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED, "xxx", NULL);......return IRQ_HANDLED;
}
//设备驱动模块卸载函数
void __exit xxx_exit(void)
{......//释放中断free_irq(xxx_irq, xxx_interrupt);
}

2. 工作队列

工作队列的使用方法和tasklet基本相似。

(1) 定义一个工作队列和底半部处理函数

struct work_struct my_wq; //工作队列
void my_wq_func(struct work_struct *work);//处理函数

(2) 初始化工作队列

INIT_WORK(&my_wq, my_wq_func);

(3) 调用工作队列执行函数

schedule_work(&my_wq); //调度工作队列执行

工作队列程序模板

//定义工作队列和处理函数
struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);//中断处理底半部
void xxx_do_work(struct work_struct *work)
{.......
}
//中断处理顶半部
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{.......schedule_work(&xxx_wq);.......return IRQ_HANDLED;
}
//设备驱动模块加载函数
int xxx_init(void)
{......//申请中断result = request_irq(xxx_irq, xxx_interrupt,0, "XXX", NULL);//初始化工作队列INIT_WORK(&xxx_wq, xxx_do_work); ......
}
//设备驱动模块卸载函数
void xxx_exit(void)
{.......//释放中断free_irq(xxx_irq, xxx_interrupt);.......
}

3. 软中断

这个我们一般不直接使用,tasklet就是基于软中断实现的。

4. 线程irq

在内核中除了使用request_irq()、devm_request_irq()申请中断外,还可以使用 request_threaded_irq() 和 devm_request_threaded_irq()来申请。

int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id)int devm_request_threaded_irq(struct device *dev, unsigned int irq,irq_handler_t handler, irq_handler_t thread_fn,unsigned long irqflags, const char *devname,void *dev_id)

上面两个函数多了个thread_fn, 用这两个API申请中断的时候,内核会为相应的中断号分配一个对应的内核线程。

参数handler对应的函数执行于中断上下文,thread_fn参数对应的函数执行于内核线程。如果handler结束时返回值为IRQ_WAKE_THREAD, 内核会调度对应线程执行thread_fn对应的函数。

更多好文,请关注公众号

Linux内核中断系统相关推荐

  1. Linux内核中断系统处理机制-详细分析

    原文地址::https://blog.csdn.net/weixin_42092278/article/details/81989449 相关文章 1.Linux中断管理 (1)Linux中断管理机制 ...

  2. 《linux内核中断》之 法外狂徒张三删库跑路

    法外狂徒张三删库跑路 真实案例:在今年2月份,国内一个程序员删库的消息传遍it界.他的几行代码,直接让上市公司微盟的市值一天蒸发超10亿,300百万用户直接受到影响.网上是谣言四起,可谓是最牛逼的删库 ...

  3. linux内核看门狗关闭方法,linux内核中断之看门狗

    一:内核中断 linux内核中的看门狗中断跟之前的裸板的中断差不多,在编写驱动之前,需要线把内核自带的watch dog模块裁剪掉,要不然会出现错误:在Device Drivers /Watchdog ...

  4. linux内核中断详解

    linux内核中断详解 1.中断的硬件触发流程 外设:如果外设有操作或者有数据可用,那么就会产生一个电信号,这个电信号发送给中断控制器. 中断控制器:中断控制器接收到外设发来的电信号以后,进行进一步的 ...

  5. Linux内核时钟系统和定时器实现

    1. Linux内核时钟系统和定时器实现 Linux 2.6.16之前,内核只支持低精度时钟,内核定时器的工作方式: 系统启动后,会读取时钟源设备(RTC, HPET,PIT-),初始化当前系统时间: ...

  6. Linux内核构建系统之六

    转自:http://www.juliantec.info/julblog/yihect/linux-kernel-build-system-6 Linux内核构建系统之六 yihect | 10 元月 ...

  7. Linux 内核中断体系 初探

    还是要先理解整个中断的体系,首先要理解对中断的含义 如果这是涉及到的软件的调试的话,没有接触过硬件的同学会对,gdb的调试中的中断有一定的认知 但是,这两个中断指的意思是不相同的,gdb的调试中的中断 ...

  8. 初步了解Linux内核中断初始化

    在linux内核中,用struct irq_chip结构体描述一个可编程中断控制器,它的整个结构和调度器中的调度类类似,里面定义了中断控制器的一些操作: 在中断处理中所涉及的几个重要的数据结构:中断描 ...

  9. linux 内核中断与时钟的冲突 问题 del_timer,Linux内核开发之中断与时钟(三)

    晚上7点10分.. "小涛哥,这章不是叫Linux设备驱动程序之中断与时钟,前边你讲了中断,还给了我很多模版,我都看懂了,这次是不是要开始讲时钟了.." "真聪明,越来越 ...

最新文章

  1. 设计模式学习2 工厂模式
  2. FB壕掷千万办换脸视频检测挑战赛,网友:这是帮Deepfake训练鉴别器吗?
  3. 游戏开发攻略—黑杰克扑克牌
  4. 关于DJANGO和JAVASCRIPT的时间
  5. iOS 7开发快速入门
  6. Fiddler插件开发 - 实现网站离线浏览功能
  7. Hibernate随机获取指定范围内的指定条目的记录
  8. C++学习之路 | PTA乙级—— 1047 编程团体赛 (20 分)(精简)
  9. Java 开发者每天都在做什么?
  10. java xml收文转对象_Springmvc发送json数据转Java对象接收
  11. python根据方程的y求x_用Python解方程
  12. Linux x86架构下ACPI PNP Hardware ID的识别机制
  13. 《东周列国志》第七十二回 棠公尚捐躯奔父难 伍子胥微服过昭关
  14. PCA9685与 NXP1768单片机iic通信,扩展PWM端口。已调试成功。
  15. Matlab利用textread或者textscan读取格式化txt文件
  16. 下面不属于电子计算机外存储器的是,2013年计算机专转本模拟题三答案
  17. 【MicroPython ESP32】ssd1306驱动0.96“I2C屏幕cube3D图形显示
  18. 下着雨的星期天下午,年素清一个人走在外面
  19. 【英语六级】【仔细阅读】(1)
  20. 选择器优先级如何排列?

热门文章

  1. .San(三). Xia(峡).近一个月调度过程图解简介
  2. 通过Git同步Obsidian与IOS
  3. Filter的过滤器链
  4. 双系统切换到Ubuntu后,显示器不能扩展的问题
  5. Android刷机脚本——updater-script
  6. 如何精准化的做微信公众号运营?
  7. java tcp 乱码_Java和C++通过Socket通信中文乱码的解决
  8. 绝对值编码器常见的故障有哪些 如何处理
  9. Android 仿美团大众字母索引实现
  10. 永磁同步电机驱动视频教程_矢量控制_手把手教你写代码_无感FOC_有感FOC_状态观测器_卡尔曼滤波_慧驱动