日志标签 ‘中断’

对中断进行上部分和下部分的划分

2010年10月3日

上文中我们通过一个简单的例子分析了一个中断程序的基本结构。可以看到,中断处理程序在处理中断时起到了关键作用,也是一个中断程序必不可少的部分。不过,现如今的中断处理流程都会分为两部分:上半部分(top half)和下半部分(bottom half)。为什么要将一个中断分为如此两部分?下面的几个经典原因可以很好的诠释这个问题。

1.中断可以随时的打断处理机对其他程序的执行,如果被打断的代码对系统很重要,那么此时中断处理程序的执行时间应该是越短越好。

2.通过上文我们知道,中断处理程序正在执行时,会屏蔽同条中断线上的中断请求;而更严重的是,如果设置了IRQF_DISABLED,那么该中断服务程序执行是会屏蔽所有其他的中断请求。那么此时应该让中断处理程序执行的越快越好。

上面的几个例子都要求中断服务程序的执行时间越短越好。一般的,中断处理程序会在上半部分执行。而事实上,几乎所有的情况,上半部分就只执行中断处理程序。因此,我们可以这样认为:一个完整的中断处理流程是由中断处理程序和下半部分共同完成的。

这样划分是有一定原因的,因为我们必须有一个快速、异步而且简单的处理程序专门来负责对硬件的中断请求做出快速响应,与此同时也要完成那些对时间要求很严格的操作。而那些对时间要求相对宽松,其他的剩余工作则会在稍候的任意时间执行,也就是在所谓的下半部分去执行。

总之,这样划分一个中断处理过程主要是希望减少中断处理程序的工作量(当然了,理想情况是将全部工作都抛给下半段。但是中断处理程序至少应该完成对中断请求的相应。),因为在它运行期间至少会使得同级的中断请求被屏蔽,这些都直接关系到整个系统的响应能力和性能。而在下半段执行期间,则会允许响应所有的中断。

和上半段只能通过中断处理程序实现不同的是,下半部可以通过多种机制来完成:小任务(tasklet),工作队列,软中断。在本博客后续的文章当中你会看到,不管是那种机制,它们均为下半部提供了一种执行机制,比上半部灵活多了。至于何时执行,则由内核负责。

以上是上下部分划分的基本概述,通过tasklet工作队列机制,你可以更深刻的理解下部分的执行。

你的第一个中断程序!

2010年10月2日

Last Update:2011/11/09

本文通过一个简单的中断程序来描述一般中断程序的基本框架。完整代码这里

中断程序一般会包含在某个设备的驱动程序中,因此,接下来的程序本质上还是一个内核模块。说到内核模块,你应该知道首先去看什么了吧?对了,就是内核模块加载函数。

static int __init myirq_init()
{
	printk("Module is working..\n");
	if(request_irq(irq,myirq_handler,IRQF_SHARED,devname,&mydev)!=0)
	{
		printk("%s request IRQ:%d failed..\n",devname,irq);
		return -1;
	}
	printk("%s rquest IRQ:%d success..\n",devname,irq);
	return 0;
}

在内核加载函数中,我们除了显示一些信息外,最重要的工作就是申请一根中断请求线,也就是注册中断处理程序。很明显,这一动作是通过request_irq函数来完成的。这个函数的原型如下:

static int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);

第一个参数是中断号,这个中断号对应的就是中断控制器上IRQ线的编号。

第二个参数是一个irq_handler_t类型个函数指针:

typedef irqreturn_t (*irq_handler_t)(int, void *);

handler所指向的函数即为中断处理程序,需要具体来实现。

第三个参数为标志位,可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本实例程序中取IRQF_SHARED,该标志表示多个设备共享一条IRQ线,因此相应的每个设备都需要各自的中断服务例程。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。取IRQF_SAMPLE_RANDOM则表示设备可以被看做是事件随见的发生源。

第四个参数是请求中断的设备的名称。可以在/proc/interface中查看到具体设备的名称,与此同时也可以查看到这个设备对应的中断号以及请求次数,甚至中断控制器的名称。

第五个参数为一个指针型变量。注意此参数为void型,也就是说通过强制转换可以转换为任意类型。这个变量在IRQF_SHARED标志时使用,目的是为即将要释放中断处理程序提供唯一标志。因为多个设备共享一条中断线,因此要释放某个中断处理程序时,必须通过此标志来唯一指定这个中断处理程序。习惯上,会给这个参数传递一个与设备驱动程序对应的设备结构体指针。关于中断程序,可参考这里的文章。

以上就是request_irq函数各个参数的意义。

与中断处理程序的注册相对应的是free_irq函数,它会注销相应的中断处理程序,并释放中断线。这个函数一般被在内核模块卸载函数中被调用。

static void __exit myirq_exit()
{
	printk("Module is leaving..\n");
	free_irq(irq,&mydev);
	printk("%s request IRQ:%d success..\n",devname,irq);
}

