转自:http://www.cnblogs.com/embedded-tzp/p/4453987.html

欢迎转载,转载时需保留作者信息,谢谢。

邮箱:tangzhongp@163.com

博客园地址:http://www.cnblogs.com/embedded-tzp

Csdn博客地址:http://blog.csdn.net/xiayulewa

1.   概述

Linux内核中断机制:为了在中断执行时间尽可能短和中断处理需要完成大量工作之间找到一个平衡点,Linux将中断处理程序分解为两个半部,顶半部和底半部。

顶半部完成尽可能少的比较紧急的任务,它往往只是简单地读取寄存器中的中断状态并清除中断标志位就进行“登记工作”,将底半部处理程序挂到该设备的底半部执行队列中去。

那上半部和下半部是分界线是什么? 以request_irq注册的中断函数为分界线。

上半部:

当执行完request_irq注册的中断函数后,上半部结束。在注册的中断函数中,登记下半部要做的工作。

上半部已经讨论了:http://www.cnblogs.com/embedded-tzp/p/4451354.html

底半部实现方式有:

软中断

tasklet

工作队列

2.   软中断

http://www.cnblogs.com/embedded-tzp/p/4452041.html中已讨论。

3.   Tasklet

3.1.数据结构

struct tasklet_struct

{

struct tasklet_struct *next;

unsigned long state;

atomic_t count;

void (*func)(unsigned long);

unsigned long data;

};

该结构体由tasklet_init初始化。

Softirq.c (src\kernel)中:

struct tasklet_head

{

    struct tasklet_struct *head;

    struct tasklet_struct **tail;

};

struct tasklet_head  tasklet_vec;

3.2.注册

softirq_init中有:

open_softirq(TASKLET_SOFTIRQ, tasklet_action);

可见其实现方式为软件中断。其处理函数为tasklet_action

tasklet_schedule→__tasklet_schedule

t->next = NULL;

*__this_cpu_read(tasklet_vec.tail) = t;

__this_cpu_write(tasklet_vec.tail, &(t->next));

将tasklet对象组织成一张链表。

3.3.执行流程

tasklet_schedule→__tasklet_schedule→raise_softirq_irqoff(TASKLET_SOFTIRQ);

触发软中断执行,软中断执行流程见http://www.cnblogs.com/embedded-tzp/p/4452041.html

最终软中断会执行到tasklet的处理函数tasklet_action

tasklet_action中是个while,循环处理tasklet_vec链表中的struct   tasklet_struct。

3.4.其它:

* 不允许访问用户空间;?????
* 不允许访问current指针;
* 不能执行休眠或调度。

特征:

* 一个tasklet可被禁用或启用;只用启用的次数和禁用的次数相同时,tasklet才会被执行。
* 和定时器类似,tasklet可以注册自己;
* tasklet可被调度在一般优先级或者高优先级上执行,高优先级总会首先执行;
* 如果系统负荷不重,则tasklet会立即得到执行,且始终不会晚于下一个定时器滴答;
* 一个tasklet可以和其他tasklet并发,但对自身来讲必须严格串行处理,即一个tasklet不会在多个处理器上执行。

3.5.函数集合

DECLARE_TASKLET(name,func,data); /*定义及初始化tasklet*/

DECLARE_TASKLET_DISABLED(name,func,data);      /*定义及初始化后禁止该tasklet*/

void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); /* 初始化tasklet,func指向要执行的函数,data为传递给函数func的参数 */

void tasklet_disable(struct tasklet_struct *t) /*禁用指定tasklet, */

void tasklet_disable_nosync(struct tasklet_struct *t) /*禁用指定tasklet,但不会等待任何正在运行的tasklet退出*/

void tasklet_enable(struct tasklet_struct *t)      /*启用先前被禁用的tasklet*/

void tasklet_schedule(struct tasklet_struct *t)    /*注册并调度执行指定的tasklet*/

void tasklet_hi_schedule(struct tasklet_struct *t) /*调度指定的tasklet以高优先级执行*/

void tasklet_kill(struct tasklet_struct *t)   /*移除指定tasklet*/

3.6.    实例

#include <linux/init.h>

#include <linux/module.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>

/*硬件相关的数据结构*/

struct btn_resource {

int irq;  //中断号

char *name; //中断名称

};

//初始化板卡按键信息

static struct btn_resource btn_info[] = {

] = {

),

