linux内核的中断机制,linux内核中断机制

本文详细解析了中断的分类,包括外部和内部、可屏蔽与不可屏蔽,以及向量和非向量中断。介绍中断处理流程和中断处理程序的顶半部与底半部设计。重点讲解了tasklet和workqueue的使用,以及共享中断和工作队列在Linux中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中断的分类:

来源:外部,内部

是否可屏蔽:可屏蔽中断,不可屏蔽中断

按中断入口跳转方法的不同:向量和非向量

申请和释放IRQ:

int request_irq(unsigned int irq,//要申请的中断号

irqreturn t(*handler)(int void *,struct pt_regs *),//要安装的中断处理函数指针

unsigned long flags,//0是普通外部中断,SA_INTERRUPT快速中断,SA_SHIRQ共享中断

const char *dev_name,

void *dev_id);//用于共享的中断数据线。用来唯一的表示设备

禁止/使能单个中断

为什么将中断处理程序分成顶半部和底半部?

需求:

不能使中断阻塞的时间过长

完成耗时的任务

解决方案:

顶半部,让中断阻塞尽可能短

底半部,完成耗时的任务

顶半部:

实际响应中断的例程

用request_irq注册的中断例程

在很短的时间内完成

底半部:   (不会被执行,而是被调度)

被顶半部调度,并在稍后更安全的时间内执行的例程

实现的机制:

实现底半部的机制:tasklet(任务队列),work queue(工作队列)

二者的区别:tasklet(速度快,优先选择,原子操作,运行于中断上下文)

work queue(高延迟,允许休眠,运行于进程上下文)

void free_irq(unsigned int irq,void *dev_id);//释放

中断处理流程:

产生->跳转到中断向量表入口地址(arch/arm/kernel/entry-armv.S)->asm_do_IRQ(){arch/arm/lkernel/irq.c根据irq编号从已申请的中断(通过request_irq)找到该irq编号对应的中断处理函数}->执行对应的中断 处理函数{执行你自己的中断处理函数}->返回到asm_do_IRQ()->返回到entry-armv.S(恢复到终端前的上下文状态,结束中断处理,继续执行中断发生前的程序)

tasklet的使用:

声明tasklet

declare_tasklet(name,function,data);

name:tasklet的名字

function:执行tasklet时调用的函数

data:一个用来传递给tasklet函数的unsigned long 类型的值

例:DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0)

调度tasklet:

void tasklet_shedule(struct tasklet_struct *t)

创建新的工作队列:

//一个工作队列可对应多个内核线程

struct workqueue_struct *create_workqueue(const char *name)

//一个工作队列对应单个线程

struct workqueue_struct *create_singlethread_workqueue(const char *name);

向工作队列提交任务,首先填充一个work_struct结构

DECLARE_WORK(name,void (*function)(void *),void *data);//申明

init_WORK(struct work_struct *work,void (*function)(void *),void *data);

//不会初始化用来将work_struct结构连接到工作 队列的指针,用于任务已经提交,只是修改了任务时使用PREPARE_WORK

PREPARE_WORK(struct work_struct *work,void (*function)(void *),void *data)

提交任务:

int queue_work(struct workqueue_struct *queue,struct work_struct *work);

//实际的工作至少会在经过指定的jiffies(由delay指定)之后才会被执行

int queue_delayed_work(struct workqueue_struct *queue,struct work_struct *work,unsigned long delay);

取消某个队列的入口项:

int cancel_delayed_work(struct work_struct *work);

!=0;内核会确保不会初始化给定入口项的执行

==0;则说明该入口项已经在其他处理器上运行,因此在cancel_delayed_work返回后可能仍在运行。

为了,更新,所有要强制刷新工作队列

void flush_workqueue(struct workqueue_struct *queue);

销毁工作队列

void destroy_workqueue(struct workqueue_struct *queue);

共享工作队列:

1.在许多情况下,驱动不需要有自己的工作队列,只是偶尔向工作队列添加任务

2.使用内核提供的共享的默认工作队列

3。不应该长期独占该队列,即不能长时间休眠

4.任务可能需要更长时间延迟才能获得处理器时间

//初始化

INIT_WORK(struct work_struct *work,void(*function)(void *),void *data);

//调度

INT schedule_work(struct work_struct *work);

int schedule_delayed_work(struct work_struct *work,unsigned long delay);

//取消共享工作队列的一个入口项(一个工作任务 work)

int cancel_delayed_work(struct work_struct *work);

//刷新共享工作队列

void flush_scheduled_work(void);

中断共享:

linux中断共享:多个设备使用同一个中短线号,同一个中断设备线号的所有处理程序连接成一个链表。

共享中断的多个设备在申请 中断时都应该使用SA_SHIRQ标志

设备结构指针可以作为request_irq(...,void *dev_id)的最后一个参数dev_id传入,dev_id这个参数必须是唯一的,用来表示一个唯一的设备。

在中断到来时,对应链表的所有共享该中断的处理程序都会被执行,它们会测dev_id参数信息,并根据硬件中断寄存器中的信息判断是否是本设备的中断,如果不是,应迅速返回,如果是,则处理完成,如果链表中没有一个是,则说明出现错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值