• Linux中断分层技术


     一、中断嵌套
       当系统正在执行某中断处理函数时,又产生了一个新的中断,这就叫做中断嵌套。当中断为慢速中断时,新的中断会取代当前中断,即当前中断没有执行完就结束 了;当中断为快速中断时,新的终端就不会产生。这两种情况都是我们不愿意看到的情况,所以就有了今天的题目——中断分层。

    二、中断分层
      中断分层是将中断处理函数分为上下两个部分。上半部是与硬件相关的,下半部是与硬件无关的。与硬件无关的内容我们就可以将它分离出中断处理函数,交给内核在系统空闲的时候处理,这样就缩短了中断处理的时间,从而减小了中断丢失的几率。
      分层方式:
        1、软中断
        2、tasklet
        3、工作队列
    三、工作队列处理方式
      将中断下半部分的工作挂载到工作队列中去,在内核空闲的时候将工作调用。Linux内核使用struct    workqueue_struct来描述一个工作队列,用struct    work_struct来描述一个工作项。

    struct workqueue_struct {
        unsigned int        flags;        /* I: WQ_* flags */
        union {
            struct cpu_workqueue_struct __percpu    *pcpu;
            struct cpu_workqueue_struct        *single;
            unsigned long                v;
        } cpu_wq;                /* I: cwq's */
        struct list_head    list;        /* W: list of all workqueues */
    
        struct mutex        flush_mutex;    /* protects wq flushing */
        int            work_color;    /* F: current work color */
        int            flush_color;    /* F: current flush color */
        atomic_t        nr_cwqs_to_flush; /* flush in progress */
        struct wq_flusher    *first_flusher;    /* F: first flusher */
        struct list_head    flusher_queue;    /* F: flush waiters */
        struct list_head    flusher_overflow; /* F: flush overflow list */
    
        mayday_mask_t        mayday_mask;    /* cpus requesting rescue */
        struct worker        *rescuer;    /* I: rescue worker */
    
        int            saved_max_active; /* W: saved cwq max_active */
        const char        *name;        /* I: workqueue name */
    };

      

    struct work_struct {
        atomic_long_t data;
        struct list_head entry;
        work_func_t func;
    };

      1、创建工作队列
      create_workqueue(name)
      参数:name为const char *类型,创建工作队列的名字
      返回值:struct    workqueue_struct*型指针
      2、创建工作
      INIT_WORK(_work, _func)
      参数:

        work:创建好工作项变量的地址
        func:工作项中的成员func的名字
      无返回值
      3、提交工作
      即将工作项挂载到工作队列中去。
      int    queue_work(struct workqueue_struct *wq, struct work_struct *work)
      参数:

        wq:工作队列变量的地址
        work:工作项变量的地址

      

      其实Linux内核已创建好了一个默认的工作队列供用户使用,keventd_wq,这样我们就不用再去进行创建工作队列的工作。提交工作项到默认工作队列的函数为
      int    schedule_work(struct work_struct *work)
      参数:要提交工作项的地址

    工作队列应用代码:

    #include <linux/module.h>
    #include <linux/init.h>
    
    struct work_struct *work1,*work2;
    
    void work1_func()
    {
        printk("this is work1
    ");
    }
    
    void work2_func()
    {
        printk("this is work2
    ");
    }
    
    int que_init()
    {
        /*创建工作*/
        work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    
        INIT_WORK(work1, work1_func);
    
        /*挂载工作到工作队列*/
        schedule_work(work1);
    
        /*创建工作*/
        work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    
        INIT_WORK(work2, work2_func);
    
        /*挂载工作到工作队列*/
        schedule_work(work2);
    
        return 0;
    }
    
    void que_exit()
    {
    
    }
    
    MODULE_LICENSE("GPL");
    module_init(que_init);
    module_exit(que_exit);

    将中断分层应用到键盘中断驱动程序中:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/miscdevice.h>
    #include <linux/interrupt.h>
    #include <linux/io.h>
    
    #define GPGCON 0x56000060
    
    /*定义工作队列*/
    struct work_struct *work1;
    
    void work1_func()
    {
        printk("press key down
    ");
    }
    int que_init()
    {
        /*创建工作*/
        work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    
        INIT_WORK(work1, work1_func);
    }
    
    /*中断处理函数*/
    irqreturn_t key_int(int irq, void *dev_id)
    {
        /*1、检测设备是否产生中断*/
    
        /*2、清除中断产生标志*/
    
        /*3、提交下半部分工作*/
        schedule_work(work1);
    
        return 0;
    }
    
    void key_hw_init()
    {
        unsigned int data; 
        unsigned int *gpio_config;
        
        gpio_config = ioremap(GPGCON,4);
        data = readw(gpio_config);
        data &= ((3)|(3<<6)|(3<<10)|(3<<12)|(3<<14)|(3<<22));//~(0b11);
        data |= (2|(2<<6)|(2<<10)|(2<<12)|(2<<14)|(2<<22));//0b10;
        writew(data,gpio_config);
    }
    
    int key_open(struct inode *node, struct file *filp)
    {
        return 0; 
    }
    
    struct file_operations key_fops = 
    {
        .open = key_open,
        //.unlocked_ioctl = key_ioctl,
    };
    
    struct miscdevice key_miscdev = 
    {
        .minor = 200,
        .name = "key",
        .fops = &key_fops,
    };
    
    static int key_init()
    {
        /*注册设备*/
        misc_register(&key_miscdev);
    
        /*硬件初始化*/
        key_hw_init();
        
        /*注册中断*/
        request_irq(IRQ_EINT8,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
        request_irq(IRQ_EINT11,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
        request_irq(IRQ_EINT13,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
        request_irq(IRQ_EINT14,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
        request_irq(IRQ_EINT15,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
        request_irq(IRQ_EINT19,key_int,IRQF_TRIGGER_FALLING ,"key", 0);
    
        que_init();
        printk("key.ko is ready
    ");
        return 0;
    }
    
    static void key_exit()
    {
        /*注销设备*/
        misc_deregister(&key_miscdev);
        /*注销中断*/
        free_irq(IRQ_EINT8, 0);
    }
    
    MODULE_LICENSE("GPL");
    module_init(key_init);
    module_exit(key_exit);

    实现功能:

      当按下一个按键,在串口中打印出对应的信息。

    此代码适用mini2440开发板,不同型号开发板IO口和中断号不同。如果有疑问或建议,欢迎指出。

  • 相关阅读:
    bzoj 1053
    bzoj 1004 组合
    字符串哈希
    bzoj 1015 并查集
    bzoj 1003 最短路+dp
    HDU 4352 数位dp
    bzoj 1879 状压dp
    codeforces 55D 数位dp
    Codeforces 830B
    组合计数 && Stirling数
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4294644.html
Copyright © 2020-2023  润新知