您的位置:首页 > 产品设计 > UI/UE

Request_irq和setup_irq的区别

2013-01-31 20:40 197 查看
Linux 内核提供了两个注册中断处理函数的接口:setup_irq和request_irq。这两个函数都定义在kernel/irq/manage.c里。这两个函数有什么样的区别呢?
(1)Setup_irq,Setup_irq通常用在系统时钟(GP Timer)驱动里,注册系统时钟驱动的中断处理函数。下面举个列子,如s3c2410 timer驱动结构体定义在Time.c (arch/arm/plat-s3c)

static struct irqaction s3c2410_timer_irq = {

.name = "S3C2410 Timer Tick",

.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,

.handler = s3c2410_timer_interrupt,

};

然后再定义中断过程:

static void __init s3c64xx_timer_init(void)

{

s3c64xx_timer_setup();

setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);

}

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

(2)再看看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注册中断。

下面做个小试验,将s3c2410 timer驱动的setup_irq那行去掉,改为用request_irq注册,编译运行。结果:内核挂掉。为什么呢?很明显,系统时钟驱动中断不能用request_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(); //TIMER初始化

……

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会调用体系结构相关部分系统时钟驱动的初始化函数。time_init最终会调用s3c64xx_timer_init函数,进行s3c2410时钟驱动的初始化和注册中断处理函数。现在我们搞清楚了,我们大概的估计是系统时钟驱动(GP Timer Driver)的中断处理函数不能用request_irq注册是因为request_irq内会调用kmalloc动态分配内存创建timer的irqaction结构体。而kmalloc是使用的slab内存分配机制,使用kmalloc前必须先对kernel的slab以及mem
data structure进行初始化。而这部分初始化工作是在系统时钟驱动初始化之后才进行的,所以造成kmalloc失败,从而造成系统时钟驱动的中断未注册成功,进而内核挂掉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: