20200408-01 堵塞 非堵塞 傻傻分不清楚(配linux 按键例程含堵塞、非堵塞例程)

堵塞 / 非堵塞 IO

IO:是指对于内存的读写操作,很多时候读跟写存在互斥性不能同时进行,亦或者没法多个线程实现同时读写  
 堵塞:对应线程获取不到资源则被挂起,优势让出 CPU 资源  
 非堵塞:一直轮循等待

堵塞方式打开文件: fd = open("xx", O_RDWR);
非堵塞方式打开文件: fd = open("xx", O_RDWR | O_NONBLOCK);

堵塞IO

1. 等待队列

1.1 等待队列头

在驱动中使用等待队列,必须创建一个等待队列头,使用 wait_queue_head_t 表示 (include/linux/wait.h)

函数 描述
void init_waitqueue_head(wait_queue_head_t *q); 初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD() 一次性完成定义和初始化

1.2 等待队列项
每个设备的进程都是一个队列项,当设备不可用的时候需要将它们的等待队列项加入到等待队列里
DECLARE_WAITQUEUE(name, tsk); 完成定义和初始化一个等待队列项

1.3 添加/移除等待队列头

函数 描述
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) 等待队列添加
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) 等待队列移除
void wake_up(wait_queue_head_t *q) 唤醒进程 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
void wake_up_interruptible(wait_queue_head_t *q) 只能唤醒 TASK_INTERRUPTIBLE 进程
void wait_event(wq, condition) 等待 wq 为等待队列头的等待队列被唤醒,前提满足 condition 为true,否则一直堵塞(状态:TASK_UNINTERRUPTIBLE)
int wait_event_timeout(wq, condition, timeout) 可以添加超时时间,返回0:false && 超时; 返回1:true 条件满足
void wait_event_interruptible(wq, condition) 将状态设置为 TASK_INTERRUPTIBLE 可以被信号打断状态
int wait_event_interruptible_timeout(wq, condition, timeout) 同上面类似

非堵塞 IO

2. 轮询

程序可用通过 poll / epoll / select 来查询设备是否可以操作

2.1 select(单个线程中默认能够监视最多 1024 个,可以通过修改内核增大,但会降低效率)

函数 描述
void FD_ZERO(fd_set* set) 将所有 fd_set 变量所有位清零
void FD_SET(int fd, fd_set* set) 将某个位置 1
void FD_CLR(int fd, fd_set* set) 置0
int FD_ISSET(int fd, fd_set* set) 可以判断进行操作
EG
int ret, fd;
fd_set readfds;
struct timeval timeout;

fd = open("xxx", O_RDWR | O_NONBLOCK);
FD_ZERO(&readfds);
FD_SET(fd, &readfds);

//超时
timeout.tv_sec = 0;
timeout.tv_usec = 500000; //500ms

ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
switch(ret) {
    case 0:
        printf("timeout \n"); break;
    case -1:
        printf("error \n"); break;
    default:
        if (FD_ISSET(fd, &readfds)) {
            //使用 read 读数据
        }
        break;
}

2.2 poll 函数(与 select 效果相似,但是没有最大文件描述符数量限制)

void poll_wait(struct file* filp, wait_queue_head_t* wait_address, poll_table* p); //将 应用程序加入到 poll_table 中
POLLIN 有数据可读
POLLPRI 有紧急数据需要读取
POLLOUT 可以写数据
POLLERR 指定文件描述符发送错误
POLLHUP 制定文件描述符挂起
POLLNVAL 无效请求
POLLRDNORM 等同于 POLLIN
int ret, fd;
struct pollfd fds;
fd = open(filename, O_RDWR | O_NONBLOCK);

fds.fd = fd;
fds.events = POLLIN;
ret = poll(&fds, 1, 500);
if (ret) {
    //读取设备
} else if (ret == 0) {
    //超时
} else if (ret < 0) {
    //错误
}

2.3 epoll 为处理大并发,网络编程常用 epoll

int epoll_create(0); //创建一个 epoll 句柄
int epoll_wait(int epfd, struct epoll_event *event, int maxevents, int timeout); //等待事件发生
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); //向 epoll 加入监视文件描述符及监视事件
op: 操作设置
EPOLL_CTL_ADD   添加
EPOLL_CTL_MOD   修改
EPOLL_CTL_DEL   删除

events:
EPOLLIN 有数据可读
EPOLLOUT 写数据
EPOLLPRI 有紧急数据需要读取
EPOLLERR 指定文件描述符发生错误
EPOLLHUP 指定的文件描述符挂起
EPOLLET 设置 epoll 为边沿触发,默认为水平触发
EPOLLONESHOT 一次性的监视

