setup_irq与request_irq的区别

来源:互联网 发布:访客网络账号密码 编辑:程序博客网 时间:2024/05/01 20:54

setup_irq与request_irq的区别

题记:

   前两天在做i.mx28硬件定时器的驱动,参考了BSP里带的,使用TIMER0作为系统时钟的注册代码,发现其注册时钟中断的函数使用的是setup_irq,而非常用的request_irq,觉得有些奇怪,今天网上搜了一下,大概了解了一下原因。其实,request_irq最终调用的中断注册函数也是setup_irq,只是request_irq函数中还使用了kmalloc,而kmalloc又要用到slab分配单元,但系统时钟的注册是在slab初始化之前执行的,所以在系统时钟注册阶段不能使用request_irq,而使用了setup_irq,以下为网络文章原文(出处http://apps.hi.baidu.com/share/detail/421310):

Linux 内核提供了两个注册中断处理函数的接口:setup_irq和request_irq。这两个函数都定义在kernel/irq/manage.c里。

这两个函数有什么样的区别呢?

先看看setup_irq

Setup_irq通常用在系统时钟(GP Timer)驱动里,注册系统时钟驱动的中断处理函数。

下面举个列子, 如s3c2410 timer驱动:

/* arch/arm/mach-s3c2410/time.c */

static struct irqaction s3c2410_timer_irq = {

       .name          = "S3C2410 Timer Tick",

       .flags            = IRQF_DISABLED | IRQF_TIMER,

       .handler       = s3c2410_timer_interrupt,

};

static void __init s3c2410_timer_init (void)

{

       s3c2410_timer_setup();

       setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);

}

struct sys_timer s3c24xx_timer = {

       .init        = s3c2410_timer_init,

       .offset           = s3c2410_gettimeoffset,

       .resume        = s3c2410_timer_setup

};

struct sys_timer s3c24xx_timer = {

       .init        = s3c2410_timer_init,

       .offset           = s3c2410_gettimeoffset,

       .resume        = s3c2410_timer_setup

};

可以看到,setup_irq的使用流程很简单。首先定义s3c2410 timer驱动的irqaction结构体,该结构体用于描述timer中断的基本属性包括中断名、类别以及该中断handler等。然后通过setup_irq函数将timer的irqaction注册进内核。其中,IRQ_TIMER4为s3c2410 timer的中断号。

再看看request_irq

request_irq源码如下:

/* kernel/irq/manage.c */

int request_irq(unsigned int irq,

              irqreturn_t (*handler)(int, void *, struct pt_regs *),

              unsigned long irqflags, const char *devname, void *dev_id)

{

       struct irqaction *action;

       int retval;

#ifdef CONFIG_LOCKDEP

       /*

        * Lockdep wants atomic interrupt handlers:

        */

       irqflags |= SA_INTERRUPT;

#endif

       /*

        * Sanity-check: shared interrupts must pass in a real dev-ID,

        * otherwise we'll have trouble later trying to figure out

        * which interrupt is which (messes up the interrupt freeing

        * logic etc).

        */

       if ((irqflags & IRQF_SHARED) && !dev_id)   /* 使用共享中断但没有提供非NULL的dev_id则返回错误 */

              return -EINVAL;

       if (irq >= NR_IRQS)            /* 中断号超出最大值 */

              return -EINVAL;

       if (irq_desc[irq].status & IRQ_NOREQUEST) /* 该中断号已被使用并且未共享 */

              return -EINVAL;

       if (!handler)

              return -EINVAL;

       action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);     /* 动态创建一个irqaction */

       if (!action)

              return -ENOMEM;

/* 下面几行是根据request_irq 传进来的参数对irqaction结构体赋值 */

       action->handler = handler;  

       action->flags = irqflags;

       cpus_clear(action->mask);

       action->name = devname;

       action->next = NULL;

       action->dev_id = dev_id;

       select_smp_affinity(irq);

       retval = setup_irq(irq, action);      /* 调用setup_irq注册该中断的irqaction结构体 */

       if (retval)

              kfree(action);

       return retval;

}

由上可以看出,request_irq的大致流程为先对申请的中断线进行安全检测,然后根据request_irq传进来的参数,动态创建该中断对应的irqaction结构体,最后通过setup_irq函数将该irqaction注册进内核适当的位置。

这两个函数的使用流程搞清楚了,那么两者之间的联系也就清楚了:

1) Request_irq的注册过程包含setup_irq,最终是调用setup_irq。

2) Request_irq比setup_irq多一套错误检测机制,即kmalloc前面3行if语句。

而Setup_irq通常是直接注册irqaction,并没针对相应中断线进行错误检测,如该irq 线是否已经被占用等。因此setup_irq通常只用在特定的中断线上,如System timer。除系统时钟驱动外,大部份驱动还是通过request_irq注册中断。

这里有个小问题:

既然Request_irq实际上就是包含了setup_irq的注册过程,那系统时钟驱动(GP Timer Driver)中断可以用request_irq来注册吗?

做个小试验, 将s3c2410 timer驱动的setup_irq那行去掉,改为用request_irq注册。