.name = "KEY_UP"

},

] = {

),

.name = "KEY_DOWN"

}

};

//tasklet的处理函数,在tasklet_action函数中被处理

static void btn_tasklet_func(unsigned long data)

{

struct btn_resource *pdata = (struct btn_resource *)data;

printk("%s: irq = %d, name = %s\n",

__func__, pdata->irq, pdata->name);

}

//定义tasklet对象

static DECLARE_TASKLET(btn_tasklet,

btn_tasklet_func]);

//中断处理函数

//irq:中断号,dev_id:保存注册中断时传递的参数信息

static irqreturn_t button_isr(int irq, void *dev_id)

{

//登记底半部信息

tasklet_schedule(&btn_tasklet); //注册并调度tasklet,CPU会在空闲时执行tasklet的处理函数

printk("%s\n", __func__);

return IRQ_HANDLED; //成功,失败:IRQ_NONE

}

static int btn_init(void)

{

//申请中断和注册中断处理程序

/*

* IRQ_EINT(0):中断号

* button_isr:中断处理程序,中断发生以后,内核执行此函数

* IRQF*:外部中断的触发方式,内部中断此参数为0

* KEY_UP:中断名称,出现在 cat /proc/interrupts

* &mydata:给中断处理程序传递的参数信息,不传递参数指定为NULL

*/

int i;

; i < ARRAY_SIZE(btn_info); i++)

request_irq(btn_info[i].irq, button_isr,

IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

btn_info[i].name, &btn_info[i]);

printk("%s\n", __func__);

;

}

static void btn_exit(void)

{

int i;

; i < ARRAY_SIZE(btn_info); i++)

free_irq(btn_info[i].irq, &btn_info[i]);

printk("%s\n", __func__);

}

module_init(btn_init);

module_exit(btn_exit);

MODULE_LICENSE("GPL");

4.   工作队列

工作队列涉及到linux内核进程调度策略,但调度策略不是此处应该讨论的。

4.1.内核默认队列

4.1.1.   数据结构

struct work_struct

struct delayed_work

初始化队列对象

INIT_WORK(&mywork, my_work_func);

INIT_DELAYED_WORK(&mydwork, my_dwork_func);

4.1.2.   函数集合

schedule_work(&mywork)

schedule_delayed_work(&mydwork, 5*HZ)

4.1.3.   为什么是内核默认队列

Workqueue.c (src\kernel) 中:

init_workqueues→

system_wq = alloc_workqueue("events", 0, 0);

而 schedule_work→

queue_work(system_wq, work);

可见struct work_struct对象是默认放在缺省的内核线程线程events中。当缺省工作队列负载太重,执行效率会很低,需要我们自建队列

4.1.4.   实例

#include <linux/init.h>

#include <linux/module.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>

/*硬件相关的数据结构*/

struct btn_resource {

int irq;  //中断号

char *name; //中断名称

};

//初始化板卡按键信息

static struct btn_resource btn_info[] = {

] = {

),

.name = "KEY_UP"

},

] = {

),

.name = "KEY_DOWN"

}

};

//分配工作和延时工作

static struct work_struct mywork;

//工作队列处理函数

static void my_work_func(struct work_struct *work)

{

printk("%s\n", __func__);

}

//中断处理函数

//irq:中断号,dev_id:保存注册中断时传递的参数信息

static irqreturn_t button_isr(int irq, void *dev_id)

{

//登记工作底半部信息

/*

* schedule_work:登记工作,将工作交给内核默认的工作队列和

* 内核线程去管理和调度执行,cpu会在适当的时候会执行工作的处理函数

* */

schedule_work(&mywork);

printk("%s\n", __func__);

return IRQ_HANDLED; //成功,失败:IRQ_NONE

}

static int btn_init(void)

{

//申请中断和注册中断处理程序

/*

* IRQ_EINT(0):中断号

* button_isr:中断处理程序,中断发生以后,内核执行此函数

* IRQF*:外部中断的触发方式,内部中断此参数为0

* KEY_UP:中断名称,出现在 cat /proc/interrupts

* &mydata:给中断处理程序传递的参数信息,不传递参数指定为NULL

*/

int i;

; i < ARRAY_SIZE(btn_info); i++)

request_irq(btn_info[i].irq, button_isr,

IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

btn_info[i].name, &btn_info[i]);

