poll() and select()
上一篇 / 下一篇 2008-04-27 22:10:51 / 个人分类:LINUX之路
select()和poll()方法是复用网络套接字的强大工具。采用这两个方法可以表明过程在什么时候可以安全地执行打开的文件描述符而没有任何延迟。比方说,程序员就可以用这些函数调用得知某个套接字上何时有数据被读取。在给select()和poll()指定任务之后你就不必经常性地检查套接字是否有数据要读取了。实际上,select()和poll()还可以置于操作系统的后台运行,一旦满足特定事件或者时间超时就会被唤醒。这个进程可以显著地增加程序的效率(假如你更关心程序的性能而非可以移植性,我们在本文的末尾简单讨论了两种替代select()和poll()的方法)。
就象你看到的那样,select()和poll()在功能上非常相似。在很多情况下,select()和poll()的方法实现其实是互相映射的。比方说,在Apache 2.0的核心组件Apache可移植运行时内,可移植接口(portable interface)就是模仿poll()语法提供的。在没有本地poll()实现的平台上,poll()的语法就被映射为select()。在FreeBSD系统上,select()的libc_r实现就完全是对poll()系统调用的简单封装。
select() 说明
Single UNI规范第2版(SUSv2)是这样定义select()的:
int select(intnfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, structtimeval *timeout);
该函数调用有以下参数:
- intnfds– 所有给定集合内最高文件描述符加1
- fd_set*readfds–在有数据读取时触发函数返回的文件描述符
- fd_set*writefds– 在有数据准备写入时触发函数返回的文件描述符
- fd_set*errorfds– 发生错误反常时触发函数返回的文件描述符
- structtimeval *timeout–select()必须等待事件的最大周期
返回值表示其请求事件满足条件的文件描述符的数目。
你不能通过直接改变fd_set结构值这种方式来修改fd_set结构。设置或者获值的唯一可移植方式是采用FD_*宏:
- FD_ZERO(fd_set *)–把fd_set初始化为空
- FD_CLR(intfd, fd_set *)– 从fd_set中删除关联的fd
- FD_SET(intfd, fd_set *)– 在fd_set中增加关联的fd
- FD_ISSET(intfd, fd_set *)– 假如fd在fd_set内即可返回非0值
在从select()返回时,FD_ISSET()可以针对给定集合内的每个fd而被调用来表明其条件是否满足。
timeout值的含义是这样的,你可以指定select()等待事件的时间需要多久。假如timeout的值是NULL,那么select()将无限期地等待事件。假如超时的timeval结构设置为0,那么select()将立即返回而不会等待任何事件的发生。否则,timeout则具体定义select()函数等待的时间长度。SUSv2规定所有遵守规范的函数实现都应该支持至少31天的超时。不妨查阅 清单A了解select()函数的具体应用示例。
poll()说明
poll()方法试图合并select()函数的参数,同时提供范围更广的事件通知。SUSv2 如下定义poll()函数:
int poll(structpollfdfds[ ], nfds_tnfds, int timeout);
参数含义如下:
- structpollfdfds[ ]-pollfd结构数组
- nfds_tnfds-fds[ ]中文件描述符集合的数目
- inttimeout-poll()等待事件发生的时间长度(单位是毫秒)
返回值表示多少fds有事件发生。
pollfd结构通常包括以下结构成员:
- intfd– 表示某个事件由哪个fd监视
- short events– 表示哪些事件将被监视的位字段
- short revents– 表示调用poll()时检测到的事件的比特位
SUSv2规范对以上内容和事件位字段的含义进行了详细的说明。同select()函数相比,poll()在确定被处理的事件类型方面具有更大一些的灵活性。除了读、写和错误通知以外,poll()函数还支持带外和高优先级数据的直接识别。
不同于select(),poll()的timeout参数是一个简单的整数,表示poll()等待事件的时长。可以赋以特殊的值,通常是 -1 或者常数INFTIM,这是很多旧系统常用的参数,指示poll()永远等待事件。同select()函数一样,0 超时表示poll()函数必须立即返回。
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>
#include<linux/poll.h>
#include<linux/wait.h>
#include<linux/slab.h>
#include<linux/string.h>
#defineDEVICE_NAME"chardev"
staticintchardev_open(structinode*inodp,structfile*filp);
staticssize_t chardev_read(structfile*filp,char__user*buff,size_tcount,loff_t*f_pos);
staticssize_t chardev_write(structfile*filp,constchar__user*buff,size_tcount,loff_t*f_pos);
staticunsignedintchardev_poll(structfile*filp,structpoll_table_struct*p);
staticintchardev_release(structinode*inodp,structfile*filp);
staticstructfile_operations chardev_fops={
.open=chardev_open,
.read=chardev_read,
.write=chardev_write,
.poll=chardev_poll,
.release=chardev_release,
};
structchardev{
chardata[8192];
dev_t devno;
structcdev cdev;
structsemaphore sem;
wait_queue_head_t rqueue;
};
staticstructchardev*dev=NULL;
staticintchardev_open(structinode*inodp,structfile*filp)
{
structchardev*device=NULL;
device=container_of(inodp->i_cdev,structchardev,cdev);
filp->private_data=device;
return0;
}
staticssize_t chardev_read(structfile*filp,char__user*buff,size_tcount,loff_t*f_pos)
{
if(wait_event_interruptible(dev->rqueue,strlen(dev->data)!=0))
gotoerr;
if(down_interruptible(&dev->sem))
gotoerr;
copy_to_user(buff,dev->data,strlen(dev->data));
memset(dev->data,'\0',sizeof(dev->data));
up(&dev->sem);
returncount;
err:
return-ERESTARTSYS;
}
staticssize_t chardev_write(structfile*filp,constchar__user*buff,size_tcount,loff_t*f_pos)
{
if(down_interruptible(&dev->sem))
gotoerr;
copy_from_user(dev->data,buff,count);
up(&dev->sem);
wake_up_interruptible(&dev->rqueue);
returncount;
err:
return-ERESTARTSYS;
}
staticunsignedintchardev_poll(structfile*filp,structpoll_table_struct*p)
{
unsignedintmask=0;
if(down_interruptible(&dev->sem))
gotoerr;
poll_wait(filp,&dev->rqueue,p);
mask|=POLLIN|POLLRDNORM;
up(&dev->sem);
returnmask;
err:
return-ERESTARTSYS;
}
staticintchardev_release(structinode*inodp,structfile*filp)
{
return0;
}
staticint__init chardev_init(void)
{
intret=-1;
dev=kmalloc(sizeof(structchardev),GFP_KERNEL);
if(!dev)
gotoout;
memset(dev,'\0',sizeof(structchardev));
init_MUTEX(&dev->sem);
init_waitqueue_head(&dev->rqueue);
ret=alloc_chrdev_region(&dev->devno,0,1,DEVICE_NAME);
if(ret<0)
gotoerr;
cdev_init(&dev->cdev,&chardev_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&chardev_fops;
ret=cdev_add(&dev->cdev,dev->devno,1);
if(!ret)
gotoout;
unregister_chrdev_region(dev->devno,1);
err:
kfree(dev);
out:
returnret;
}
staticvoid__exit chardev_exit(void)
{
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->devno,1);
kfree(dev);
}
MODULE_LICENSE("GPL");
module_init(chardev_init);
module_exit(chardev_exit);
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>
#include<linux/poll.h>
#include<linux/wait.h>
#include<linux/slab.h>
#include<linux/string.h>
#defineDEVICE_NAME"chardev"
staticintchardev_open(structinode*inodp,structfile*filp);
staticssize_t chardev_read(structfile*filp,char__user*buff,size_tcount,loff_t*f_pos);
staticssize_t chardev_write(structfile*filp,constchar__user*buff,size_tcount,loff_t*f_pos);
staticunsignedintchardev_poll(structfile*filp,structpoll_table_struct*p);
staticintchardev_release(structinode*inodp,structfile*filp);
staticstructfile_operations chardev_fops={
.open=chardev_open,
.read=chardev_read,
.write=chardev_write,
.poll=chardev_poll,
.release=chardev_release,
};
structchardev{
chardata[8192];
dev_t devno;
structcdev cdev;
structsemaphore sem;
wait_queue_head_t rqueue;
};
staticstructchardev*dev=NULL;
staticintchardev_open(structinode*inodp,structfile*filp)
{
structchardev*device=NULL;
device=container_of(inodp->i_cdev,structchardev,cdev);
filp->private_data=device;
return0;
}
staticssize_t chardev_read(structfile*filp,char__user*buff,size_tcount,loff_t*f_pos)
{
if(wait_event_interruptible(dev->rqueue,strlen(dev->data)!=0))
gotoerr;
if(down_interruptible(&dev->sem))
gotoerr;
copy_to_user(buff,dev->data,strlen(dev->data));
memset(dev->data,'\0',sizeof(dev->data));
up(&dev->sem);
returncount;
err:
return-ERESTARTSYS;
}
staticssize_t chardev_write(structfile*filp,constchar__user*buff,size_tcount,loff_t*f_pos)
{
if(down_interruptible(&dev->sem))
gotoerr;
copy_from_user(dev->data,buff,count);
up(&dev->sem);
wake_up_interruptible(&dev->rqueue);
returncount;
err:
return-ERESTARTSYS;
}
staticunsignedintchardev_poll(structfile*filp,structpoll_table_struct*p)
{
unsignedintmask=0;
if(down_interruptible(&dev->sem))
gotoerr;
poll_wait(filp,&dev->rqueue,p);
mask|=POLLIN|POLLRDNORM;
up(&dev->sem);
returnmask;
err:
return-ERESTARTSYS;
}
staticintchardev_release(structinode*inodp,structfile*filp)
{
return0;
}
staticint__init chardev_init(void)
{
intret=-1;
dev=kmalloc(sizeof(structchardev),GFP_KERNEL);
if(!dev)
gotoout;
memset(dev,'\0',sizeof(structchardev));
init_MUTEX(&dev->sem);
init_waitqueue_head(&dev->rqueue);
ret=alloc_chrdev_region(&dev->devno,0,1,DEVICE_NAME);
if(ret<0)
gotoerr;
cdev_init(&dev->cdev,&chardev_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&chardev_fops;
ret=cdev_add(&dev->cdev,dev->devno,1);
if(!ret)
gotoout;
unregister_chrdev_region(dev->devno,1);
err:
kfree(dev);
out:
returnret;
}
staticvoid__exit chardev_exit(void)
{
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->devno,1);
kfree(dev);
}
MODULE_LICENSE("GPL");
module_init(chardev_init);
module_exit(chardev_exit);