程序讲解

    堵塞 IO
    DECLARE_WAITQUEUE(wait, current);
    if (atomic_read(&dev->releasekey) == 0) {
        add_wait_queue(&dev->r_wait, &wait);
        __set_current_state(TASK_INTERRUPTIBLE); //设置任务状态
        schedule(); //进程调度,令进程进度进入休眠
        if (signal_pending(current)) { //若由信号唤醒则报错
            ret = -ERESTARTSYS;
            goto wait_error;
        }
    }
    remove_wait_queue(&dev->r_wait, &wait); //移除队列

wait_error:
    set_current_state(TASK_RUNNING); //运行状态
    remove_wait_queue(&dev->r_wait, &wait);//从队列去除
    return ret;
    非堵塞 IO
    if (filp->f_flags & O_NONBLOCK) {
        if (atomic_read(&dev->releasekey) == 0)
            return -EAGAIN; //没按下则返回重试值
    } else {
        ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
        if (ret) {
            goto wait_error;
        }
    }

//由应用程序调用 select 或 poll 时调用, 调用 poll_wait 将等待队列加入到 poll_table 中
unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct* wait)
{
    unsigned int mask = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*) filp->private_data;

    poll_wait(filp, &dev->r_wait, wait);

    if (atomic_read(&dev->releasekey)) {
        mask = POLLIN | POLLRDNORM;
    }
    return mask;
}

堵塞IO 例程

//堵塞方式IO
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "blockio"
#define KEY0VLAUE 0x01  //按键值
#define INVAKEY   0xFF  //无效按键值
#define KEY_NUM   1

//中断 IO 描述结构体
struct irq_keydesc {
    int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int, void*);
};

struct imx6uirq_dev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct timer_list timer;
    struct irq_keydesc irqkeydesc [KEY_NUM];
    unsigned char curkeynum;
    wait_queue_head_t r_wait; //读等待队列头
};

struct imx6uirq_dev imx6uirq;

static irqreturn_t key0_handler(int irq, void* dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *) dev_id;
    dev->curkeynum = 0;
    dev->timer.data = (volatile long) dev_id;
    //10ms 并激活定时器
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

void timer_function(unsigned long arg)
{
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*) arg;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];
    value = gpio_get_value(keydesc->gpio);
    if (0 == value) {
        atomic_set(&dev->keyvalue, keydesc->value);
    } else {
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1);
    }

    //唤醒进程
    if (atomic_read(&dev->releasekey)) {
        wake_up_interruptible(&dev->r_wait);
    }
}


static int keyio_init(void)
{
    unsigned char i = 0;
    char name[10];
    int ret = 0;


    imx6uirq.nd = of_find_node_by_path("/key");
    if (NULL == imx6uirq.nd) {
        return -EINVAL;
    }

    for (i = 0; i < KEY_NUM; i++) {
        imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpio", i);

        if (imx6uirq.irqkeydesc[i].gpio < 0) {
            printk("can't get key %d \n", i);
        }
    }

    for (i = 0; i < KEY_NUM; i++) {
        memset(imx6uirq.irqkeydesc[i].name, 0, sizeof (name));
        sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);
        gpio_request(imx6uirq.irqkeydesc[i].gpio, name); //向内核申请 GPIO
        gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);
        imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i); //获取中断号

#if 0
        imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif
        printk("key%d: gpio = %d, irqnum = %d \n", i, imx6uirq.irqkeydesc[i].gpio,
                                                    imx6uirq.irqkeydesc[i].irqnum);
    }

    //申请中断
    imx6uirq.irqkeydesc[0].handler = key0_handler;
    imx6uirq.irqkeydesc[0].value = KEY0VLAUE;

    for (i = 0; i < KEY_NUM; i++) {
        ret = request_irq(imx6uirq.irqkeydesc[i].irqnum,
                          imx6uirq.irqkeydesc[i].handler,
                          IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                          imx6uirq.irqkeydesc[i].name, &imx6uirq);

        if (0 > ret) {
            printk("irq %d request failed! \n", imx6uirq.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    //创建定时器
    init_timer(&imx6uirq.timer);
    imx6uirq.timer.function = timer_function;

    //初始化队列头
    init_waitqueue_head(&imx6uirq.r_wait);
    return 0;
}

static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6uirq;
    return 0;
}

static ssize_t imx6uirq_read(struct file *filp, char __user *buf,
                        size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0, releasekey = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
#if 0
    ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
    if (ret) {
        goto wait_error;
    }
#endif

    DECLARE_WAITQUEUE(wait, current);
    if (atomic_read(&dev->releasekey) == 0) {
        add_wait_queue(&dev->r_wait, &wait);
        __set_current_state(TASK_INTERRUPTIBLE); //设置任务状态
        schedule(); //进行一次进程调度,令进程进入休眠
        if (signal_pending(current)) { //判断是否因为信号而被唤醒,是则报错
            ret = -ERESTARTSYS;
            goto wait_error;
        }
    }
    remove_wait_queue(&dev->r_wait, &wait);

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) {
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof (keyvalue));
        } else {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); //清除按键标志
    } else {
        goto data_error;
    }
    return 0;

