中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务 的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。

单片机中断处理

①分辨中断类型
②调用处理函数
③清中断

Linux系统 : asm_do_IRQ

1.申请中断:request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);
①分配irqaction结构。
②setup_irq(irq, action);    a.加入中断链表:irq_desc[irq]->action.  b.设置中断引脚和中断类型:desc->chip->settype()  c.使能中断:desc->chip->startup/enable
2.释放中断:free_irq(irq, dev_id);
①从链表取出中断
②禁止中断,free

中断线共享的数据结构:
   struct irqaction {
    irq_handler_t handler; /* 具体的中断处理程序 */
    unsigned long flags;/*中断处理属性*/
    const char *name; /* 名称,会显示在/proc/interreupts 中 */
    void *dev_id; /* 设备ID,用于区分共享一条中断线的多个处理程序 */
    struct irqaction *next; /* 指向下一个irq_action 结构 */
    int irq;  /* 中断通道号 */
    struct proc_dir_entry *dir; /* 指向proc/irq/NN/name 的入口*/
    irq_handler_t thread_fn;/*线程中断处理函数*/
    struct task_struct *thread;/*线程中断指针*/
    unsigned long thread_flags;/*与线程有关的中断标记属性*/
};

thread_flags参见枚举型:
enum {
    IRQTF_RUNTHREAD,/*线程中断处理*/
    IRQTF_DIED,/*线程中断死亡*/
    IRQTF_WARNED,/*警告信息*/
    IRQTF_AFFINITY,/*调整线程中断的关系*/
};

多个中断处理程序可以共享同一条 中断线(引脚),irqaction 结构中的 next 成员用来把共享同一条中断线的所有中断处理程序组成一个单向链表,dev_id 成员用于区分各个中断处理程序。

3.poll机制
 app: poll
 kernel: sys_poll -> do_sys_poll(..., timeout_jiffies) -> poll_initwait(&table); -> init_poll_funcptr(&pwq->pt, __pollwait); -> pt->qproc = qproc -> do_poll(nfds, head, &table, timeout) :
      for(;;)
      {
        if (do_pollfd(pfd, pt)){   ->   mask = file->f_op->poll(file, pwait);  return mask;
                       //驱动poll:
                        __pollwait(flip, &button_waitq, p); //把当前进程挂到button_waitq队列里去
          count++;
          pt = NULL;
        }
        //break的条件:count非0, 超时,有信号在等待处理
        if (count || !*timeout || signal_pending(current))
          break;
        //休眠__timeout 5s
        __timeout = schedule_timeout(__timeout);
      }
   

等待队列:

在 Linux 驱动程序设计中,可以使用等待队列可以看作保存进程的容器,在阻塞进程时,将进程放入等待队列;

当唤醒进程时,从等待队列中取出进程.

等待队列的 定义 和 初始化 wait_queue_head_t    DECLARE_WAIT_QUEUE_HEAD  :

Linux 2.6 内核提供了如下关于等待队列的操作:

1,定义等待队列.

wait_queue_head_t   my_queue

2,初始化等待队列.

init_waitqueue_head ( &my_queue )

3,定义并初始化等待队列.

DECLARE_WAIT_QUEUE_HEAD  ( my_queue )

等待队列的 睡眠  wait_event_interruptible :

有条件睡眠:

1,  wait_event ( queue , condition )

当 condition ( 一个布尔表达式 ) 为真,立即返回;否则让进程进入 TASK_UNINTERRUPTIBLE 模式

睡眠,并挂在 queue 参数所指定的等待队列上.

2,  wait_event_interruptible ( queue , condition )

当 condition ( 一个布尔表达式 ) 为真,立即返回;否则让进程进入
TASK_INTERRUPTIBLE 模式

睡眠,并挂在 queue 参数所指定的等待队列上.

3, int  wait_event_killable ( wait_queue_t  queue , condition )

当 condition ( 一个布尔表达式 ) 为真,立即返回;否则让进程进入 TASK_KILLABLE 模式

睡眠,并挂在 queue 参数所指定的等待队列上.

无条件睡眠:

( 老版本,不建议使用 )

sleep_on  ( wait_queue_head_t  *q )

让进程进入 不可中断 的睡眠,并把它放入等待队列 q.

interruptible_sleep_on  ( wait_queue_head_t  *q )

让进程进入 可中断 的睡眠,并把它放入等待队列 q.

等待队列中唤醒进程 wake_up :

