应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open()、read()、write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直

到被唤醒。如果是使用非阻塞方式,就需要采用poll/select机制,而且打开文件时标记文件的访问权限位为O_NONBLOCK。

 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将超时返回。

 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函数,系统会走以下流程

 app:poll
kernel:sys_poll
do_sys_poll
poll_initwait(&table)
do_poll(nfds, head, &table, timeout)
for (;;) {
for (; pfd != pfd_end; pfd++) { /* 可以监测多个驱动设备所产生的事件 */
if (do_pollfd(pfd, pt)) {
count++;
pt = NULL;
}
if (count || !*timeout || signal_pending(current))
break;
__timeout = schedule_timeout(__timeout);
}
} do_pollfd(pfd, pt){
...
if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, pwait);
return mask;
...
}

使用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函数

 static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
{
unsigned int mask =;
struct globalfifo_dev *dev = filp->private_data; mutex_lock(&dev->mutex); poll_wait(filp, &dev->w_wait, wait);//将当前进程加入到写等待队列
poll_wait(filp, &dev->r_wait, wait); //将当前进程加入到读等待队列 if(dev->current_len != )
mask |=POLLIN | POLLRDNORM;//当有数据时,报告可读状态
if(dev->current_len != GLOBALMEM_SIZE)
mask |=POLLOUT | POLLWRNORM;//当缓冲区未满时,报告可写状态 mutex_unlock(&dev->mutex);
return mask;
}

使用select监控globalfifo是否可非阻塞读、 写的应用程序

 #include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#define FIFO_CLEAR (0x01)
void main(int argc, char **argv)
{
int fd;
int err;
fd_set rfds,wfds;
struct timeval timeout;
timeout.tv_sec = ; //设置超时时间为5s fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
if(fd == -)
printf("open fail\n");
else{
if (ioctl(fd, FIFO_CLEAR, ) < )
printf("ioctl command failed\n");
while(){
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd,&rfds);
FD_SET(fd,&wfds); err = select(fd+, &rfds, &wfds, NULL, &timeout);
if(err == -)
printf("select fail:0x%x\n",errno);
if(FD_ISSET(fd, &rfds))
printf("Poll monitor:can be read\n");
if(FD_ISSET(fd, &wfds))
printf("Poll monitor:can be write\n");
}
}
return ;
}

驱动程序全部源码:

 #include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/poll.h> //#define GLOBALMEM_SIZE 0x1000