wait_error:
    set_current_state(TASK_RUNNING); //运行状态
    remove_wait_queue(&dev->r_wait, &wait);//从队列去除
    return ret;

data_error:
    return -EINVAL;
}

static struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .read = imx6uirq_read,
};

static int __init imx6uirq_init(void)
{
    if (imx6uirq.major) {
        imx6uirq.devid = MKDEV(imx6uirq.major, 0);
        register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    } else {
        alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
    }
    //注册字符设备
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);
    imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.class)) {
        return PTR_ERR(imx6uirq.class);
    }

    imx6uirq.device = device_create(imx6uirq.class, NULL,
                                  imx6uirq.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.device)) {
        return PTR_ERR(imx6uirq.device);
    }

    //初始化按键
    atomic_set(&imx6uirq.keyvalue, INVAKEY);
    atomic_set(&imx6uirq.releasekey, 0);
    keyio_init();
    return 0;
}

static void __exit imx6uirq_exit(void)
{
    unsigned int i = 0;
    del_timer_sync(&imx6uirq.timer);
    for (i = 0; i < KEY_NUM; i++) {
        free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
    }
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
    device_destroy(imx6uirq.class, imx6uirq.devid);
    class_destroy(imx6uirq.class);

}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zzz");

//APP 堵塞读 IO
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <linux/ioctl.h>

int main(int argc, char* argv[])
{
    int fd, ret = 0;
    char* filename;
    unsigned char data;

    if (argc != 2) {
        printf("Error Usage! \n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("file %s open failed ! \n", argv[1]);
        return -1;
    }

    while(1) {
       ret = read(fd, &data, sizeof (data));
       if (0 > ret) {
           //数据异常
       } else {
            if (data)
                printf("key value = %#X \n", data);
       }
    }

    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed !\n", argv[1]);
        return -1;
    }
    return 0;
}

非堵塞IO 例程

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "noblockio"
#define KEY0VLAUE 0x01  //按键值
#define INVAKEY   0xFF  //无效按键值
#define KEY_NUM   1

//中断 IO 描述结构体
struct irq_keydesc {
    int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int, void*);
};

struct imx6uirq_dev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    atomic_t keyvalue;
    atomic_t releasekey;
    struct timer_list timer;
    struct irq_keydesc irqkeydesc [KEY_NUM];
    unsigned char curkeynum;
    wait_queue_head_t r_wait; //读等待队列头
};

struct imx6uirq_dev imx6uirq;

static irqreturn_t key0_handler(int irq, void* dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *) dev_id;
    dev->curkeynum = 0;
    dev->timer.data = (volatile long) dev_id;
    //10ms 并激活定时器
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

void timer_function(unsigned long arg)
{
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*) arg;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];
    value = gpio_get_value(keydesc->gpio);
    if (0 == value) {
        atomic_set(&dev->keyvalue, keydesc->value);
    } else {
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1);
    }

    //唤醒进程
    if (atomic_read(&dev->releasekey)) {
        wake_up_interruptible(&dev->r_wait);
    }
}


static int keyio_init(void)
{
    unsigned char i = 0;
    char name[10];
    int ret = 0;


    imx6uirq.nd = of_find_node_by_path("/key");
    if (NULL == imx6uirq.nd) {
        return -EINVAL;
    }

    for (i = 0; i < KEY_NUM; i++) {
        imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpio", i);

        if (imx6uirq.irqkeydesc[i].gpio < 0) {
            printk("can't get key %d \n", i);
        }
    }

    for (i = 0; i < KEY_NUM; i++) {
        memset(imx6uirq.irqkeydesc[i].name, 0, sizeof (name));
        sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);
        gpio_request(imx6uirq.irqkeydesc[i].gpio, name); //向内核申请 GPIO
        gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);
        imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i); //获取中断号

