Linux之poll机制分析
Posted program_Li
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux之poll机制分析相关的知识,希望对你有一定的参考价值。
应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open()、read()、write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直
到被唤醒。如果是使用非阻塞方式,就需要采用poll/select机制,而且打开文件时标记文件的访问权限位为O_NONBLOCK。
1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
如果参数timeout设为:NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。
1 int poll(struct pollfd *fds, nfds_t nfds, int timeout);这两个函数其实本质类似.
fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 请求的事件类型,监视驱动文件的事件掩码 */
short revents; /* 驱动文件实际返回的事件 */
} ;
nfds 监测驱动文件的个数
timeout 超时时间,单位为ms
事件类型events 可以为下列值:
POLLIN 有数据可读
POLLRDNORM 有普通数据可读,等效与POLLIN
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLER 指定的文件描述符发生错误
POLLHUP 指定的文件描述符挂起事件
POLLNVAL 无效的请求,打不开指定的文件描述符
返回值
有事件发生 返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告)
超时 返回0;
失败 返回-1,并设置errno为错误类型
理解select模型:
理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每个bit 可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。
注意:没有事件 发生的fd=5被清空。
从内核态理解poll机制
我们从应用程序直接调用poll函数,系统会走以下流程
1 app:poll 2 kernel:sys_poll 3 do_sys_poll 4 poll_initwait(&table) 5 do_poll(nfds, head, &table, timeout) 6 for (;;) { 7 for (; pfd != pfd_end; pfd++) { /* 可以监测多个驱动设备所产生的事件 */ 8 if (do_pollfd(pfd, pt)) { 9 count++; 10 pt = NULL; 11 } 12 if (count || !*timeout || signal_pending(current)) 13 break; 14 __timeout = schedule_timeout(__timeout); 15 } 16 } 17 18 19 do_pollfd(pfd, pt){ 20 ... 21 if (file->f_op && file->f_op->poll) 22 mask = file->f_op->poll(file, pwait); 23 return mask; 24 ... 25 }
使用poll_initwait(&table),就是将__pollwait设为回调函数,后面会去调用驱动程序的poll函数,poll函数调用pollwait就等于调用__pollwait,将当前进程加入到等待队列中。然后一直在循环,do_pollfd就是去
调用驱动程序的poll函数,poll函数开始调用pollwait就等于调用__pollwait回调函数,将当前进程加入到等待队列中,以便唤醒休眠后的当前进程。然后返回当前驱动设备的状态(mask). 如果do_pollfd返回的
mask为非0,即count非0,就会马上返回,应用程序就可以使用FD_ISSET了解此时设备状态。当然,如果超时或者此进程有其他信号要处理,也会马上返回,但是应用程序使用FD_ISSET了解到此时设备状
态还是不可用时,又继续轮询。如果do_pollfd返回的mask为0,而且未超时且未有其他信号发生,就会进程调度,让此进程休眠。在前面已经将此进程加入到驱动程序的等待队列中了,如果设备可用时,就会
唤醒等待队列中的进程,也就唤醒了此进程,又去do_pollfd(pfd, pt)。
实例:基于<<Linux设备驱动开发详解:基于最新的Linux4.0内核.pdf>>第8.2章节
驱动程序的poll函数
1 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait) 2 { 3 unsigned int mask =0; 4 struct globalfifo_dev *dev = filp->private_data; 5 6 mutex_lock(&dev->mutex); 7 8 poll_wait(filp, &dev->w_wait, wait);//将当前进程加入到写等待队列 9 poll_wait(filp, &dev->r_wait, wait); //将当前进程加入到读等待队列 10 11 if(dev->current_len != 0) 12 mask |=POLLIN | POLLRDNORM;//当有数据时,报告可读状态 13 if(dev->current_len != GLOBALMEM_SIZE) 14 mask |=POLLOUT | POLLWRNORM;//当缓冲区未满时,报告可写状态 15 16 mutex_unlock(&dev->mutex); 17 return mask; 18 }
使用select监控globalfifo是否可非阻塞读、 写的应用程序
1 #include "stdio.h" 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <sys/time.h> 6 #include <unistd.h> 7 #include <errno.h> 8 #define FIFO_CLEAR (0x01) 9 void main(int argc, char **argv) 10 { 11 int fd; 12 int err; 13 fd_set rfds,wfds; 14 struct timeval timeout; 15 timeout.tv_sec = 5; //设置超时时间为5s 16 17 fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK); 18 if(fd == -1) 19 printf("open fail\\n"); 20 else{ 21 if (ioctl(fd, FIFO_CLEAR, 0) < 0) 22 printf("ioctl command failed\\n"); 23 while(1){ 24 FD_ZERO(&rfds); 25 FD_ZERO(&wfds); 26 FD_SET(fd,&rfds); 27 FD_SET(fd,&wfds); 28 29 err = select(fd+1, &rfds, &wfds, NULL, &timeout); 30 if(err == -1) 31 printf("select fail:0x%x\\n",errno); 32 if(FD_ISSET(fd, &rfds)) 33 printf("Poll monitor:can be read\\n"); 34 if(FD_ISSET(fd, &wfds)) 35 printf("Poll monitor:can be write\\n"); 36 } 37 } 38 return 0; 39 }
驱动程序全部源码:
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/init.h> 4 #include <linux/cdev.h> 5 #include <linux/slab.h> 6 #include <linux/uaccess.h> 7 #include <linux/poll.h> 8 9 //#define GLOBALMEM_SIZE 0x1000 10 #define GLOBALMEM_SIZE 0x10 11 #define GLOBALMEM_MAJOR 230 12 #define GLOBALMEM_MAGIC \'g\' 13 //#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0) 14 #define MEM_CLEAR (0x01) 15 static int globalfifo_major = GLOBALMEM_MAJOR; 16 module_param(globalfifo_major, int, S_IRUGO); 17 18 struct globalfifo_dev { 19 struct cdev cdev; 20 unsigned int current_len; 21 unsigned char mem[GLOBALMEM_SIZE]; 22 struct mutex mutex; 23 wait_queue_head_t r_wait; 24 wait_queue_head_t w_wait; 25 }; 26 27 struct globalfifo_dev *globalfifo_devp; 28 29 static int globalfifo_open(struct inode *inode, struct file *filp) 30 { 31 filp->private_data = globalfifo_devp; 32 return 0; 33 } 34 static int globalfifo_release(struct inode *inode, struct file *filp) 35 { 36 return 0; 37 } 38 static ssize_t globalfifo_read(struct file *filp, char __user * buf, size_t size, 39 loff_t * ppos) 40 { 41 unsigned int count = size; 42 int ret = 0; 43 struct globalfifo_dev *dev = filp->private_data; 44 DECLARE_WAITQUEUE(wait, current); 45 46 mutex_lock(&dev->mutex); 47 add_wait_queue(&dev->r_wait, &wait); 48 49 while(dev->current_len ==0){ 50 if(filp->f_flags & O_NONBLOCK){ 51 ret = -EAGAIN; 52 goto out; 53 } 54 55 set_current_state(TASK_INTERRUPTIBLE); 56 mutex_unlock(&dev->mutex); 57 58 schedule(); 59 if(signal_pending(current)){ 60 ret = -ERESTARTSYS; 61 goto out2; 62 } 63 mutex_lock(&dev->mutex); 64 65 } 66 67 if (count > dev->current_len) 68 count = dev->current_len; 69 70 if (copy_to_user(buf, dev->mem, count)) { 71 ret = -EFAULT; 72 goto out; 73 } else { 74 memcpy(dev->mem, dev->mem+count, dev->current_len - count); 75 dev->current_len -=count; 76 printk(KERN_INFO "read %d bytes(s) current_len %d\\n", count, dev->current_len); 77 wake_up_interruptible(&dev->w_wait); 78 ret = count; 79 } 80 81 out: 82 mutex_unlock(&dev->mutex); 83 out2: 84 remove_wait_queue(&dev->r_wait, &wait); 85 set_current_state(TASK_RUNNING); 86 87 return ret; 88 } 89 90 static ssize_t globalfifo_write(struct file *filp, const char __user * buf, 91 size_t size, loff_t * ppos) 92 { 93 unsigned int count = size; 94 int ret = 0; 95 struct globalfifo_dev *dev = filp->private_data; 96 DECLARE_WAITQUEUE(wait, current); 97 98 mutex_lock(&dev->mutex); 99 add_wait_queue(&dev->w_wait, &wait); 100 101 while(dev->current_len == GLOBALMEM_SIZE){ 102 if(filp->f_flags & O_NONBLOCK){ 103 ret = -EAGAIN; 104 goto out; 105 } 106 107 set_current_state(TASK_INTERRUPTIBLE); 108 mutex_unlock(&dev->mutex); 109 schedule(); 110 if(signal_pending(current)){ 111 ret = -ERESTARTSYS; 112 goto out2; 113 } 114 mutex_lock(&dev->mutex); 115 116 } 117 118 if (count > (GLOBALMEM_SIZE - dev->current_len)) 119 count = (GLOBALMEM_SIZE - dev->current_len); 120 121 if (copy_from_user(dev->mem + dev->current_len, buf, count)){ 122 ret = -EFAULT; 123 goto out; 124 } 125 else { 126 dev->current_len += count; 127 wake_up_interruptible(&dev->r_wait); 128 ret = count; 129 printk(KERN_INFO "written %d bytes(s) current_len %d\\n", count, dev->current_len); 130 } 131 out: 132 mutex_unlock(&dev->mutex); 133 out2: 134 remove_wait_queue(&dev->w_wait, &wait); 135 set_current_state(TASK_RUNNING); 136 return ret; 137 } 138 static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig) 139 { 140 loff_t ret = 0; 141 switch (orig) { 142 case 0: /* ´ÓÎļþ¿ªÍ·Î»ÖÃseek */ 143 if (offset< 0) { 144 ret = -EINVAL; 145 break; 146 } 147 if ((unsigned int)offset > GLOBALMEM_SIZE) { 148 ret = -EINVAL; 149 break; 150 } 151 filp->f_pos = (unsigned int)offset; 152 ret = filp->f_pos; 153 break; 154 case 1: /* ´ÓÎļþµ±Ç°Î»ÖÿªÊ¼seek */ 155 if ((filp->f_pos + offset) > GLOBALMEM_SIZE) { 156 ret = -EINVAL; 157 break; 158 } 159 if ((filp->f_pos + offset) < 0) { 160 ret = -EINVAL; 161 break; 162 } 163 filp->f_pos += offset; 164 ret = filp->f_pos; 165 break; 166 default: 167 ret = -EINVAL; 168 break; 169 } 170 return ret; 171 } 172 static long globalfifo_ioctl(struct file *filp, unsigned int cmd, 173 unsigned long arg) 174 { 175 struct globalfifo_dev *dev = filp->private_data; 176 switch (cmd) { 177 case MEM_CLEAR: 178 mutex_lock(&dev->mutex); 179 memset(dev->mem, 0, GLOBALMEM_SIZE); 180 dev->current_len =0; 181 printk(KERN_INFO "globalfifo is set to zero\\n"); 182 mutex_unlock(&dev->mutex); 183 break; 184 default: 185 return -EINVAL; 186 } 187 188 return 0; 189 } 190 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait) 191 { 192 unsigned int mask =0; 193 struct globalfifo_dev *dev = filp->private_data; 194 195 mutex_lock(&dev->mutex); 196 197 poll_wait(filp, &dev->w_wait, wait); 198 poll_wait(filp, &dev->r_wait, wait); 199 200 if(dev->current_len != 0) 201 mask |=POLLIN | POLLRDNORM; 202 if(dev->current_len != GLOBALMEM_SIZE) 203 mask |=POLLOUT | POLLWRNORM; 204 205 mutex_unlock(&dev->mutex); 206 return mask; 207 } 208 209 210 static const struct file_operations globalfifo_fops = { 211 .owner = THIS_MODULE, 212 .llseek = globalfifo_llseek, 213 .read = globalfifo_read, 214 .write = globalfifo_write, 215 .unlocked_ioctl = globalfifo_ioctl, 216 .open = globalfifo_open, 217 .poll = globalfifo_poll, 218 .release = globalfifo_release, 219 }; 220 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index) 221 { 222 int err, devno = MKDEV(globalfifo_major, index); 223 cdev_init(&dev->cdev, &globalfifo_fops); 224 dev->cdev.owner = THIS_MODULE; 225 err = cdev_add(&dev->cdev, devno, 1); 226 if (err) 227 printk(KERN_NOTICE "Error %d adding globalfifo%d", err, index); 228 } 229 static int __init globalfifo_init(void) 230 { 231 int ret; 232 dev_t devno = MKDEV(globalfifo_major, 0); 233 234 if (globalfifo_major) 235 ret = register_chrdev_region(devno, 1, "globalfifo"); 236 else { 237 ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo"); 238 globalfifo_major = MAJOR(devno); 239 } 240 if (ret < 0) 241 return ret; 242 243 globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL); 244 if (!globalfifo_devp) { 245 ret = -ENOMEM; 246 goto fail_malloc; 247 } 248 globalfifo_setup_cdev(globalfifo_devp, 0); 249 250 mutex_init(&globalfifo_devp->mutex); 251 init_waitqueue_head(&globalfifo_devp->r_wait); 252 init_waitqueue_head(&globalfifo_devp->w_wait); 253 return 0; 254 255 fail_malloc: 256 unregister_chrdev_region(devno, 1); 257 return ret; 258 } 259 260 static void __exit globalfifo_exit(void) 261 { 262 cdev_del(&globalfifo_devp->cdev); 263 kfree(globalfifo_devp); 264 unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); 265 } 266 module_init(globalfifo_init); 267 module_exit(globalfifo_exit); 268 269 MODULE_LICENSE("GPL v2");
测试:将应用程序设置为后台执行。
当无数据时.
当有数据时但未满时.
当数据满时.
此文源码基于内核源码版本为linux-2.6.22.6
以上是关于Linux之poll机制分析的主要内容,如果未能解决你的问题,请参考以下文章