#define GLOBALMEM_SIZE 0x10
#define GLOBALMEM_MAJOR 230
#define GLOBALMEM_MAGIC 'g'
//#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0)
#define MEM_CLEAR (0x01)
static int globalfifo_major = GLOBALMEM_MAJOR;
module_param(globalfifo_major, int, S_IRUGO); struct globalfifo_dev {
struct cdev cdev;
unsigned int current_len;
unsigned char mem[GLOBALMEM_SIZE];
struct mutex mutex;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
}; struct globalfifo_dev *globalfifo_devp; static int globalfifo_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalfifo_devp;
return ;
}
static int globalfifo_release(struct inode *inode, struct file *filp)
{
return ;
}
static ssize_t globalfifo_read(struct file *filp, char __user * buf, size_t size,
loff_t * ppos)
{
unsigned int count = size;
int ret = ;
struct globalfifo_dev *dev = filp->private_data;
DECLARE_WAITQUEUE(wait, current); mutex_lock(&dev->mutex);
add_wait_queue(&dev->r_wait, &wait); while(dev->current_len ==){
if(filp->f_flags & O_NONBLOCK){
ret = -EAGAIN;
goto out;
} set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex); schedule();
if(signal_pending(current)){
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex); } if (count > dev->current_len)
count = dev->current_len; if (copy_to_user(buf, dev->mem, count)) {
ret = -EFAULT;
goto out;
} else {
memcpy(dev->mem, dev->mem+count, dev->current_len - count);
dev->current_len -=count;
printk(KERN_INFO "read %d bytes(s) current_len %d\n", count, dev->current_len);
wake_up_interruptible(&dev->w_wait);
ret = count;
} out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->r_wait, &wait);
set_current_state(TASK_RUNNING); return ret;
} static ssize_t globalfifo_write(struct file *filp, const char __user * buf,
size_t size, loff_t * ppos)
{
unsigned int count = size;
int ret = ;
struct globalfifo_dev *dev = filp->private_data;
DECLARE_WAITQUEUE(wait, current); mutex_lock(&dev->mutex);
add_wait_queue(&dev->w_wait, &wait); while(dev->current_len == GLOBALMEM_SIZE){
if(filp->f_flags & O_NONBLOCK){
ret = -EAGAIN;
goto out;
} set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&dev->mutex);
schedule();
if(signal_pending(current)){
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&dev->mutex); } if (count > (GLOBALMEM_SIZE - dev->current_len))
count = (GLOBALMEM_SIZE - dev->current_len); if (copy_from_user(dev->mem + dev->current_len, buf, count)){
ret = -EFAULT;
goto out;
}
else {
dev->current_len += count;
wake_up_interruptible(&dev->r_wait);
ret = count;
printk(KERN_INFO "written %d bytes(s) current_len %d\n", count, dev->current_len);
}
out:
mutex_unlock(&dev->mutex);
out2:
remove_wait_queue(&dev->w_wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static loff_t globalfifo_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = ;
switch (orig) {
case : /* ´ÓÎļþ¿ªÍ·Î»ÖÃseek */
if (offset< ) {
ret = -EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case : /* ´ÓÎļþµ±Ç°Î»ÖÿªÊ¼seek */
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < ) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static long globalfifo_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;
switch (cmd) {
case MEM_CLEAR:
mutex_lock(&dev->mutex);
memset(dev->mem, , GLOBALMEM_SIZE);
dev->current_len =;
printk(KERN_INFO "globalfifo is set to zero\n");
mutex_unlock(&dev->mutex);
break;
default:
return -EINVAL;
} return ;
}
static unsigned int globalfifo_poll(struct file * filp, poll_table * wait)
{
unsigned int mask =;
struct globalfifo_dev *dev = filp->private_data; mutex_lock(&dev->mutex); poll_wait(filp, &dev->w_wait, wait);
poll_wait(filp, &dev->r_wait, wait); if(dev->current_len != )
mask |=POLLIN | POLLRDNORM;
if(dev->current_len != GLOBALMEM_SIZE)
mask |=POLLOUT | POLLWRNORM; mutex_unlock(&dev->mutex);
return mask;
} static const struct file_operations globalfifo_fops = {
.owner = THIS_MODULE,
.llseek = globalfifo_llseek,
.read = globalfifo_read,
.write = globalfifo_write,
.unlocked_ioctl = globalfifo_ioctl,
.open = globalfifo_open,
.poll = globalfifo_poll,
.release = globalfifo_release,
};
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
int err, devno = MKDEV(globalfifo_major, index);
cdev_init(&dev->cdev, &globalfifo_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, );
if (err)
printk(KERN_NOTICE "Error %d adding globalfifo%d", err, index);
}
static int __init globalfifo_init(void)
{
int ret;
dev_t devno = MKDEV(globalfifo_major, ); if (globalfifo_major)
ret = register_chrdev_region(devno, , "globalfifo");
else {
ret = alloc_chrdev_region(&devno, , , "globalfifo");
globalfifo_major = MAJOR(devno);
}
if (ret < )
return ret; globalfifo_devp = kzalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
if (!globalfifo_devp) {
ret = -ENOMEM;
goto fail_malloc;
}
globalfifo_setup_cdev(globalfifo_devp, ); mutex_init(&globalfifo_devp->mutex);
init_waitqueue_head(&globalfifo_devp->r_wait);
init_waitqueue_head(&globalfifo_devp->w_wait);
return ; fail_malloc:
unregister_chrdev_region(devno, );
return ret;
} static void __exit globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev);
kfree(globalfifo_devp);
unregister_chrdev_region(MKDEV(globalfifo_major, ), );
}
module_init(globalfifo_init);
module_exit(globalfifo_exit); MODULE_LICENSE("GPL v2");