wake_up ( wait_queue_t  *q )

从等待队列 q 中唤醒状态为 TASKUNINTERRUPTIBLE ,TASK_INTERRUPTIBLE ,TASK_KILLABLE

的所有进程.

wake_up_interruptible ( wait_queue_t  *q )

从等待队列 q 中唤醒状态为 TASK_INTERRUPTIBLE 的进程.

 
      

按键中断驱动示例代码
1.驱动代码:button_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
 
 
static struct class *buttondrv_class;
static struct class_device    *buttondrv_class_dev;
 
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
 
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
 
/*该宏初始化一个button_waitq队列*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 
/* 中断事件标志, 中断服务程序将它置1,button_drv_read将它清0 */
static volatile int ev_press = 0;
 
 
struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};
 
 
/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;
 
struct pin_desc pins_desc[4] = {
    {S3C2410_GPF0, 0x01},
    {S3C2410_GPF2, 0x02},
    {S3C2410_GPG3, 0x03},
    {S3C2410_GPG11, 0x04},
};
 
 
/*
  * 确定按键值
  */
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    struct pin_desc * pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;
    
    pinval = s3c2410_gpio_getpin(pindesc->pin);
 
    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }
 
    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
 
    
    return IRQ_RETVAL(IRQ_HANDLED);
}
 
static int button_drv_open(struct inode *inode, struct file *file)
{
    /* 配置GPF0,2为输入引脚 */
    /* 配置GPG3,11为输入引脚 */
    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
    request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
    request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
    request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);    
 
    return 0;
}
 
ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    if (size != 1)
        return -EINVAL;
 
    /* 如果没有按键动作, 休眠 */
    wait_event_interruptible(button_waitq, ev_press); //ev_press : 0-休眠,1-返回
 
    /* 如果有按键动作, 返回键值 */
    copy_to_user(buf, &key_val, 1);
    ev_press = 0;
    
    return 1;
}
 
int button_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, &pins_desc[0]);
    free_irq(IRQ_EINT2, &pins_desc[1]);
    free_irq(IRQ_EINT11, &pins_desc[2]);
    free_irq(IRQ_EINT19, &pins_desc[3]);
    return 0;
}
 
static unsigned button_drv_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait); // 不会立即休眠
 
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
 
    return mask;
}
 
static struct file_operations button_drv_fops = {
    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    =  button_drv_open,      
    .read    =    button_drv_read,        
    .release =  button_drv_close,
    .poll    =  button_drv_poll,
};
 
 
int major;
static int button_drv_init(void)
{
    major = register_chrdev(0, "button_drv", &button_drv_fops);
 
    buttondrv_class = class_create(THIS_MODULE, "button_drv");
 
    buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
 
    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;
 
    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpgdat = gpgcon + 1;
 
    return 0;
}
 
static void button_drv_exit(void)
{
    unregister_chrdev(major, "button_drv");
    class_device_unregister(buttondrv_class_dev);
    class_destroy(buttondrv_class);
    iounmap(gpfcon);
    iounmap(gpgcon);
    return 0;
}
 
 
module_init(button_drv_init);
 
module_exit(button_drv_exit);
 
MODULE_LICENSE("GPL");

2.测试代码:buttondrvtest.c

#include <sys/types.h> 
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
 
 
/* buttondrvtest */
int main(int argc, char **argv)
{
    int fd;
    unsigned char key_val;
    int ret;
 
    struct pollfd fds[1]; //同时可以查询多个,当前只定义一个
    
    fd = open("/dev/buttons", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }
 
    fds[0].fd     = fd; //要查询的文件
    fds[0].events = POLLIN; //期待获得的值,POLLIN:有数据等待读取
    while (1)
    {
     //read(fd, &key_val, 1); //wait_event_interruptible(button_waitq, ev_press); 会休眠阻塞到中断发生返回,使用poll机制可以避免此问题
     //printf("key_val = 0x%x\n", key_val);
//sleep(5);
        //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
     ret = poll(fds, 1, 5000);
        if (ret == 0)
        {
            printf("time out\n");
        }
        else
        {
            read(fd, &key_val, 1);
            printf("key_val = 0x%x\n", key_val);
        }
    }
    return 0;
}

3.Makefile:

KERN_DIR = /work/system/linux-2.6.22.6

all:
    make -C $(KERN_DIR) M=`pwd` modules clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order obj-m    += button_drv.o