//初始化工作和延时工作

INIT_WORK(&mywork, my_work_func);

printk("%s\n", __func__);

;

}

static void btn_exit(void)

{

int i;

; i < ARRAY_SIZE(btn_info); i++)

free_irq(btn_info[i].irq, &btn_info[i]);

printk("%s\n", __func__);

}

module_init(btn_init);

module_exit(btn_exit);

MODULE_LICENSE("GPL");

4.2.自建队列

如上讨论,当使用work_struct,默认放在缺省的内核线程线程events中。当缺省工作队列负载太重,执行效率会很低,需要我们自建队列

4.2.1.   数据结构

struct workqueue_struct

4.2.2.   函数集合

wq = create_workqueue("tzp");

queue_work(wq, &mywork);

queue_delayed_work(wq, &mydwork, 3*HZ); // 3秒后执行

destroy_workqueue

4.2.3.   实例

#include <linux/init.h>

#include <linux/module.h>

#include <linux/irq.h>

#include <linux/interrupt.h>

#include <asm/gpio.h>

#include <plat/gpio-cfg.h>

/*硬件相关的数据结构*/

struct btn_resource {

int irq;  //中断号

char *name; //中断名称

};

//初始化板卡按键信息

static struct btn_resource btn_info[] = {

] = {

),

.name = "KEY_UP"

},

] = {

),

.name = "KEY_DOWN"

}

};

//定义工作队列的指针

static struct workqueue_struct *wq;

//分配延时工作

static struct delayed_work mydwork;

//延时工作的处理函数

static void my_dwork_func(struct work_struct *work)

{

printk("%s\n", __func__);

}

//中断处理函数

//irq:中断号,dev_id:保存注册中断时传递的参数信息

static irqreturn_t button_isr(int irq, void *dev_id)

{

//登记关联自己的工作队列和工作

queue_delayed_work*HZ);

printk("%s\n", __func__);

return IRQ_HANDLED; //成功,失败:IRQ_NONE

}

static int btn_init(void)

{

//申请中断和注册中断处理程序

/*

* IRQ_EINT(0):中断号

* button_isr:中断处理程序,中断发生以后,内核执行此函数

* IRQF*:外部中断的触发方式,内部中断此参数为0

* KEY_UP:中断名称,出现在 cat /proc/interrupts

* &mydata:给中断处理程序传递的参数信息,不传递参数指定为NULL

*/

int i;

; i < ARRAY_SIZE(btn_info); i++)

request_irq(btn_info[i].irq, button_isr,

IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

btn_info[i].name, &btn_info[i]);

//初始化延时工作

INIT_DELAYED_WORK(&mydwork, my_dwork_func);

//创建自己的工作队列和内核线程

wq = create_workqueue("tzp"); //线程名叫tzp

printk("%s\n", __func__);

;

}

static void btn_exit(void)

{

int i;

//销毁自己的工作队列和线程

destroy_workqueue(wq);

; i < ARRAY_SIZE(btn_info); i++)

free_irq(btn_info[i].irq, &btn_info[i]);

printk("%s\n", __func__);

}

module_init(btn_init);

module_exit(btn_exit);

MODULE_LICENSE("GPL");