测试:将应用程序设置为后台执行。

当无数据时.

当有数据时但未满时.

当数据满时.

此文源码基于内核源码版本为linux-2.6.22.6

参考:https://www.cnblogs.com/amanlikethis/p/6915485.html

http://www.cnblogs.com/shihaochangeworld/p/5747490.html

Linux之poll机制分析的更多相关文章

  1. Linux通信之poll机制分析

    poll机制分析 韦东山 2009.12.10 所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的 ...

  2. linux RCU锁机制分析

    openVswitch(OVS)源代码之linux RCU锁机制分析 分类: linux内核  |  标签: 云计算,openVswitch,linux内核,RCU锁机制  |  作者: yuzhih ...

  3. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  4. 字符设备驱动笔记——poll机制分析(七)

    poll机制分析 所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open. ...

  5. Linux kernel workqueue机制分析

    Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...

  6. Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  7. poll机制分析

    更多文档:http://pan.baidu.com/s/1sjzzlDF linux poll/select用法及在字符驱动中的简单实现 1.poll和select 使用非阻塞I/O 的应用程序常常使 ...

  8. poll机制分析[转]

    所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open ...

  9. 【原创】Linux select/poll机制原理分析

    前言 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 1. 概述 Linux系统 ...

随机推荐

  1. 用Synoptic Panel自定义基于图形的可视化控件--制作一张剧场售票统计报表

    数据可视化的一大特点就是能给报表使用者带来感官上的享受.不再是枯燥的数字,而变成一个一个亮丽的图形.之前业界大神公布过一个统计Car Accidents的报表,这个Power BI Report的特点 ...

  2. ecshop常见sql注入修复(转)

    ecshop系统部署在阿里云服务器上,阿里云提示Web-CMS漏洞: 修复方法如下: 0. /good.php 大概在第80行 $goods_id = $_REQUEST['id']; 修改为 $go ...

  3. 创建文件夹c++

    linux #include <sys/types.h> #include <sys/stat.h> string filepath; mkdir(filepath.c_str ...

  4. git命令收集(记得持续更新)

    这里收集了一些常用的git命令: git remote add origin git@192.168.1.128:sabo/ycdd-server.git git push -u origin mas ...

  5. NOI-1.1-06-空格分隔输出-体验多个输入输出

    06:空格分隔输出 总时间限制:  1000ms 内存限制:  65536kB 描述 读入一个字符,一个整数,一个单精度浮点数,一个双精度浮点数,然后按顺序输出它们,并且要求在他们之间用一个空格分隔. ...

  6. php 中使用正则

    1.匹配一个由一个小写字母和一位数字组成的字符串,比如”z2″   用^[a-z][0-9]$ 2.当在一组方括号里使用^是,它表示“非”或“排除”的意思   比如要求第一个字符不能是数字:^[^0- ...

  7. C语言--第三周作业评分和总结(5班)

    作业链接:https://edu.cnblogs.com/campus/hljkj/CS2017-5/homework/1073 一.评分要求 要求1 完成PTA第三周所有题(20分). 要求2 4道 ...

  8. PS学习之制作音乐视屏

    素材: 新建画布 插入图片素材 调整和画布一样大小 喜欢彩色的 可以加照片滤镜 喜欢黑白的可以加黑白滤镜 也可以添加自己喜欢的文字 在窗口中选择时间轴 创建视屏时间轴 图中标记得就是每秒能播放30张 ...

  9. 【HDOJ1529】【差分约束+SPFA+二分】

    http://acm.hdu.edu.cn/showproblem.php?pid=1529 Cashier Employment Time Limit: 2000/1000 MS (Java/Oth ...

  10. hdu4338 Simple Path

    Everybody knows that totalfrank has absolutely no sense of direction. Getting lost in the university ...