美文网首页程序员
初尝内核中断

初尝内核中断

作者: 网路元素 | 来源:发表于2019-10-02 08:20 被阅读0次

    每台计算机都连接有很多外设,那操作系统对这些外设进行管理时要如何通信呢?一般有轮询(Polling)中断(Interrupt)两种,第一种是操作系统定时主动去查询这些外设,看有没有设备需要进行处理,而第二种则是外设需要处理时主动向操作系统发送请求信号,然后操作系统再做相应的处理。这第二种不需要定时去查询,只有在设备需要时才进行处理,节省了CPU的消耗,提高了效率。故而我们先尝试下如何去使用Linux内核的中断处理机制,本次采用共享中断的机制来学习我们的中断程序。 

    一般来说,一个中断有这样的操作步骤: 

    1.注册中断及服务子程序;
    2.使能中断;
    3.中断到来时调用服务子程序进行处理,处理完成后退出继续等待下一个中断的到来。 

    而在Linux内核源码include/linux/interrupt.h里有注册中断需要用到的如下函数:

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

    接下来说说这5个参数吧: 

    1.irq:中断号/中断向量,在嵌入式里经常会用gpio来作为外部中断请求线,经常会用到gpio_to_irq函数来作转换;
    2.handler:服务子程序/中断处理程序,指向一个处理本中断的回调函数,其函数原型如下:

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

    其两参数分别对应irq和dev;
    3.flags:作为gpio请求中断时,该标志可设置触发方式:高电平、低电平、上升延、下降延,还可以设置该中断的处理模式是:屏蔽、共享等,具体可查阅interrupt.h文件里以IRQF_开头的宏;
    4.name:中断请求的设备名称,在/proc/interrupts里可查看到中断号、中断次数、中断设备名称;
    5.dev:传递给中断处理程序的参数,void*类型表示可以传递任意类型的数据,一般传递设备结构体指针。该参数在设置为共享模式(IRQF_SHARED)时用于标志共享中断的唯一性。 上面说到了中断注册函数,释放时对应的函数为:

    extern void free_irq(unsigned int, void *);

    第一个参数对应irq,第二个对应dev。 

    好了,理论的东西了解了部分,还是从实践出发吧,尝尝这次共享中断号实现中断的魅力吧:

    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/interrupt.h>

    static int irqn;
    static char* devname;

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

    struct shrirq
    {
            int devid;
    } shrdev={1119};

    static irqreturn_t shrirq_handler(int irq,void * dev)
    {
            static int count=0;
            struct shrirq shrdev=*(struct shrirq*)dev;

            printk("Enter shrirq_handler!\n");
            printk("ISR devid:%d\n",shrdev.devid);
            printk("count=%d\n",count);

            count++;

            if(count > 65536)
                    count=0;

            printk("Leaving IRQ handler!\n");

            return IRQ_HANDLED;
    }

    static int __init shrirq_init(void)
    {
            printk("Enter shrirq_init...\n");

            if(request_irq(irqn,shrirq_handler,IRQF_SHARED,devname,&shrdev) != 0)
            {
                    printk("Request IRQ failed!\ndevname=%s,IRQ:%d\n",devname,irqn);
                    return -1;
            }
            printk("Request IRQ success!\ndevname=%s,IRQ:%d\n",devname,irqn);
            printk("Exit shrirq_init...\n");

            return 0;
    }

    static void __exit shrirq_exit(void)
    {
            printk("Enter shrirq_exit...\n");
            free_irq(irqn,&shrdev);
            printk("Release irq sucess!\ndevname=%s,IRQ:%d\n",devname,irqn);
            printk("Exit shrirq_exit...\n");
    }

    module_init(shrirq_init);
    module_exit(shrirq_exit);
    MODULE_LICENSE("GPL");

    而相应的Makefile如下:

    obj-m += shrirq.o
    CUR_PATH:=$(shell pwd)
    LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

    all:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

    clean:
            make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean

    从源码看到,多了module_param来指定模块参数和MODULE_LICENSE来指定模块的使用的开源协议,这两部分在后期再详细说明。此时相应的源码树如下: 

    /home/xinu/xinu/linux_kernel_driver_l1/shrirq/
    ├── Makefile
    └── shrirq.c 

    我们make后使用如下命令来共享键盘中断验证我们的中断驱动模块: 

    1.PS/2接口 

    我们使用cat /proc/interrupts可以查看到PS/2所使用的i8042键盘控制器对应的中断信息如下:

    1: 3 0 IO-APIC-edge i8042 

    即中断号为1,那么我们使用命令sudo insmod shrirq.ko irqn=1 devname=shrirq来加载模块,加载成功后我们再看下中断信息: 

    1: 3 0 IO-APIC-edge i8042, shrirq 

    共享的设备名shrirq出现在i8042后面了。

    2.USB接口 

    对于USB键盘,我们先用lsusb确认下自己的键盘在哪条USB总线上,我这边是如下信息:

     Bus 002 Device 005: ID 1241:1603 Belkin Keyboard 

    在BUS 002上,而cat /proc/interrupts对应的内容如下:

     23: 15035 15044 IO-APIC-fasteoi ehci_hcd:usb2, uhci_hcd:usb6 

    即中断号为23,那我们使用命令sudo insmod shrirq.ko irqn=23 devname=shrirq来加载模块,加载成功后再查看中断信息如下:

     23: 15627 15651 IO-APIC-fasteoi ehci_hcd:usb2, uhci_hcd:usb6, shrirq 

    当上面加载模块成功后,我们可以使用sudo rmmod shrirq命令来卸载模块。 如果都能正常运行,相应的dmesg信息如下: 

    Enter shrirq_init…
    Request IRQ success!
    devname=shrirq,IRQ:18
    Exit shrirq_init…
    Enter shrirq_handler!
    ISR devid:1119
    count=0
    Leaving IRQ handler!
    Enter shrirq_handler!
    ISR devid:1119
    count=1
    Leaving IRQ handler! ……
    Enter shrirq_exit…
    Release irq sucess!
    devname=shrirq,IRQ:23
    Exit shrirq_exit… 

    至此,我们体验了一把中断,接下来会继续尝试与中断相关的深层内容,大家继续加油啊! 

    参考网址: 

    http://edsionte.com/techblog/archives/1521
    https://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/

    相关文章

      网友评论

        本文标题:初尝内核中断

        本文链接:https://www.haomeiwen.com/subject/ejplyctx.html