【linux kernel】 中断处理-中断下半部【转】的更多相关文章

  1. Linux kernel的中断子系统之(六):ARM中断处理过程

    返回目录:<ARM-Linux中断系统>. 总结:二中断处理经过两种模式:IRQ模式和SVC模式,这两种模式都有自己的stack,同时涉及到异常向量表中的中断向量. 三ARM处理器在感知到 ...

  2. Linux kernel的中断子系统之(一):综述

    返回目录:<ARM-Linux中断系统>. 总结: 一从作为一名驱动工程师角度看,用好中断需要正确认识request_threaded_irq/request_irq关系.中断临界区保护. ...

  3. Linux kernel的中断子系统之(八):softirq

    返回目录:<ARM-Linux中断系统>. 总结:中断分为上半部和下半部,上半部关中断:下半部开中断,处理可以延迟的事情.下半部有workqueue/softirq/tasklet三种方式 ...

  4. Linux kernel的中断子系统之(三):IRQ number和中断描述符

    返回目录:<ARM-Linux中断系统>. 总结: 二描述了中断处理示意图,以及关中断.开中断,和IRQ number重要概念. 三介绍了三个重要的结构体,irq_desc.irq_dat ...

  5. Linux kernel的中断子系统之(二):IRQ Domain介绍

    返回目录:<ARM-Linux中断系统>. 总结:一.二概述了软硬件不同角度的IRQ Number和HW Interrupt ID,这就需要他们之间架个桥梁. 三介绍了架设这种桥梁的几种方 ...

  6. Linux kernel的中断子系统之(四):High level irq event handler

    返回目录:<ARM-Linux中断系统>. 总结:从架构相关的汇编处理跳转到Machine/控制器相关的handle_arch_irq,generic_handle_irq作为High l ...

  7. Linux kernel的中断子系统之(九):tasklet

    返回目录:<ARM-Linux中断系统>. 总结: 二介绍了tasklet存在的意义. 三介绍了通过tasklet_struct来抽想一个tasklet,每个CPU维护一个tasklet链 ...

  8. Linux kernel的中断子系统之(七):GIC代码分析

    返回目录:<ARM-Linux中断系统>. 总结: 原文地址:<linux kernel的中断子系统之(七):GIC代码分析> 参考代码:http://elixir.free- ...

  9. linux kernel的中断子系统之(三):IRQ number和中断描述符【转】

    转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interr ...

  10. linux kernel的中断子系统 softirq

    linux kernel的中断子系统之(八):softirq http://www.wowotech.net/irq_subsystem/soft-irq.html http://www.ibm.co ...

随机推荐

  1. ”数学口袋精灵“第二个Sprint计划---第二天

    “数学口袋精灵”第二个Sprint计划----第二天进度 任务分配: 冯美欣:欢迎界面的音效 吴舒婷:游戏界面的动作条,选择答案后的音效 林欢雯:完善算法代码的设计 进度:   冯美欣:找到了几个音乐 ...

  2. OVS流表table之间的跳转

    OVS流表table之间的跳转 前言 今天在帮学弟解决问题的时候,遇到一个table0.table1之间的微妙小插曲,引起了注意,后来查了一下资料发现原因了. 问题描述 wpq@wpq:~$ sudo ...

  3. 老李的blog使用日记(2)

    寥寥数语结束一个不曾期待的遇见,可还是剧情不会这样结束,他也会在我的时间里注册自己的专属账号,无论什么时候,他会时而需要被注视着,为了达到目的,即使不择手段,只为一次擦肩而过的邂逅,极短的一段时间,相 ...

  4. 32位机和64位机下面各类型sizeof的大小

    ---<这种类型的题目出的太多了,还是整理下吧!>--- 一.机器平台:X86_64 处理器 操作系统:Red Hat 4.1.2-14 编译器: gcc version 4.1.2 20 ...

  5. Delphi 之 菜单组件(TMainMenu)

    菜单组件TMainMenu 创建菜单双击TmenuMain,单击Caption就可以添加一个菜单项 菜单中添加分割线只需加“-”就可以添加一个分割线 级联菜单的设计 单击鼠标右键弹出菜单中选择Crea ...

  6. (转)远程连接webservice遇到无法访问的问题解决办法

    原帖:http://stu-xu.i.sohu.com/blog/view/170429191.htm 如果在本地测试webservice可以运行,在远程却显示“测试窗体只能用于来自本地计算机的请求” ...

  7. Fake or True(HNOI2018)

    闲话 或许有人会问博主蒟蒻:ZJOI爆0记呢? 博主太弱了,刚刚去ZJ做了个梦回来,又得马不停蹄地准备HNOI 于是就成了烂坑 不过至少比某某更强更fake的xzz的游记要好一些 其实ZJOI挺值得回 ...

  8. java实现超大整数加减乘除四则运算

    原理: 用数组存储数字,按照计算法则进行运算. 代码: package com.hdwang; import java.util.regex.Matcher; import java.util.reg ...

  9. 【CF437C】The Child and Toy

    题目大意:给定一个有 N 个点,M 条边的无向图,点有点权,删除一个点就要付出所有与之有联系且没有被删除的点的点权之和的代价,求将所有点删除的最小代价是多少. 题解:从图连通性的角度出发,删除所有点就 ...

  10. asp.net连接数据库超时的解决办法

    错误提示:“超时时间已到.超时时间已到,但是尚未从池中获取连接.出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小.  ” 经过几天辛苦写的代码,终于实现了功能丰富的查询功能,但是使用的过 ...