目錄
- (一)poll輪詢的作用
- (二)poll輪詢相關的接口
- (三)poll使用流程
- (四)實例代碼
(一)poll輪詢的作用
以阻塞的方式打開文件,那么對多個文件讀寫時,若某個文件未準備好,則系統會處于讀寫阻塞,并影響其他文件的讀寫,poll輪訓就是實現既可使用輸入輸出流又不想阻塞在任何一個設備的讀寫操作
調用poll函數返回時,會返回一個文件是否可讀寫的標志狀態,用戶程序根據不同的標志狀態來讀寫相應的文件,實現阻塞方式打開但是非阻塞方式讀寫的結果
(二)poll輪詢相關的接口
- 系統層接口:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd *fds: 輪訓描述符的集合數據結構
struct pollfd {int fd; /* file descriptor */輪訓的文件描述符short events; /* requested events */對于輪訓的文件描述所檢測的事件類型short revents; /* returned events */對于輪訓的文件描述所返回的事件類型
};
nfds_t nfds:輪訓描述符的個數
int timeout:輪詢時間
請求事件和返回事件類型:
POLLIN There is data to read.
POLLOUT Writing is now possible, though a write larger that the available spacein a socket or pipe will still block (unless O_NONBLOCK is set).
POLLPRI: There is urgent data to read
POLLERR: Error condition (only returned in revents; ignored in events).POLLHUP: Hang up (only returned in revents; ignored in events).
POLLNVAL: Invalid request: fd not open (only returned in revents; ignored in events).
POLLRDNORM Equivalent to POLLIN.
POLLRDBAND Priority band data can be read (generally unused on Linux).POLLWRNOR Equivalent to POLLOUT.POLLWRBAND Priority data may be written.
RETURN VALUE On success, a positive number is returned; this is the number of structures which havenonzero revents fields (in other words, those descriptors with events or errors reported).A value of 0 indicates that the call timed out and no file descriptors were ready. Onerror, -1 is returned, and errno is set appropriately.
- 內核層接口:
unsigned int (*poll) (struct file *, struct poll_table_struct *);//file_operations結構體里
設置輪訓的等待隊列poll_wait:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{if (p && p->_qproc && wait_address)p->_qproc(filp, wait_address, p);
}
喚醒等待隊列節點wake_up_interruptible或wake_up:
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
/*** __wake_up - wake up threads blocked on a waitqueue.* @q: the waitqueue* @mode: which threads* @nr_exclusive: how many wake-one or wake-many threads to wake up* @key: is directly passed to the wakeup function** It may be assumed that this function implies a write memory barrier before* changing the task state if and only if any tasks are woken up.*/
(三)poll使用流程
1.內核層編寫poll相關的函數
unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)
2.在合適cdev_poll函數中調用poll_wait
poll_wait(fp,&waithead,table);//不會進行阻塞,添加等待隊列到輪詢列表中
3.在合適的地方調用wake_up_interruptible喚醒poll等待隊列
wake_up_interruptible(&waithead)
(四)實例代碼
假設要求創建多個設備節點,每個設備節點對應一個設備,在系統調用多次read接口來讀取底層設備的狀態。例如4個按鍵 ----- 4個設備節點 ----分別讀取4個設備節點文件的數據獲取按鍵的狀態-----不確定哪一個按鍵被按下
分析:
若采用阻塞方式操作:四個讀寫操作會相互阻塞,如按鍵1未被按下,則讀取不到數據,既是按鍵4被按下,也會被按鍵1的操作阻塞,獲取不到數據
若采用非阻塞操作:既是讀取不到數據,也會直接返回,對于監測按鍵的狀態,按鍵隨時可能被按下,所以需要的是循環檢測,采用非阻塞就會導致一直在進行無用功
采用多路io輪詢操作:檢測是文件描述符是否發生變化,變化就去操作對應的文件,否則阻塞在指定的操作
chrdev.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/poll.h>#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define INODENAME "mycdev"
int count=0;
dev_t dev=0;
int keyflag;//等待隊列第二個參數
char keyvalue[4]={-1,-1,-1,-1};
struct cdev * cdev =NULL;
struct class * cdevclass =NULL;
wait_queue_head_t waithead;struct Key
{unsigned int gpios;char * name;int num;unsigned int irq;
};struct Key key[]={{EXYNOS4_GPX3(2),"K1",0},{EXYNOS4_GPX3(3),"K2",1},{EXYNOS4_GPX3(4),"K3",2},{EXYNOS4_GPX3(5),"K4",3},
};irqreturn_t key_handler(int irq, void * dev)
{struct Key * tmp =(struct Key *)dev;int value[4];value[tmp->num]= gpio_get_value(tmp->gpios);gpio_set_value(EXYNOS4X12_GPM4(tmp->num),value[tmp->num]);keyflag = 1;keyvalue[tmp->num] = value[tmp->num];wake_up_interruptible(&waithead);return IRQ_HANDLED;
}int cdev_open (struct inode *node, struct file *file)
{printk("cdev_open is install\n");return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{int ret =0;if((fp->f_flags & O_NONBLOCK)== O_NONBLOCK)//非阻塞{if(!keyflag)return -EAGAIN;}else//阻塞{wait_event_interruptible(waithead,keyflag);}keyflag =0;ret = copy_to_user(buf,keyvalue,4);if(ret <0)return -EFAULT;printk("cdev_read is install\n");return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{printk("cdev_write is install\n");return 0;
}
unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)
{unsigned int bitmask =0;printk("this is poll_wait before\n");poll_wait(fp,&waithead,table);//不會進行阻塞,添加等待隊列到輪詢列表中if(keyflag)//必須有判斷標志bitmask = POLLIN;printk("this is poll_wait after\n");return bitmask;
}int cdev_release (struct inode *node, struct file *fp)
{printk("cdev_release is install\n");return 0;
}
struct file_operations fop={.open=cdev_open,.read=cdev_read,.write=cdev_write,.release=cdev_release,.poll = cdev_poll,
};void mycdev_add(void)
{//1.申請設備號--動態int ret =alloc_chrdev_region(&dev,0, CDEVCOUNT, CDEVNAME);if(ret)return ;//初始化cdev結構體cdev = cdev_alloc();if(!cdev){goto out;}cdev_init(cdev,&fop);//添加字符設備到系統中ret =cdev_add(cdev,dev, CDEVCOUNT);if(ret){goto out1;}//創建設備類cdevclass = class_create(THIS_MODULE, INODENAME);if(IS_ERR(cdevclass)){goto out2;}for (count=0;count<CDEVCOUNT;count++)device_create(cdevclass, NULL, dev+count, NULL, "mydevice%d",count);out:unregister_chrdev_region(dev,CDEVCOUNT); return ;
out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;
out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;
}static int __init dev_module_init(void)
{int ret=0,i=0;unsigned long flags= IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_SHARED;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);ret =request_irq(key[i].irq, key_handler,flags,key[i].name,(void *) &key[i]);}init_waitqueue_head(&waithead);//創建等待隊列頭mycdev_add();printk("this is dev_module_init \n");return 0;
}static void __exit dev_module_cleanup(void)
{int i=0;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);free_irq(key[i].irq,(void *)&key[i]);}for(count=0;count<CDEVCOUNT;count++){device_destroy(cdevclass, dev+count);}class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev, CDEVCOUNT);kfree(cdev);printk("this is dev_module_cleanup\n");
}module_init(dev_module_init);
module_exit(dev_module_cleanup);
MODULE_LICENSE("GPL");
chr_app.c
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>int ret=0;
struct pollfd fds[1];
char status[]={-1,-1,-1,-1};
char key[]={-1,-1,-1,-1};
int main(int argc, char *argv[])
{int i=0,fd= open(argv[1],O_RDWR/*|O_NONBLOCK*/);if(fd== -1){perror("open");return -1;}while(1){fds[0].fd = fd;fds[0].events = POLLIN;ret = poll(fds,1,5000);//只有錯誤、滿足條件、超時才會返回,不滿足條件在這死等if(ret == -1){perror("poll\n");return -1;}else if(ret == 0){printf("timeout\n");}else if(ret & POLLIN){printf("ret >0\n");if(read(fd,status,4)<0)/*疑問:把read屏蔽掉打印出錯*/{printf("按鍵狀態未改變\n");sleep(1);continue;}for(i=0;i<4;i++){if(status[i] != key[i] ){printf("key%d is %s\n",i,status[i]?"up":"down");status[i]=key[i];}}}}close(fd);return 0;
}
Makefile
CFLAG =-C
TARGET = chrdev
TARGET1 = chr_app
KERNEL = /mydriver/linux-3.5
obj-m += $(TARGET).oall:make $(CFLAG) $(KERNEL) M=$(PWD)arm-linux-gcc -o $(TARGET1) $(TARGET1).c
clean:make $(CFLAG) $(KERNEL) M=$(PWD) clean
本文章僅供學習交流用禁止用作商業用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作
微信公眾號:zhjj0729
微博:文藝to青年