如果该中断线不是共享的,那么该函数在释放中断处理程序的同时也将禁用此条中断线。如果是共享中断线,只是释放与mydev对应的中断处理程序。除非该中断处理程序恰好为该中断线上的最后一员,此条中断线才会被禁用。在此处,你也可以感受到mydev的重要性。

下面具体分析中断处理函数。该函数的功能很简单,只是显示一些提示信息。

static irqreturn_t myirq_handler(int irq,void* dev)
{
	struct myirq mydev;
	static int count=1;
	mydev=*(struct myirq*)dev;
	printk("key: %d..\n",count);
	printk("devid:%d ISR is working..\n",mydev.devid);
	printk("ISR is leaving..\n");
	count++;
	return IRQ_HANDLED;
}

另外,本内核模块在插入时还需要附带参数,下面的语句首先定义两个参数,然后利用宏module_param宏来接受参数。

static int irq;
static char* devname;

module_param(devname,charp,0644);
module_param(irq,int,0644);

使用方法:

1.通过cat /proc/interrupts查看中断号,以确定一个即将要共享的中断号。本程序因为是与键盘共享1号中断线,因此irq=1;

2.使用如下命令就可以插入内核:

sudo insmod filename.ko irq=1 devname=myirq

3.再次查看/proc/interrupts文件,可以发现1号中断线对应的的设备名处多了myirq设备名;

4.dmesg查看内核日志文件,可看到在中断处理程序中所显示的信息;

5.卸载内核模块;

可以看到,内核模块加载后,我们所写中断处理程序是被自动调用的,主要是因为该中断线上有键盘所发出的中断请求,因此内核会执行该中断线上的所有中断处理程序,当然就包括我们上述所写的那个中断处理程序。关于中断处理程序的执行,可参考这里的文章。

这样,一个最基本的中断程序就编写完成了!try!

后记:

这个程序调试起来并不难,但是我们并不能仅仅局限在这个程序本身。以它为入口点深入学习中断的基本原理再好不过。下面给出几个学习的入口点。

1.为何我们的中断程序和其他设备共享了一个中断线后会被执行?或者说,共享中断线上的所有中断服务例程是怎么执行的?

2.中断涉及到那些基本的数据结构?这些数据结构之间有什么关系?

3.do_IRQ()函数的大体执行流程是什么?

亲们,要学习的东西还很多,让我们一起加油吧!

什么是中断?

2010年10月1日

Last Update:2011/11/03

我们经常听到中断这个词,到底什么是中断?在这之前我先讲给大家一个故事。

从前有两位班主任A和B,A老师带一班,B老师带二班。这两位老师平时都很忙,平时除了为学生们备课改作业,当某个同学提出问题时,还要为他们解答疑问。A老师生怕遗漏每一位同学提出的问题,每隔一段时间就放下手头的工作,不断轮流寻问每一位同学:“你有问题吗”。也许被寻问的这位同学恰好有问题要咨询老师,可这毕竟是少数;而当A老师继续批改作业的时候又出现了一些同学提出问题,可是这个时候还没到A老师轮流寻问同学的时间。就这样,A老师的宝贵时间经常被浪费。

B老师和A老师有所不同,他认为完全没有必要这样死板的寻问每一位同学是否有问题。他对全班同学说:“谁有问题就主动来找我“。即便他可能正在改作业,但是完全可以暂时放下手头那些重要的工作,先为这位迷惑的同学解决问题。因此,B老师既可以改作业,又可以在学生主动提出问题的时候为那个学生解决问题。显然B老师的工作效率比A老师提高了很多。

故事看懂了,那么恭喜你,你也懂了什么是中断。

上面的两位老师分别代表系统中对设备进行管理的两种典型的方式。A老师的那种工作方式属于早期的程序查询控制方式(或称为轮询),内核定期对设备的状态进行查询;而B老师则属于中断控制方式,I/O设备需要服务时,可主动向内核发出中断请求并打断CPU当前正在执行的任务。前者内核为主动;而后者变内核主动为被动,由设备主动向内核发出中断请求。

从物理角度来看,中断请求是由外部硬件设备产生的一种电信号,外部设备首先将这种电信号发给中断控制器,接着中断控制器将此电信号发送给CPU。CPU检查到该中断信号后再通知内核,然后由内核完成后续的一些列处理工作。显然,内核不需要定期去检查设备,从而提高了CPU利用率。

通过以上的描述,你应该对中断有了一个大致的了解,不过想要更具体的了解中断必须搞清楚以下的知识点。

1.中断的分类

早期以及一般情况下,我们所说的中断即指由外设所产生的中断。随着计算机的迅速发展,中断不再仅仅局限于外部设备,CPU本身也会产生中断,不过我们将这种中断称为异常。

