你的第一个中断程序!

2010年10月2日 由 edsionte 留言 »

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()函数的大体执行流程是什么?

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

广告位

21 条评论

  1. wuxx说道:

    我试了一下,中断处理程序myirq_handler没有运行,dmesg看不到?请问何解?
    ubuntu 2.6.38

    [回复一下]

    edsionte 回复:

    @wuxx, 还有其他提示信息吗?

    [回复一下]

    whitepig 回复:

    @edsionte, 试了下,操作usb键盘不会触发myirq,然而操作笔记本自带ps/2键盘会触发myirq。1号iqr中的i8042是ps/2的中断处理程序。但是usb键盘的中断处理程序在哪个iqr线呢?

    [回复一下]

    qiao 回复:

    @wuxx, 再给点信息吧。。。

    [回复一下]

  2. qiao说道:

    看看是否你的irq和interface给的对不对?
    static int irq;
    static char* interface;
    试着将interface改为devname试试??!!

    [回复一下]

    edsionte 回复:

    @qiao, 你说萨呢。我看不懂。。

    [回复一下]

    qiao 回复:

    @edsionte, static int request_irq的倒数第二个参数你所给的name,是需要手动设置的,你这里使用的是devname,但是从外接收的是interface。。。

    [回复一下]

    edsionte 回复:

    @qiao, 那个参数就是传递一个设备名阿。。是随意设置的。

    [回复一下]

    qiao 回复:

    @edsionte, 其实我也想知道上面哪位的错误在哪。。。

    [回复一下]

    edsionte 回复:

    @qiao, 已改正,我刚才懂了你说的是哪里。

  3. sempurna说道:

    在2.6.32内核下试了下,在insmod时,irq参数设为1,但是按下键盘按键以后,对应的中断处理函数并未执行

    [回复一下]

    edsionte 回复:

    @sempurna, demsg后没有任何显示吗/

    [回复一下]

  4. 说道:

    sudo insmod filename.ko irq=1 devname=myirq
    一旦装载模块键盘就没有了响应,是怎么回事,我是在Virtualbox虚拟机下面运行的

    [回复一下]

    edsionte 回复:

    @光, 键盘指示灯是否闪烁,应该是你程序问题。

    [回复一下]

    回复:

    @edsionte, 我看了一下是内核出了异常,好像是tasklet_init重复调用引起的,将tasklet_int放到myirq_init()就没问题了。

    static int __init myirq_init()
    {
    printk(“Module is working..\n”);
    tasklet_init(&mytasklet,mytasklet_handler,0);
    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;
    }

    [回复一下]

  5. Victor Jiang说道:

    虚拟机会提示说CPU被禁止,必须重启

    [回复一下]

    edsionte 回复:

    @Victor Jiang, 这个程序还是在物理机试验吧

    [回复一下]

  6. silentwind说道:

    你好 如果我想修改linux的IDT中的内容 然后对现有的CPU组件进行编程 将中断定位到IDT中 实现自己的一个中断 这个在linux下有什么方式吗?刚入手linux,请不吝赐教

    [回复一下]

  7. 黄敏说道:

    请问一下,我的键盘共享中断是可以了,但是我把irq改成4,想弄一下串口共享中断,是可以加载这个模块,但是怎么才能触发里面的中断服务程序呢?我怎么触发都触发不起来啊!

    [回复一下]

  8. hoterran说道:

    如果你的键盘是通过usb 口的,那么 cat /proc/interrupts|grep usb 执行几次 就能看到 键盘是哪个 irq 啦.

    [回复一下]

发表回复

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