#if 0
        imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif
        printk("key%d: gpio = %d, irqnum = %d \n", i, imx6uirq.irqkeydesc[i].gpio,
                                                    imx6uirq.irqkeydesc[i].irqnum);
    }

    //申请中断
    imx6uirq.irqkeydesc[0].handler = key0_handler;
    imx6uirq.irqkeydesc[0].value = KEY0VLAUE;

    for (i = 0; i < KEY_NUM; i++) {
        ret = request_irq(imx6uirq.irqkeydesc[i].irqnum,
                          imx6uirq.irqkeydesc[i].handler,
                          IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                          imx6uirq.irqkeydesc[i].name, &imx6uirq);

        if (0 > ret) {
            printk("irq %d request failed! \n", imx6uirq.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    //创建定时器
    init_timer(&imx6uirq.timer);
    imx6uirq.timer.function = timer_function;

    //初始化队列头
    init_waitqueue_head(&imx6uirq.r_wait);
    return 0;
}

static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6uirq;
    return 0;
}

static ssize_t imx6uirq_read(struct file *filp, char __user *buf,
                        size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0, releasekey = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

    if (filp->f_flags & O_NONBLOCK) {
        if (atomic_read(&dev->releasekey) == 0)
            return -EAGAIN;
    } else {
        ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
        if (ret) {
            goto wait_error;
        }
    }

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) {
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof (keyvalue));
        } else {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); //清除按键标志
    } else {
        goto data_error;
    }
    return 0;

wait_error:
    return ret;

data_error:
    return -EINVAL;
}

unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct* wait)
{
    unsigned int mask = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*) filp->private_data;

    poll_wait(filp, &dev->r_wait, wait);

    if (atomic_read(&dev->releasekey)) {
        mask = POLLIN | POLLRDNORM;
    }
    return mask;
}

static struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .read = imx6uirq_read,
    .poll = imx6uirq_poll,
};

static int __init imx6uirq_init(void)
{
    if (imx6uirq.major) {
        imx6uirq.devid = MKDEV(imx6uirq.major, 0);
        register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    } else {
        alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
    }
    //注册字符设备
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);
    imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.class)) {
        return PTR_ERR(imx6uirq.class);
    }

    imx6uirq.device = device_create(imx6uirq.class, NULL,
                                  imx6uirq.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.device)) {
        return PTR_ERR(imx6uirq.device);
    }

    //初始化按键
    atomic_set(&imx6uirq.keyvalue, INVAKEY);
    atomic_set(&imx6uirq.releasekey, 0);
    keyio_init();
    return 0;
}

static void __exit imx6uirq_exit(void)
{
    unsigned int i = 0;
    del_timer_sync(&imx6uirq.timer);
    for (i = 0; i < KEY_NUM; i++) {
        free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);
    }
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
    device_destroy(imx6uirq.class, imx6uirq.devid);
    class_destroy(imx6uirq.class);

}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zzz");

//非堵塞IO APP
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <linux/poll.h>
#include <linux/ioctl.h>

int main(int argc, char* argv[])
{
    int fd, ret = 0;
    char* filename;
    struct pollfd fds;
    fd_set readfds;
    struct timeval timeout;
    unsigned char data;

    if (argc != 2) {
        printf("Error Usage! \n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR | O_NONBLOCK);
    if (fd < 0) {
        printf("file %s open failed ! \n", argv[1]);
        return -1;
    }

#if 0
    fds.fd = fd;
    fds.events = POLLIN;
    while( 1) {
        ret = poll(&fds, 1, 500);
        if (ret) {
            ret = read(fd, &data, sizeof(data));
            if (ret < 0) {
                //读取错误
            } else {
                if (data)
                    printf("key value = %d \n", data);
            }
        } else if (ret == 0) {
            //超时
        } else if (ret < 0) {
            //错误
        }
    }
#endif

    while (1) {
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
        ret = select(fd+1, &readfds, NULL, NULL, &timeout);
        switch (ret) {
            case 0: //超时
            break;
        case -1:
            //错误
            break;
        default:
            //读取错误
            if (FD_ISSET(fd, &readfds)) {
                ret = read(fd, &data, sizeof (data));
                if (ret < 0) {
                    //读取错误
                } else {
                    if (data)
                        printf("key value = %d \n", data);
                }
            }
            break;
        }
    }

    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed !\n", argv[1]);
        return -1;
    }
    return 0;
}

编译方式

KERNELDIR := /home/xza/workspace/01_linux_kernel/test/imx_4.1.15_2.0.0_ga_rc3/
CURRENT_PATH := $(shell pwd)
obj-m := blockio.o

build: kernel_modules

kernel_modules:
	$(MAKE) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- EXTRA_CFLAGS=-fno-pic -C $(KERNELDIR) M=`pwd` modules 

clean:
	$(MAKE) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=`pwd` clean
驱动:make 
APP: arm-linux-gnueabihf-gcc blockio_app.c -o blockio
测试: ./blockio /dev/imx6uirq & 查看 top 可以看到 CPU 占用率很低
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章