linux随笔记 - 中断相关知识

该笔记是学习左神或者伟神的教程的随笔记录,供需要时方便查阅,难免理解出错,如有纰漏还望指正。

一、linux中断的基本知识

1.1.中断号(int)


        每个中断都有一个中断编号,用来区分不同中断。

1.2.中断申请(request_irq)/释放(free_irq)

        在申请中断的参数:request_irq(中断号, 中断处理函数, 中断标志(共享/触发方式), 中断名字, 设备指针(传给中断处理函数))。返回值0-成功,1失败。

1.3.中断处理函数

        irqreturn_t (*irq_handler_t) (int, void *);输入参数为:中断号,申请时传入的设备指针。

1.4.中断的使能和失能

在需要关闭和开启某个中断时,可以用下列函数去指定开始和关闭对应中断号的中断。而nosync失能函数的区别在于他在关闭时,不会等到该中断执行完才会返回,他会立即返回,在执行完后自动关闭。前者是需要该中断执行完成之后,才会返回。

void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)  ==> void disable_irq_nosync(unsigned int irq)

在需要关闭所有中断时,用以下函数。但是当多个线程去操作中断开关时,可能会导致中断开启的时机错误。如A[0-6]期间关闭,B[0-3]之间关闭,B在3这个时刻会开启中断,导致A也在3这个时刻开启,所以就衍生出带标志位的使能/失能API。

local_irq_enable() ==> local_irq_save(flags) 
local_irq_disable() ==> local_irq_restore(flags)

二、中断上下部

上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可
以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部
去执行,这样中断处理函数就会快进快出。
主要介绍几种常用的下半部处理方式。

2.1 软中断

类型_enum:

struct softirq_action
{
    void (*action)(struct softirq_action *);
};
enum
{
    HI_SOFTIRQ=0, /* 高优先级软中断 */
    TIMER_SOFTIRQ, /* 定时器软中断 */
    NET_TX_SOFTIRQ, /* 网络数据发送软中断 */
    NET_RX_SOFTIRQ, /* 网络数据接收软中断 */
    BLOCK_SOFTIRQ,
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ, /* tasklet 软中断 */
    SCHED_SOFTIRQ, /* 调度软中断 */
    HRTIMER_SOFTIRQ, /* 高精度定时器软中断  */
    RCU_SOFTIRQ, /* RCU 软中断  */
    NR_SOFTIRQS
};
//软中断数组
static struct softirq_action softirq_vec[NR_SOFTIRQS];

数组 softirq_vec 是个全局数组,因此所有的 CPU(对于 SMP 系统而言)都可以访问到,每个 CPU 都有自己的触发和控制机制,并且只执行自己所触发的软中断

需要使用open_softirq函数进行注册,才能使用。软中断必须在编译的时候静态注册!!

/*
nr:enum类型中选取一个所要注册的中断类型
void(*active)(...):为软终端处理函数
*/
void open_softirq(int nr, void (*action)(struct softirq_action *))

2.2 tasklet

        tasklet 是利用软中断来实现的另外一种下半部机制,在软中断和 tasklet 之间,建议大家使
用 tasklet。

        tasklet的机制有点跟我们通常使用的RTOS中的软件定时器类似。需要对他进行初始化、注册后即可。只不过软件定时器是有一个计数器去进行触发,而tasklet是通过中断的上半部去调用一个调度的函数,通知下半部可以在合适的时间去执行。(再说通俗点,和RTOS中硬件中断给task发送一个msg,告诉task去做什么事情一样。不过可能这个task的优先级很高,会被优先执行吧,这只是我的猜测,可能不准确)。

struct tasklet_struct
{
	struct tasklet_struct *next; /* 下一个 tasklet */
	unsigned long state; /* tasklet 状态 */
	atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
	void (*func)(unsigned long); /* tasklet  执行的函数 */
	unsigned long data; /* 函数 func 的参数 */
};

下面贴一段正点原子中tasklet的使用实例:

/* 定义 taselet */
struct tasklet_struct testtasklet;
/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
/* tasklet 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 tasklet */
tasklet_schedule(&testtasklet);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 tasklet,下半部注册 */
tasklet_init(&testtasklet, testtasklet_func, data);
/* 注册中断处理函数,上半部注册 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

2.3 工作队列

        工作队列在进程上下文执行,工作队列将要推后的工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软中断或 tasklet。【工作队列和】和【tasklet】的使用区别。

同样cp一段教程的源码。以后用的时候直接看这个实例即可。

/* 定义工作(work) */
struct work_struct testwork;
/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 work */
schedule_work(&testwork);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 work */
INIT_WORK(&testwork, testwork_func_t);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

另外还有部分中断下半部的处理:中断线程化(提高内核的实时性)等等。

三、中断相关的设备树

1 fxls8471@1e {
2 compatible = "fsl,fxls8471";
3 reg = <0x1e>;
4 position = <0>;
5 interrupt-parent = <&gpio5>;
6 interrupts = <0 8>;
7 };

第 5 行,interrupt-parent 属性设置中断控制器,这里使用 gpio5 作为中断控制器。
第 6 行,interrupts 设置中断信息,0 表示 GPIO5_IO00,8 表示低电平触发。

1 low to high edeg triggered

2 high to low edeg triggered

3 active high level-sensitive

8 active low level-sensitive
简单总结一下与中断有关的设备树属性信息:
①、#interrupt-cells,指定中断源的信息 cells 个数。
②、interrupt-controller,表示当前节点为中断控制器。
③、interrupts,指定中断号,触发方式等。
④、interrupt-parent,指定父中断,也就是中断控制器。

获取中断号

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

int gpio_to_irq(unsigned int gpio) //只有是gpio时,才能用该api获取中断号

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值