<一> 中断处理流程例如以下:



1、发生中断时,CPU运行异常向量vector_irq的代码。

2、在vector_irq里面。终于会调用中断处理的总入口函数asm_do_IRQ。

3、asm_do_IRQ依据中断号调用irq_desc数组项中的handle_irq。

4、hadnle_irq会使用chip成员中的函数来设置硬件,比方清除中断、禁止中断、又一次使能中断等。

5、handle_irq逐个调用用户在action链表中注冊的处理函数。

      <二>安装中断处理例程

注冊/释放中断的函数:

int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs*), unsigned long flags,

const char *dev_name, void *dev_id);

void free_irq(unsigned int irq, void *dev_id);

unsigned int irq:申请的中断号

irqreturn_t (*handler)(int, void *, struct pt_regs*):安装的中断处理函数指针

unsigned long flags:中断管理有关的位掩码选项,中断的触发方式。如SA_INTERRUPT、SA_SHIRQ等

const char *dev_name:传递给request_irq的字符串。中断名字

void *dev_id:该指针仅仅用于共享的中断信号线。没有使用共享时,dev_id设置为NULL。

中断处理例程能够在驱动程序初始化时或设备第一次打开时安装。但考虑中断资源有限,调用request_irq的正确位置应该是在设备第一次打开、硬件被告知产生中断之前。调用free_irq的位置是最后一次关闭设备、硬件被告知不用再中断处理器之后。

    <三>实现中断处理例程

中断处理例程事实上也是一个C函数程序,但因为发生在中断时间内,所以它有下面特点:

1、处理例程不能向用户空间发送或者接收数据,由于它不是在不论什么进程的上下文中运行的。

2、处理例程不能做不论什么可能发生休眠的操作,比如调用wait_event,锁住一个信号量等。

3、处理例程不能调用schdule函数。

中断处理例程的功能就是将有关中断接收的信息反馈给设备,并依据正在服务的中断的不同含义对数据进行对应的读或写。它的一个典型任务就是:假设中断通知进程所等待的事件已经发生,比方新的数据到达,就会唤醒在该设备上休眠的进程。

不管是高速还是慢速处理例程,程序猿都应该编写运行时间尽可能短的处理例程。假设须要运行一个长时间的计算任务。最好的方法是使用tasklet或者工作队列在更安全的时间内调度计算任务。

中断处理例程irqreturn_t (*handler)(int, void *, struct pt_regs*)的參数及返回值:

int irq:中断号

void *dev_id:一种客户数据类型即驱动程序可用的私有数据,传递给request_irq函数的void *參数会在中断发生时作为參数被传回处理例程。通常,我们会为dev_id传递一个指向自己设备的数据结构指针。比如:

static irqreturn_t sample_interrupt(int irq, void *dev_id, struct pt_regs)
{
struct sample_dev *dev = dev_id;
……
}
/*与该处理例程相关联的典型open代码例如以下所看到的:*/
static void sample_open(struct inode *inode, struct file *filp)
{
struct sample_dev *dev = hwinfo + MINOR(inode->i_rdev);
request_irq(dev->irq, sample_interrupt, 0, "sample", (void *)dev);
……
return 0;
}

struct pt_reg *regs:非常少使用。

返回值有三种:IRQ_HANDLED。IRQ_NONE;IRQ_RETVAL(handled);

禁用和启用中断:

有时设备驱动程序必须在一个时间内堵塞中断的发生。

中断的禁用分为禁用单个中断和禁用全部中断。

禁用单个中断

#include <asm/irq.h>

void disable_irq(int irq);

void disable_irq_nosync(int irq);

void enable_irq(int irq);

禁用全部中断

#include <asm/irq.h>

void local_irq_save(unsigned long flags);

void local_irq_disable(void);

void local_irq_restore(unsigned long flags);

void local_irq_enable(void);

      <四>顶半部和底半部

中断处理的一个主要问题是如何在处理例程内完毕耗时的任务。对应一次设备中断须要完毕一定数量的工作。可是中断处理例程须要尽快结束而不能使终端堵塞的时间过长,这两个需求彼此冲突。

Linux通过将中断处理例程分成两部分来解决问题。

称为顶半部的部分。是实际响应中断的例程,也就是用request_irq注冊的中断例程;而所谓的底半部是一个被顶半部调度。并在稍后更安全的时间内运行的例程。顶半部处理例程和底半部处理例程之间最大的不同,就是当底半部处理例程运行时,全部的中断都是打开的——这就是所谓的更安全时间内运行。

典型的情况是顶半部保存设备的数据到一个设备特定的缓冲区并调度它的底半部。然后退出,这个操作是很快的。然后。底半部运行其它必要的工作。比如唤醒进程、启动另外的I/O操作等等。这样的方式同意在底半部工作期间,顶半部还能够继续为新的中断服务。

Linux内核有两种不同机制能够用来实现底半部处理:tasklet和工作队列。tasklet一般是底半部处理的优选机制,由于它很快,可是全部的tasklet代码必须是原子的。工作队列具有更高的延迟,但同意休眠。

