Linux高级字符设备之Poll操作
在用戶程序中,select()和poll()也是與設備阻塞與非阻塞訪問息息相關的,使用非阻塞I/O的應用程序通常會使用select和poll系統調用查詢是否可對設備進行無阻塞的訪問。select系統調用最終會引發設備驅動中的poll函數被執行。
一、select()系統調用:
用于多路監控,當沒有一個文件滿足要求時,select將阻塞調用進程。
1.select()原型:
返回值:
(1)正常情況下返回滿足要求的文件描述符個數;
(2)經過了timeout等待后仍無文件滿足要求,返回0;
(3)如果select被某個信號中斷,將返回-1并設置errno為EINTR;
(4)若出錯,返回-1并設置相應的errno;
2.select的使用方法:
(1)將要監控的文件添加到文件描述符集;
(2)調用select開始監控;
(3)判斷文件是否發生變化;
?
3.系統提供四個宏對描述符集進行操作:
void FD_SET(int fd, fd_set *fdset); //將文件描述符fd添加到文件描述符集fdset中; void FD_CLR(int fd, fd_set *fdset); //從文件描述符集fdset中清除文件描述符fd; void FD_ISSET(int fd, fd_set *fdset); //在調用select后使用FD_ISSET來檢測文件描述符集中的文件fd發生了變化 void FD_ZERO(fd_set *fdset);//清空文件描述符集?
二、Poll方法:
1.poll函數原型:
unsigned int(*poll)(struct file *filp, struct poll_table *wait); //第一個參數為file結構體指針,第二個參數為輪詢表指針。這個函數應該進行以下兩項工作:
(1)對可能引起設備文件狀態變化的等待隊列調用poll_wait()函數,將對應等待隊列添加到poll_table中;?
(2)返回表示是否能對設備進行無阻塞可讀或可寫訪問的掩碼;
位掩碼:POLLRDNORM, POLLIN,POLLOUT,POLLWRNORM
設備可讀,通常返回:(POLLIN | POLLRDNORM)
設備可寫,通常返回:(POLLOUT | POLLWRNORM)
三、調用過程:
Linux下select調用的過程:
1、用戶層應用程序調用select(),底層調用poll())
2、核心層調用sys_select() ------> do_select()
最終調用文件描述符fd對應的struct file類型變量的struct file_operations *f_op的poll函數。
poll指向的函數返回當前可否讀寫的信息。
1)如果當前可讀寫,返回讀寫信息。
2)如果當前不可讀寫,則阻塞進程,并等待驅動程序喚醒,重新調用poll函數,或超時返回。
3、驅動需要實現poll函數。
當驅動發現有數據可以讀寫時,通知核心層,核心層重新調用poll指向的函數查詢信息。
在中斷中使用wake_up_interruptible(&wait_q)喚醒等待隊列。
?
四、實例分析:
1.memdev.h
#ifndef _MEMDEV_H_ #define _MEMDEV_H_#ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0 /*預設的mem的主設備號*/ #endif#ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 /*設備數*/ #endif#ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem設備描述結構體*/ struct mem_dev { char *data; unsigned long size; wait_queue_head_t inq; };#endif /* _MEMDEV_H_ */?
2.memdev.c
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h>#include <linux/poll.h> #include "memdev.h"static mem_major = MEMDEV_MAJOR; bool have_data = false; /*表明設備有足夠數據可供讀*/module_param(mem_major, int, S_IRUGO);struct mem_dev *mem_devp; /*設備結構體指針*/struct cdev cdev; /*文件打開函數*/ int mem_open(struct inode *inode, struct file *filp) {struct mem_dev *dev;/*獲取次設備號*/int num = MINOR(inode->i_rdev);if (num >= MEMDEV_NR_DEVS) return -ENODEV;dev = &mem_devp[num];/*將設備描述結構指針賦值給文件私有數據指針*/filp->private_data = dev;return 0; }/*文件釋放函數*/ int mem_release(struct inode *inode, struct file *filp) {return 0; }/*讀函數*/ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*//*判斷讀位置是否有效*/if (p >= MEMDEV_SIZE)return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;while (!have_data) /* 沒有數據可讀,考慮為什么不用if,而用while */{if (filp->f_flags & O_NONBLOCK)return -EAGAIN;wait_event_interruptible(dev->inq,have_data);}/*讀數據到用戶空間*/if (copy_to_user(buf, (void*)(dev->data + p), count)){ret = - EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);}have_data = false; /* 表明不再有數據可讀 *//* 喚醒寫進程 */return ret; }/*寫函數*/ static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*//*分析和獲取有效的寫長度*/if (p >= MEMDEV_SIZE)return 0;if (count > MEMDEV_SIZE - p)count = MEMDEV_SIZE - p;/*從用戶空間寫入數據*/if (copy_from_user(dev->data + p, buf, count))ret = - EFAULT;else{*ppos += count;ret = count;printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);}have_data = true; /* 有新的數據可讀 *//* 喚醒讀進程 */wake_up(&(dev->inq));return ret; }/* seek文件定位函數 */ static loff_t mem_llseek(struct file *filp, loff_t offset, int whence) { loff_t newpos;switch(whence) {case 0: /* SEEK_SET */newpos = offset;break;case 1: /* SEEK_CUR */newpos = filp->f_pos + offset;break;case 2: /* SEEK_END */newpos = MEMDEV_SIZE -1 + offset;break;default: /* can't happen */return -EINVAL;}if ((newpos<0) || (newpos>MEMDEV_SIZE))return -EINVAL;filp->f_pos = newpos;return newpos;} unsigned int mem_poll(struct file *filp, poll_table *wait) {struct mem_dev *dev = filp->private_data; unsigned int mask = 0;/*將等待隊列添加到poll_table */poll_wait(filp, &dev->inq, wait);if (have_data) mask |= POLLIN | POLLRDNORM; /* readable */return mask; }/*文件操作結構體*/ static const struct file_operations mem_fops = {.owner = THIS_MODULE,.llseek = mem_llseek,.read = mem_read,.write = mem_write,.open = mem_open,.release = mem_release,.poll = mem_poll, };/*設備驅動模塊加載函數*/ static int memdev_init(void) {int result;int i;dev_t devno = MKDEV(mem_major, 0);/* 靜態申請設備號*/if (mem_major)result = register_chrdev_region(devno, 2, "memdev");else /* 動態分配設備號 */{result = alloc_chrdev_region(&devno, 0, 2, "memdev");mem_major = MAJOR(devno);} if (result < 0)return result;/*初始化cdev結構*/cdev_init(&cdev, &mem_fops);cdev.owner = THIS_MODULE;cdev.ops = &mem_fops;/* 注冊字符設備 */cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);/* 為設備描述結構分配內存*/mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);if (!mem_devp) /*申請失敗*/{result = - ENOMEM;goto fail_malloc;}memset(mem_devp, 0, sizeof(struct mem_dev));/*為設備分配內存*/for (i=0; i < MEMDEV_NR_DEVS; i++) {mem_devp[i].size = MEMDEV_SIZE;mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);memset(mem_devp[i].data, 0, MEMDEV_SIZE);/*初始化等待隊列*/init_waitqueue_head(&(mem_devp[i].inq));//init_waitqueue_head(&(mem_devp[i].outq)); }return 0;fail_malloc: unregister_chrdev_region(devno, 1);return result; }/*模塊卸載函數*/ static void memdev_exit(void) {cdev_del(&cdev); /*注銷設備*/kfree(mem_devp); /*釋放設備結構體內存*/unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*釋放設備號*/ }MODULE_AUTHOR("David Xie"); MODULE_LICENSE("GPL");module_init(memdev_init); module_exit(memdev_exit);
3.app-write.c
?
4.app-read.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h>int main() {int fd;fd_set rds;int ret;char Buf[128];/*初始化Buf*/strcpy(Buf,"memdev is char dev!");printf("BUF: %s\n",Buf);/*打開設備文件*/fd = open("/dev/memdev0",O_RDWR);FD_ZERO(&rds);FD_SET(fd, &rds);/*清除Buf*/strcpy(Buf,"Buf is NULL!");printf("Read BUF1: %s\n",Buf);ret = select(fd + 1, &rds, NULL, NULL, NULL);if (ret < 0) {printf("select error!\n");exit(1);}if (FD_ISSET(fd, &rds)) read(fd, Buf, sizeof(Buf)); /*檢測結果*/printf("Read BUF2: %s\n",Buf);close(fd);return 0; }總結
以上是生活随笔為你收集整理的Linux高级字符设备之Poll操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux设备驱动之Ioctl控制
- 下一篇: Linux设备驱动之mmap设备操作