内核中的中断函数request_irq()

本文详细介绍了Linux内核中的中断函数request_irq及其参数意义,包括如何申请和释放中断,以及不同标志位的作用,例如快速处理程序和慢速处理程序的区别。

http://blog.youkuaiyun.com/lhf_tiger/article/details/7173844

内核中的中断函数request_irq()

转载 2012年01月04日 02:01:47


request_irq()、free_irq()
这是驱动程序申请中断和释放中断的调用。在include/linux/sched.h里声明。
request_irq()调用的定义:
int request_irq(unsigned int irq,
void (*handler)(int irq, void *dev_id, struct pt_regs *regs
),
unsigned long irqflags,
const char * devname,
void *dev_id);
irq是要申请的硬件中断号。在Intel平台,范围0--15。handler是向系统登记的中断处理函数。这是一个回调函数,中断发生时,系统调用这个函数,传入的参数包括硬件中断号,device id,寄存器值。dev_id就是下面的request_irq时传递给系统的参数dev_id。irqflags是中断处理的一些属性。比较重要的有SA_INTERRUPT,

标明中断处理程序是快速处理程序(设置SA_INTERRUPT)还是慢速处理程序(不设置SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断。慢速处理程序不屏蔽。还有一个SA_SHIRQ属性,设置了以后运行多个设备共享中断。dev_id在中断共享时会用到。一般设置为这个设备的 device结构本身或者NULL。中断处理程序可以用dev_id找到相应的控制这个中断的设备,或者用irq2dev_map找到中断对应的设备。
void free_irq(unsigned int irq,void *dev_id); 

 

FE:

request_irq(INT_HA2608_PORT, s3c_keypad_isr, IRQF_SAMPLE_RANDOM, DEVICE_NAME, (void *) pdev);

   1.GPIO口,2.中断处理函数,3系统define的一个值,表面触发中断的方式,上升沿啊,高电平啊,下降沿啊等等  4,设备名字,5 一个设备的ID(不太确定)





<think>我们有两个问题需要解决:1.在回调函数中是否可以调用request_irq申请中断?2.如何避免使用kmalloc而改用全局变量?参考引用[4]:request_irq的声明为:inlineint__must_checkrequest_irq(unsignedintirq,irq_handler_thandler,unsignedlongflags,constchar*name,void*dev);引用[1]中提到:request_irq可能会睡眠,不能在中断上下文或不允许阻塞的代码调用。回调函数可能是运行在中断上下文,也可能是进程上下文,这取决于回调函数的调用环境。因此,我们需要区分情况:1.如果回调函数运行在中断上下文(即中断处理程序、上半部):由于request_irq可能会睡眠(例如,在分配内存时可能调用kmalloc,而kmalloc(GFP_KERNEL)在内存不足时可能睡眠),而中断上下文不允许睡眠(因为中断上下文没有进程上下文,无法被重新调度),因此绝对不能在中段上下文中调用request_irq。2.如果回调函数运行在进程上下文(例如,一个内核线程的回调、一个系统调用的回调等):此时可以调用request_irq,因为进程上下文允许睡眠。所以,在回调函数中调用request_irq的条件是:该回调函数必须运行在进程上下文。另外,关于避免使用kmalloc而采用全局变量:在request_irq函数的参数中,有一个`void*dev`,这个参数是设备标识,通常用于在共享中断时区分不同的设备。这个参数在中断处理函数中会被传入。如果我们使用全局变量,那么我们可以将这个全局变量的地址作为`dev`参数传递。但是,需要注意的是,这个`dev`参数也可以是一个整数(比如设备编号)或者一个指向设备结构体的指针。如果我们不使用kmalloc动态分配设备结构体,我们可以使用一个全局的静态结构体。这样,在注册中断时,我们可以传递这个全局变量的地址。但是,如果我们需要多个设备实例(比如多个相同设备),那么使用全局变量就不合适了,因为每个设备都需要自己的数据结构。在这种情况下,全局变量只能用于一个设备。对于多个设备,我们可能需要静态分配一个数组。因此,我们可以总结如下:1.在回调函数中调用request_irq:-必须确保回调函数在进程上下文中执行(例如,在系统调用、内核线程、工作队列等中)。-不能在中断上下文(例如,中断处理函数)中调用。2.使用全局变量代替kmalloc:-我们可以定义一个全局结构体(或数组)来保存设备信息。-在注册中断时,将全局结构体的地址(或数组中对应元素的地址)作为`dev`参数传递给request_irq。-在中断处理函数中,通过`dev`参数可以访问这个全局变量。示例代码:假设我们有一个设备,我们使用一个全局变量来保存设备信息:```c#include<linux/interrupt.h>#include<linux/module.h>//全局设备结构体staticstructmy_device{intirq;char*name;//其他设备特定数据}my_dev;//中断处理函数staticirq_handler_tmy_irq_handler(intirq,void*dev){structmy_device*dev_data=(structmy_device*)dev;printk("Interruptoccurredondevice%s\n",dev_data->name);returnIRQ_HANDLED;}//回调函数(假设在进程上下文中调用)staticintmy_callback(void){intret;//初始化全局变量my_dev.irq=10;//假设中断号10my_dev.name="my_device";//注册中断ret=request_irq(my_dev.irq,my_irq_handler,IRQF_SHARED,"my_irq",&my_dev);if(ret){printk("FailedtoregisterIRQ\n");returnret;}return0;}//模块初始化staticint__initmy_module_init(void){//假设我们在模块初始化时调用回调函数(进程上下文)returnmy_callback();}//模块退出staticvoid__exitmy_module_exit(void){free_irq(my_dev.irq,&my_dev);}module_init(my_module_init);module_exit(my_module_exit);```注意事项:-如果多个设备共享同一个中断,并且我们为每个设备都使用全局变量,那么我们可以定义一个全局数组,然后分别注册,传递数组元素的地址。-如果中断是共享的,那么在注册中断时必须指定IRQF_SHARED标志,并且每个设备的中断处理函数必须能够通过dev参数识别自己的中断。因此,回答用户的问题:1.在回调函数中可以调用request_irq,但必须确保回调函数在进程上下文中。2.可以使用全局变量(静态分配)来避免kmalloc,将全局变量的地址作为request_irq的dev参数传递。但是,请注意:全局变量在多设备实例的情况下可能不够灵活,通常更推荐使用动态分配(kmalloc)来为每个设备实例分配独立的内存,以避免设备之间的干扰。如果确实需要避免动态分配,可以使用静态数组来支持多个设备。另外,引用[3]中展示了一个中断处理函数的例子,其中在中断处理函数中调度了一个工作队列(schedule_work)。这个例子中,中断处理函数只是触发了一个工作,而实际的工作在工作队列中处理。这种模式很常见,因为中断处理函数要求快速执行,不能睡眠,而工作队列运行在进程上下文,可以执行更复杂的操作(包括可能调用request_irq,但通常不会在中断处理函数中重新注册中断)。因此,如果我们的回调函数是在中断上下文中被调用的,而我们又需要注册中断,那么我们可以考虑在工作队列中注册中断(因为工作队列在进程上下文运行)。例如:```c//全局变量定义staticstructmy_devicemy_dev;//工作队列处理函数staticvoidmy_work_handler(structwork_struct*work){//在工作队列中注册中断(进程上下文)intret=request_irq(my_dev.irq,my_irq_handler,IRQF_SHARED,"my_irq",&my_dev);//...错误处理}//声明工作队列staticDECLARE_WORK(my_work,my_work_handler);//中断处理函数(用于另一个中断,触发工作队列)staticirq_handler_ttrigger_irq_handler(intirq,void*dev){schedule_work(&my_work);returnIRQ_HANDLED;}```这样,我们就在中断处理函数中调度了一个工作,然后在工作队列中安全地调用request_irq。总结:1.回调函数中调用request_irq:允许,但必须在进程上下文。2.避免kmalloc:使用全局变量(静态分配),并将全局变量的地址作为dev参数传递。根据用户的要求,我们使用全局变量,并确保在进程上下文中调用request_irq。</think>### Linux内核回调函数中使用request_irq申请中断的完整指南 #### 1. **回调函数中调用request_irq的可行性分析** 在Linux内核编程中,**回调函数中是否可以调用request_irq取决于回调函数的执行上下文**: - **进程上下文(允许调用)**: 若回调函数运行在进程上下文(如系统调用、内核线程、工作队列处理函数): ```c static void process_context_callback(void) { // 允许调用request_irq request_irq(irq, handler, flags, name, dev); } ``` 因为`request_irq`可能调用`kmalloc(GFP_KERNEL)`,在内存不足时可能睡眠[^1]。 - **中断上下文(禁止调用)**: 若回调函数中断上下文执行(如硬件中断处理函数): ```c static irqreturn_t irq_handler(int irq, void *dev_id) { // 禁止在此调用request_irq! return IRQ_HANDLED; } ``` 中断上下文**禁止任何可能睡眠的操作**,违反将导致内核崩溃[^1]。 #### 2. **使用全局变量替代kmalloc的实现方案** 完全避免动态内存分配,采用全局变量结构体: ```c // 全局中断配置结构体 static struct my_irq_config { unsigned int irq_num; // 中断irq_handler_t handler; // 中断处理函数 unsigned long flags; // 中断标志 char name[16]; // 中断名称 void *dev_id; // 设备标识 } global_irq_cfg = { .irq_num = 10, // 示例IRQ 10 .handler = my_irq_handler, .flags = IRQF_SHARED, .name = "my_global_irq", .dev_id = NULL }; // 注册中断函数(在进程上下文调用) int register_global_irq(void) { return request_irq( global_irq_cfg.irq_num, global_irq_cfg.handler, global_irq_cfg.flags, global_irq_cfg.name, global_irq_cfg.dev_id ); } ``` #### 3. **关键实现细节与注意事项** 1. **中断处理函数实现**: ```c static irqreturn_t my_irq_handler(int irq, void *dev_id) { printk("IRQ %d triggered from global config\n", irq); // 可调度工作队列处理耗时操作[^3] schedule_work(&my_work); return IRQ_HANDLED; } ``` 2. **安全访问全局变量**: ```c static DEFINE_SPINLOCK(irq_lock); // 声明自旋锁 void update_irq_config(unsigned int new_irq) { spin_lock(&irq_lock); global_irq_cfg.irq_num = new_irq; // 安全更新 spin_unlock(&irq_lock); } ``` 3. **模块初始化和清理**: ```c static int __init my_module_init(void) { if (register_global_irq()) { printk("IRQ registration failed\n"); return -EBUSY; } return 0; } static void __exit my_module_exit(void) { free_irq(global_irq_cfg.irq_num, global_irq_cfg.dev_id); } ``` #### 4. **不同场景下的最佳实践** | **场景** | **推荐方案** | **代码示例** | |------------------------|--------------------------------------|----------------------------------| | 单一设备中断 | 直接使用全局结构体 | `global_irq_cfg` | | 多设备共享中断 | 全局结构体数组 | `struct my_irq_cfg irq_array[5]` | | 中断参数需要动态更新 | 自旋锁保护 + 刷新工作队列 | `flush_work()` + 更新后重注册 | | 中断处理耗时操作 | 在中断处理函数中调度工作队列[^3] | `schedule_work(&work)` | #### 5. **风险规避策略** 1. **中断号冲突检测**: ```c if (irq_is_allocated(global_irq_cfg.irq_num)) { printk("IRQ %d already in use!\n", global_irq_cfg.irq_num); return -EBUSY; } ``` 2. **防止重复注册**: ```c static bool irq_registered = false; // 注册状态标志 if (!irq_registered) { ret = request_irq(...); irq_registered = (ret == 0); } ``` 3. **NUMA系统优化**: ```c // 指定CPU亲和性 irq_set_affinity(global_irq_cfg.irq_num, cpumask_of(0)); ``` > **关键提醒**:全局变量方案仅适用于**中断配置固定或低频更新**的场景。高频动态场景仍需动态分配[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值