10.3 Linux中断编程

一、Linux中断处理机制

中断会打破内核进程正常运行的程序的调度,因此对于系统来说中断中执行的程序需要尽量短小。在Linux系统中,为了在中断执行时间和中断处理程序量之间找到一个平衡,引入了Top HalfBottom Half.即顶半部和底半部。

        顶半部:主要完成尽量少的紧急功能,比如紧急处理寄存器的中断状态位
        底半部:执行剩余中断中需要完成的事情。

二、Linux中断编程

Linux中的中断编程和MCU不一样,不需要你一个个去配置相关的寄存器,只需要使用Linux内核提供给你的API函数。

1、关于中断编程的API函数

① 申请中断irq

static inline int __must_check request_irq(unsigned int irq, 
        irq_handler_t handler, unsigned long flags,const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

        irq : 要申请的硬件中断号
        handler: 向内核注册的中断处理函数,相当于中断处理机制中的顶半部
        flags: 中断的触发方式
        name: 中断名字
        dev: 传递给中断服务函数的私有数据,一般设置为设备结构体,如果flags设置的是共享中断,则用来区分不同的中断
        返回值:返回0表示申请成功,-EINVAL表示中断号无效或中断处理函数为NULL,-EBUSY表示中断已经被占用

static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
		 unsigned long irqflags, const char *devname, void *dev_id)
{
	return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
					 devname, dev_id);
}

         利用此函数申请中断,不需要在出错处理和驱动出口函数中显式的释放中断。

②释放irq

void free_irq(unsigned int irq, void *dev_id)

        此函数和request_irq相对应。
        irq: 要释放的硬件中断号
        dev_id: 如果释放的中断时共享中断的话,则dev_id用来区分具体的中断

③ 使能和屏蔽中断

void disable_irq_nosync(unsigned int irq)
void disable_irq(unsigned int irq)
void enable_irq(unsigned int irq)

       从名字就能看出函数的意思,disable_irq_nosyn 屏蔽的中断会立刻返回,而disable_irq 屏蔽的中断会等待中断执行完成再返回。比如在3号中断中执行disable_irq(3)则会造成死锁。

三、 底半部机制

        Linux中实现底半部机制的主要由tasklet、workqueue、softirq和线程化irq.

① tasklet

        tasklet使用方法简单主要分为3个步骤

        定义一个tasklet ----> 将定义的tasklet和底半部需要执行的函数关联---->在需要调度的地方进行调度。

        使用模板:

/* 定义 taselet */
struct tasklet_struct testtasklet;
/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
/* tasklet 具体处理内容 */
}

/*将tasklet和函数关联*/
DECLARE_TASKLET(testtasklet, testtasklet_func, 0;

/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 tasklet */
tasklet_schedule(&testtasklet);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 tasklet */
tasklet_init(&testtasklet, testtasklet_func, data);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

②workqueue

        workque使用的方法也和tasklet很相似,区别在于workqueue执行的上下文是内核线程,可以调度和睡眠。

        定义一个workqueue---->将定义的worqueue和底半部执行函数绑定---->在需要调度的地方进行调度。

        使用模板:

/* 定义工作(work) */
struct work_struct testwork;
/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work 具体处理内容 */
}


/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 work */
schedule_work(&testwork);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 work */
INIT_WORK(&testwork, testwork_func_t);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

 ③ softirq

        软中断softirq和tasklet运行于软中断上下文,是一个原子上下文,因此不允许引起睡眠和调度。

 ④ 线程化irq

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)

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)

        通过这两个API函数向内核申请的中断,内核会为相应的中断号分配一个内核线程,handler参数对应的函数会执行在中断上下文,thread_fn参数对应的函数会执行于内核线程 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值