Request_irq和setup_irq的区别

原创 2012年03月26日 11:51:25

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

 

 

/*

 * Internal function to register an irqaction - typically used to

 * allocate special interrupts that are part of the architecture.

 */

int setup_irq(unsigned int irq, struct irqaction *new);

 

/*

 *    request_irq - allocate an interrupt line

*    This call allocates interrupt resources and enables the

 *    interrupt line and IRQ handling.

*/

int request_irq(unsigned int irq,

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

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

 

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

 

先看看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

};

可以看到,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)   /* 使用共享中断但没有提供非NULLdev_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失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。


Request_irq和setup_irq的区别

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

一步一步学习 Linux 驱动之内核中断函数 request_irq

request_irq的作用是申请使用IRQ并注册中断处理程序。 request_irq()函数的原型如下: /* kernel/irq/manage.c */ int request_irq( un...

深入分析request_irq的dev_id参数作用 转

0-11-03 23:22 深入分析request_irq的dev_id参数作用   Author : Dongas Data : 08-07-12   注:若对kernel中断处理模型不是很清楚的...

由request_irq()函数引发对中断的理解

1、  中断的理解 中断你可以理解为就是一种电信号,是由硬件设备产生的然后发送给处理器,处理器接收到中断后,就会马上向操作系统反映此信号,之后就是系统的工作了。 这里有两个...

Linux中断关于 request_threaded_irq

--------------------------------------------------------------------- 1. 重要接口 内核维护了一个中断信号线的注册表,该注册...
  • lpplou
  • lpplou
  • 2012年06月18日 09:08
  • 3723

request_irq() --注册中断

在 2.4 内核和 2.6内核中都使用 request_irq() 函数来注册中断服务函数。在 2.4 内核中,需要包含的头文件是 #include  ,2.6 内核中需要包含的头文件则是#inclu...

(转)深入分析request_irq的dev_id参数作用

  深入分析request_irq的dev_id参数作用  深入分析request_irq的dev_id参数作用 Author : DongasData : 08-07-12 注:若对kernel中断...

2.6下用request_irq引起的问题

原文地址:http://blog.sina.com.cn/s/blog_637deacd0106dfmv.html 奇怪的是在2.6用request_irq时发现好几个问题 我引入的头文件如下: ...

深入分析request_irq的dev_id参数作用

深入分析request_irq的dev_id参数作用   Author : Dongas Data : 08-07-12   注:若对kernel中断处理模型不是很清楚的话(如:irqa...

“passing argument 2 0f “request_irq” from incompatible pointer type”导致中断申请失败和模块无法加载

今天在些key的driver的时候。。。写完了编译出现一个warmming如下: warning: passing argument 2 of 'request_irq' from incompat...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Request_irq和setup_irq的区别
举报原因:
原因补充:

(最多只允许输入30个字)