tasklet:tasklet是一个能够由系统决定的安全时刻在软件中断上下文被调度执行的特殊函数。

使用宏DECLARE_TASKLET声明tasklet:DECLARE_TASKLET(name, function, data);当中的參数name是给tasklet起的名字,function是运行tasklet时调用的函数(它带有一个unsigned long型的參数而且返回void)。data是一个用来传递给tasklet函数的unsigned long类型的值。

函数tasklet_schedule用来调度一个tasklet执行。示比例如以下:

void example_do_tasklet(unsigned long)
{
……
}
DECLARE_TASKLET(example_tasklet, example_do_tasklet, 0); irqreturn_t example_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
……
tasklet_schedule(&example_tasklet);
……
}

工作队列:工作队列会在将来的某个时间、在某个特定的工作者进程上下文中调用一个函数。由于工作队列函数执行在进程上下文中。因此可在必要时休眠。

工作队列一般程序编写示比例如以下:

static struct work_struct example_workqueue;
INIT_WORK(&example_workqueue, (void (*)(void *))example_do_tasklet, NULL);
irqreturn_t example_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
……
schedule_work(&example_workqueue);
……
}

      <五>中断共享

安装共享的处理例程:就像普通非共享的中断一样,共享的中断也是通过request_irq安装的,但有两处不同:

1、请求中断时,必须指定flags參数中的SA_SHIRQ位。

2、dev_id參数必须是唯一的。

不论什么指定模块地址空间的指针都能够使用。但dev_id不能设置成NULL。

当请求一个共享中断时,假设满足以下条件之中的一个,那么request_irq就会成功:

1、中断信号线空暇

2、不论什么已经注冊了该中断信号线的处理例程也标识了IRQ是共享的。

使用共享处理例程的驱动程序须要小心一件事情:不能使用enable_irq和disable_irq。

LINUX设备驱动程序笔记(五)中断处理的更多相关文章

  1. LINUX设备驱动程序笔记(一)设备驱动程序简单介绍

    <一>:设备驱动程序的作用 从一个角度看,设备驱动程序的作用在于提供机制,而不是策略. 在编写驱动程序时,程序猿应该特别注意以下这个基本概念:编写訪问硬件的内核代码时,不要给用户强加不论什 ...

  2. LINUX设备驱动程序笔记(三)字符设备驱动程序

          <一>.主设备号和次设备号        对字符设备的訪问时通过文件系统内的设备名称进行的.那些设备名称简单称之为文件系统树的节点,它们通常位于/dev文件夹. 字符设备驱动程 ...

  3. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  4. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  5. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  6. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  7. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

  8. Linux设备驱动程序学习----目录

    目录 设备驱动程序简介 1.设备驱动程序简介 构造和运行模块 2.内核模块和应用程序的对比 3.模块编译和装载 4.模块的内核符号表  5.模块初始化和关闭  6.模块参数  7.用户空间编写驱动程序 ...

  9. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

随机推荐

  1. 【构造】Codeforces Round #480 (Div. 2) B. Marlin

    题意:给你一个4*n的网格,保证n为奇数,让你在其中放k个障碍物,不能放在边界的格子上,使得从左上角走到右下角的最短路的方案数,恰好等于从左下角走到右上角的最短路的方案数. k为偶数时,以纵向为对称轴 ...

  2. mutiplemap 总结

    之前只是在C++ Primer里面看过关联容器,可能因为没有实际用过,只是看看,所以导致用的时候并不熟悉: 在这之前,map和set的特性应该要了解,map是关联数组,也就是由键值对组成的,而set只 ...

  3. hdu 4451 Dressing 排列组合/水题

    Dressing Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  4. SMACH专题(二)----Concurrent状态机

    Concurrent状态机是一种同时执行多个状态的状态机.如下图所示.状态FOO和BAR同时执行,当两个状态输出的结果同时满足一个组合条件时(FOO输出outcome2,BAR输出outcome1)才 ...

  5. 简单的文件上传html+ashx

    前台页面:<form action="upload.ashx" method="post" enctype="multipart/form-da ...

  6. InvalidateRect(转)

    ///===================该段是自己总结的一个小结================================= InvalidateRect()函数的作用是设置一个无效区域,并 ...

  7. Tasker to answer incoming call by pressing power button

    nowadays, the smartphone is getting bigger in size, eg. samsung galaxy note and note 2, sorta big in ...

  8. POJ 3740 Dancing Links

    Dancing Links学习:http://www.cnblogs.com/steady/archive/2011/03/15/1984791.html 以及图文学习:http://www.cnbl ...

  9. JAVA常见算法题(三十三)---求子串在字符串中出现的次数

    计算某字符串中子串出现的次数. public static void main(String[] args) { String s1 = "adcdcjncdfbcdcdcd"; ...

  10. [Linux] Systemd 入门教程:实战篇

    reference : http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html 上一篇文章,我介绍了 Systemd ...