Linux学习 :中断处理机制 & poll机制的更多相关文章

  1. linux字符驱动之poll机制按键驱动

    在上一节中,我们讲解了如何自动创建设备节点,实现一个中断方式的按键驱动.虽然中断式的驱动,效率是蛮高的,但是大家有没有发现,应用程序的死循环里的读函数是一直在读的:在实际的应用场所里,有没有那么一种情 ...

  2. linux驱动编写之poll机制

    一.概念 1.poll情景描述 以按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值.这样做的效果是:如果有按键按下了,调用该re ...

  3. Linux学习-YUM 在线升级机制

    这个 yum 是透过分析 RPM 的标头资料后, 根据 各软件的相关性制作出属性相依时的解决方案,然后可以自动处理软件的相依属性问题,以解决软件 安装或移除与升级的问题. 利用 yum 进行查询.安装 ...

  4. 3.字符设备驱动------Poll机制

    1.poll情景描述 以之前的按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值. ) { read(fd, &key_v ...

  5. 嵌入式Linux驱动学习之路(十二)按键驱动-poll机制

    实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键 ...

  6. Linux驱动之poll机制的理解与简单使用

    之前在Linux驱动之按键驱动编写(中断方式)中编写的驱动程序,如果没有按键按下.read函数是永远没有返回值的,现在想要做到即使没有按键按下,在一定时间之后也会有返回值.要做到这种功能,可以使用po ...

  7. Linux之poll机制分析

    应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open().read().write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直 ...

  8. Linux内核中断处理机制

    <什么是中断> 计算停下当前处理任务,并保存现场,转而去处理其他是任务,当完成任务后再回到原来的任务中去. <中断的分类> a:软中断     软中断时执行中断指令产生的,软中 ...

  9. linux IO多路复用POLL机制深入分析

    POLL机制的作用这里就不进行介绍,根据linux man手册,解释为在一个文件描述符上等待某个事件.按照抽象一点的理解,当某个事件被触发(条件被满足),文件描述符变为有状态,那么用户空间可以根据此进 ...

随机推荐

  1. 小谈 checkbox 的选中

    先做草稿,稍后完善, javascript for (var i = 0; i < jsonmsg.length; i++) { var ischecked = false; for (var ...

  2. Latex图片显示问题(1)

    用latex编译后,若用dvipdf生成pdf文件,则其中有个eps图的左侧会显示不完全:若是用dvips--pspdf生成pdf文件,图像显示没问题. 这种情况的问题出在,加载 graphicx 宏 ...

  3. Linux中的shell

    shell的含义: 首先shell的英文含义是"壳": 它是相对于内核来说的,因为它是建议在核的基础上,面向于用户的一种表现形式,比如我们看到一个球,见到的是它的壳,而非核. Li ...

  4. [Java] Java解析XML格式Response后组装成Map

    //Get and Parse Response def response = context.expand(‘${TestStepName#Response}’) def xmlParser = n ...

  5. Evolutionary Computing: multi-objective optimisation

    1. What is multi-objective optimisation [wikipedia]: Multi-objective optimization (also known as mul ...

  6. MAC usb启动盘制作

    1.从App Store 下载OS 2.磁盘工具格式化磁盘默认即可 3. 为啥截图,因为有些是不一样的,建议使用 Tab建,    未命名则是你移动U盘命名的名称. 4.完成:所有的命令完成的话, 终 ...

  7. 数据库 MySQL安装图解

    MySQL安装图解 一.MYSQL的安装 1.打开下载的mysql安装文件,双击运行mysql-5.5.40-win32.msi. 2.选择安装类型,有"Typical(默认)". ...

  8. nginx 启动、重启、关闭

    一.启动 cd usr/local/nginx/sbin./nginx 二.重启 更改配置重启nginx kill -HUP 主进程号或进程号文件路径或者使用cd /usr/local/nginx/s ...

  9. 解决 笔记本键盘打字母却跳出数字来,每次都要按一遍Fn+Num LK 的问题

    方法一. 开始-运行,输入“Regedit"命令进入注册表 HKEY_USERS\.DEFAULT\Control Panel\Keyboard 将 InitialKeyboardIndic ...

  10. 张艾迪:全面开放“A"计划

    全面开放A计划.放飞梦想.放飞世界 AOOOiA.global将全面开启"A"计划.与世界分享We Share .与世界一同探索求知.全面开放"A"计划:拉近世 ...