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 佔用率很低
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章