修改后代码如下:

static void __init s3c2410_timer_init (void)

{

       s3c2410_timer_setup();

       //setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);

       request_irq(IRQ_TIMER4, s3c2410_timer_interrupt,

        IRQF_DISABLED | IRQF_TIMER, "S3C2410 Timer Tick", NULL);

}

编译运行。

结果:内核挂掉

为什么呢?很明显,系统时钟驱动中断不能用request_irq注册,大致搜了一下源码也发现,看到其他平台相关的时钟驱动中断部分都是用的setup_irq注册的。

我们来分析一下原因。

看看request_irq和setup_irq 还有哪些细节不一样?

仔细观察后注意到request_irq内有这么一行代码:

action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);

作用为动态创建一个irqaction。

Kmalloc实际上也是使用的slab机制进行分配的。源码如下:

/* include/linux/slab.h */

static inline void *kmalloc(size_t size, gfp_t flags)

{

       if (__builtin_constant_p(size)) {

              int i = 0;

#define CACHE(x) \

              if (size <= x) \

                     goto found; \

              else \

                     i++;

#include "kmalloc_sizes.h"

#undef CACHE

              {

                     extern void __you_cannot_kmalloc_that_much(void);

                     __you_cannot_kmalloc_that_much();

              }

found:

              return kmem_cache_alloc((flags & GFP_DMA) ?

                     malloc_sizes[i].cs_dmacachep :

                     malloc_sizes[i].cs_cachep, flags);

       }

       return __kmalloc(size, flags);

}

使用slab机制分配内存必须先对slab进行初始化,包括mem_init和kmem_cache_init。

看看kernel的初始化流程:

/* init/main.c */

asmlinkage void __init start_kernel(void)

{

       ……

       time_init();

       ……

       vfs_caches_init_early();

       cpuset_init_early();

       mem_init(); ß------ initializes the memory data structures

       kmem_cache_init(); ß---- set up the general caches

       ……

}

Time_init函数在mem_init和kmem_cache_init之前被调用,而time_init会调用体系结构相关部分系统时钟驱动的初始化函数。拿s3c2410的例子来说,time_init最终会调用s3c2410_timer_init函数,进行s3c2410时钟驱动的初始化和注册中断处理函数。

具体过程如下:

time_init函数定义在arch/arm/kernel/time.c内:

void __init time_init(void)

{

#ifndef CONFIG_GENERIC_TIME

       if (system_timer->offset == NULL)

              system_timer->offset = dummy_gettimeoffset;

#endif

       system_timer->init(); ß-这行实际执行的就是s3c2410_timer_init

#ifdef CONFIG_NO_IDLE_HZ

       if (system_timer->dyn_tick)

              system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;

#endif

}

system_timer在setup_arch(arch/arm/kernel/setup.c)内通过map_desc机制被初始化为s3c24xx_timer. 如上面s3c2410时钟驱动代码所示,s3c24xx_timer的init成员即指向s3c2410_timer_init函数。

现在我们搞清楚了,我们大概的估计是系统时钟驱动(GP Timer Driver)的中断处理函数不能用request_irq注册是因为request_irq内会调用kmalloc动态分配内存创建timer的irqaction结构体。而kmalloc也是使用的slab内存分配机制,使用kmalloc前必须先对kernel的slab以及mem data structure进行初始化。而这部分初始化工作是在系统时钟驱动初始化之后才进行的,所以造成kmalloc失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 7个月宝宝大便干燥怎么办 11个月宝宝大便干燥怎么办 9个月宝宝大便干燥怎么办 10个月宝宝大便干燥怎么办 宝宝两天没拉粑粑了怎么办 小学闺蜜嫉妒我怎么办 三年级孩子对应用题理解很差怎么办 我嫉妒我的朋友怎么办 嫉妒同学比我好怎么办 宝宝生在家里了怎么办 在家不小心生了怎么办 葫芦干了有黑点怎么办 单位费用发票丢失了怎么办 1岁宝宝太调皮了怎么办 孩子和同学打架家长该怎么办 孩子被大人打了怎么办 小孩不跟大人沟通怎么办 2岁小朋友爱动手怎么办 小班爱动手的小朋友怎么办 宝宝对小朋友不友好爱动手怎么办 小孩子上幼儿园爱打人怎么办 小孩被别人打了怎么办 1岁幼儿爱打人怎么办 作为幼小朋友打人老师怎么办 2岁半小朋友喜欢打人怎么办 2岁宝宝脾气大怎么办 4月小孩爱动怎么办 一岁宝宝老打人怎么办 1岁宝宝爱打人怎么办 3岁宝宝喜欢抓人怎么办 宝宝喜欢打人怎么办2岁 1岁宝宝动手打人怎么办 孩子总打人总哭怎么办 小孩出现夜惊家人怎么办 小孩不原跟家人沟通怎么办 孩子字写得难看怎么办 孩子上一年级不认识字怎么办 二年级孩子语文差怎么办 孩子二年级语文成绩差怎么办 孩子小学二年级语文差怎么办 二年级孩子语文理解能力差怎么办