对于x86体系结构而言,中断可以分为两大类:同步中断和异步中断。同步中断即我们上面所说的异常,它是由 CPU 在执行非法命令时所产生的。之所以称为同步,是因为这种中断请求信号与代码指令同步执行,也就是说只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间。而异步中断即由外部设备产生的中断,这种中断可以随时发生,习惯上,我们将异步中断仍然称为中断。

中断可分为可屏蔽中断(Maskable interrupt)和不可屏蔽中断(Nomaskable interrupt)。异常可分为故障(fault)、陷阱(trap)、终止(abort)三类。

可屏蔽中断主要是针对外部设备所产生的中断信号,不可屏蔽中断一般是指计算机内部硬件产生的中断。由于异常是CPU发出的中断信号,与中断控制器(下文有解释)无关,因此异常不能被屏蔽。那么,异常和不可屏蔽中断有些相似点:它们均与外部设备无关,并且均不能被屏蔽。

2.中断控制器

中断控制器可分为可编程中断控制器(Programmable Interrupt Controller,PIC)和高级可编程中断控制器(Advanced Programmable Interrupt Controller,APIC)。前者仅可用于单处理器(Uni-processor)平台,后者则可用于多处理器(Mutiliple Processor)平台。

传统的PIC都是通过两片级联的8259A来管理和控制15个由外部设备所产生的中断源。由下图可看到,每片8259A芯片最多可管理8个中断源。但由于两片8259A芯片的级联,即从片的INT输出端与主片的2号(第三条线)中断线相连接,所以总共可以管理15个中断源。

外设和中断控制器上每根相连的线被称为中断线(也称为IRQ线)。对这些中断线进行编号就形成了中断号。IRQn线所对应的中断号即为n。当外部设备产生中断时,就通过中断线向系统发出中断请求(Interrupt ReQuirement)。由于中断控制器只能控制15个中断源,而如今的外设又日益剧增,因此就出现了对各外设共享一条中断线的情况,即中断共享(后文会有详细解释)。

上面已经说过,外部设备的中断请求可以被屏蔽,但是具体体现到硬件上是如何做到的?在每个8259A芯片上都有一个8位的中断屏蔽寄存器,每一位对应一条中断线。在对应为上置1则可屏蔽此条中断线;相反置0则可启动此条中断线。

需要说明的是,现在大多数计算机都使用的是APIC,可以通过查看/proc/interrupts文件获取中断控制器的名称。之所以将传统的8259A作为PIC的举例来学习,最大的原因还是因为它非常的经典,就如同古董——8086处理器一样。通过查看/proc/interrupts文件,可以发现APIC的中断号并不止15个(但即便是这样,还需要共享中断)。

3.中断向量

x86体系结构支持256种中断,即256个中断源。将这些中断源按照0到255的顺序对每种中断进行编号,这个编号叫做中断向量,通常使用8位无符号整数来存储这个向量。中断号和中断向量存在一对一的映射关系。

中断号和中断向量是两个不同的概念。当I/O设备把中断信号发送给中断控制器时,与之关联的是一个中断号;而当中断控制器将该中断信号传递给CPU时,与之关联的是一个中断向量。也就是说,中断号是以中断控制器的角度而言的;而中断向量则是以CPU的角度而言的。中断号和中断向量存在一对一的映射关系。

通常,Intel将编号为0~31的向量分配给异常和非屏蔽中断,这部分向量是固定的。因此在8259A默认情况下,中断号n所对应的中断向量为n+32。

4.中断服务例程

在响应一个具体的中断时,内核会执行一个函数,该函数被称为中断服务例程(Interrupt Service Routine,ISR)。每一个设备的驱动程序中都会定义相关的中断服务例程。从下面的代码可以看到,中断服务例程有两个参数,分别为int型和void指针型。并且返回值为irqreturn_t。

 //linux/include/linux/interrupt.h
  98typedef irqreturn_t (*irq_handler_t)(int, void *);
 //linux/include/linux/irqreturn.h
  10enum irqreturn {
  11        IRQ_NONE,
  12        IRQ_HANDLED,
  13        IRQ_WAKE_THREAD,
  14};
  15
  16typedef enum irqreturn irqreturn_t;

由于irqreturn_t是一个枚举类型,因此本质上为整形。并且IRQ_NONE,IRQ_HANDLED,IRQ_WAKE_THREAD的值一次为0,1,2。IRQ_NONE表示不处理所收到的中断请求;IRQ_HANDLED表示接收到了有效的中断请求,并且作出了正确的处理(这一点在后文中有详细讨论)。

需要注意的是,当一个中断服务例程正在执行的时候,该中断所在所有处理器上的都会被屏蔽,以免继续接受同一条中断线上的另一个新的中断。

以上就是关于中断的基本概念,了解了它们就容易进行后续的中断分析了。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key