【转】LINUX系统I/O复用技术之二:poll() -- 不错
原文网址:http://www.cnblogs.com/alyssaCui/archive/2013/04/01/2993886.html
poll
poll或select为大部分Unix/Linux程序员所熟悉,这俩个东西原理类似,性能上也不存在明显差异,但select对所监控的文件描述符数量有限制,所以这里选用poll做说明。
1. 头文件
# include < sys/ poll. h>
2. 参数说明
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由fds指针指向这个组。pollfd结构体定义如下:
struct pollfd
{
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
} ;
typedef unsigned long nfds_t;
struct pollfd * fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的socket描述符;每当调用这个函数之后,系统不需要清空这个数组,操作起来比较方便;特别是对于 socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select() 函数需要清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测少量socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
如果待检测的socket描述符为负值,则对这个描述符的检测就会被忽略,也就是不会对成员变量events进行检测,在events上注册的事件也会被忽略,poll()函数返回的时候,会把成员变量revents设置为0,表示没有事件发生;
经常检测的事件标记:
POLLIN/POLLRDNORM(可读)、
POLLOUT/POLLWRNORM(可写)、
POLLERR(出错)
合法的事件如下:
POLLIN 有数据可读。
POLLRDNORM 有普通数据可读。
POLLRDBAND 有优先数据可读。
POLLPRI 有紧迫数据可读。
POLLOUT 写数据不会导致阻塞。
POLLWRNORM 写普通数据不会导致阻塞。
POLLWRBAND 写优先数据不会导致阻塞。
POLLMSG SIGPOLL 消息可用。
此外,revents域中还可能返回下列事件:
POLLER 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起事件。
POLLNVAL 指定的文件描述符非法。
这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。使用poll()和select()不一样,你不需要显式地请求异常情况报告。
POLLIN | POLLPRI等价于select()的读事件,
POLLOUT |POLLWRBAND等价于select()的写事件。
POLLIN等价于POLLRDNORM |POLLRDBAND,
而POLLOUT则等价于POLLWRNORM。
如果是对一个描述符上的多个事件感兴趣的话,可以把这些常量标记之间进行按位或运算就可以了;
比如:对socket描述符fd上的读、写、异常事件感兴趣,就可以这样做:
struct pollfd fds;
fds[nIndex].events=POLLIN | POLLOUT | POLLERR;
当 poll()函数返回时,要判断所检测的socket描述符上发生的事件,可以这样做:
struct pollfd fds;
检测可读TCP连接请求:
if((fds[nIndex].revents & POLLIN) == POLLIN){//接收数据/调用accept()接收连接请求}
检测可写:
if((fds[nIndex].revents & POLLOUT) == POLLOUT){//发送数据}
检测异常:
if((fds[nIndex].revents & POLLERR) == POLLERR){//异常处理}
nfds_t nfds:用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
如果timeout==0,那么 poll() 函数立即返回而不阻塞,
如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发 生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
3. 返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的 socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,
-1: poll函数调用失败,同时会自动设置全局变量errno;errno为下列值之一:
4. 错误代码
EBADF 一个或多个结构体中指定的文件描述符无效。
EFAULTfds 指针指向的地址超出进程的地址空间。
EINTR 请求的事件之前产生一个信号,调用可以重新发起。
EINVALnfds 参数超出PLIMIT_NOFILE值。
ENOMEM 可用内存不足,无法完成请求。
5. 实现机制
poll是一个系统调用,其内核入口函数为sys_poll,sys_poll几乎不做任何处理直接调用do_sys_poll,do_sys_poll的执行过程可以分为三个部分:
1),将用户传入的pollfd数组拷贝到内核空间,因此拷贝操作和数组长度相关,时间上这是一个O(n)操作,这一步的代码在do_sys_poll中包括从函数开始到调用do_poll前的部分。
2),查询每个文件描述符对应设备的状态,如果该设备尚未就绪,则在该设备的等待队列中加入一项并继续查询下一设备的状态。查询完所有设备后如果没有一个设备就绪,这时则需要挂起当前进程等待,直到设备就绪或者超时,挂起操作是通过调用schedule_timeout执行的。设备就绪后进程被通知继续运行,这时再次遍历所有设备,以查找就绪设备。这一步因为两次遍历所有设备,时间复杂度也是O(n),这里面不包括等待时间。相关代码在do_poll函数中。
3),将获得的数据传送到用户空间并执行释放内存和剥离等待队列等善后工作,向用户空间拷贝数据与剥离等待队列等操作的的时间复杂度同样是O(n),具体代码包括do_sys_poll函数中调用do_poll后到结束的部分。
6. 注意事项
1). poll() 函数不会受到socket描述符上的O_NDELAY标记和O_NONBLOCK标记的影响和制约,也就是说,不管socket是阻塞的还是非阻塞 的,poll()函数都不会收到影响;
2). poll()函数则只有个别的的操作系统提供支持(如:SunOS、Solaris、AIX、HP提供 支持,但是Linux不提供支持),可移植性差;
7. 范例
7.1. 例一
#include <string.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <poll.h> /* int poll(struct pollfd *fds, nfds_t nfds, int timeout); */ /* struct pollfd { int fd; // file descriptor short events; // requested events short revents; // returned events }; */ /* The bits that may be set/returned in events and revents are defined in <poll.h>: POLLIN: There is data to read.(数据可读) POLLOUT:Writing now will not block.(数据可读) */ #define OPEN_FLAGS O_RDWR|O_CREAT #define OPEN_MODE 00777 #define W_DATA "howaylee" int main(int argc, char* argv[]) { int ret = -1; int fd1 = -1; int fd2 = -1; char r_buf[12] = {0}; struct pollfd fds[2] = {0}; //open fd1 fd1 = open(argv[1], OPEN_FLAGS, OPEN_MODE); if (-1 == fd1) { perror("open fd1 failed: "); return -1; } //write fd1 ret = write(fd1, W_DATA, sizeof(W_DATA)); if(-1 == ret) { perror("write fd1 failed: "); goto _OUT; } //lseek fd1 head ret = lseek(fd1, 0, SEEK_SET); if(-1 == ret) { perror("lseek fd1 failed: "); goto _OUT; } //open fd2 fd2 = open(argv[2], OPEN_FLAGS, OPEN_MODE); if (-1 == fd2) { perror("open fd2 failed: "); return -1; } /*阻塞,等待程序读写操作*/ while(1) { //初始化pollfd fds[0].fd = fd1; //可读 fds[0].events = POLLIN; fds[1].fd = fd2; //可写 fds[1].events = POLLOUT; //poll ret = poll(fds, sizeof(fds)/sizeof(fds[0]), -1); if(-1 == ret) { perror("poll failed: "); goto _OUT; } //read fd1 if(fds[0].revents & POLLIN ) { //清空缓存 //memset(r_buf, 0, sizeof(r_buf)); ret = read(fd1, r_buf, sizeof(r_buf)); if(-1 == ret) { perror("poll read failed: "); goto _OUT; } printf("read = %s\n", r_buf); } //write fd2 if(fds[1].revents & POLLOUT ) { ret = write(fd2, r_buf, sizeof(r_buf)); if(-1 == ret) { perror("poll write failed: "); goto _OUT; } printf("write = %s\n", r_buf); } } //close fd1 fd2 close(fd1); close(fd2); _OUT: return ret; }
7.2. 例二
int read_time(int fd, void *buf, int size, int ms) { ssize_t len = 0; int ttl = 16 ; struct pollfd pfd; int i; while (1) { pfd.fd = fd; pfd.events = POLLIN | POLLERR | POLLHUP | POLLNVAL; errno = 0; i = poll(&pfd, 1, ms ); if (i < 0) { if (errno == EINTR) { if (--ttl<=0) { ls_log(LOG_LEVEL_ERROR,"[readn_ex]readn poll ttl overflow, errno:%d size:%d" , errno, size); return -6; } continue; } ls_log(LOG_LEVEL_ERROR,"[readn_ex]readn errno:%d" , errno ); return -1; // error } else if (0==i) { ls_log(LOG_LEVEL_ERROR,"[readn_ex]readn poll timeout" ); return -2; } if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { ls_log(LOG_LEVEL_ERROR,"[readn_ex]readn err revents:%x errno:%d len:%d" , pfd.revents, errno, size); return -3; } len = read(fd, (char*)buf, size); if (len < 0) { if (errno == EINTR) { if (--ttl<=0) { ls_log(LOG_LEVEL_ERROR,"[readn_ex]readn poll ttl overflow, errno:%d size:%d" , errno, size); return -6; } continue; } return -4; } if (len == 0) //EOF ,is return -5 right??? { ls_log(LOG_LEVEL_INFO,"[readn_ex] EOF"); return -5; } return len; } return -1; }
注:本文并非原创,乃是对多篇网络资料的整理!
【转】LINUX系统I/O复用技术之二:poll() -- 不错的更多相关文章
- Linux网络编程-IO复用技术
IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...
- 关于linux系统如何实现fork的研究(二)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 前一篇关于linux系统如何实现fork的研究(一)通过代码已经说明了从用户态怎么通过软中断实现调用系统调 ...
- 关于linux系统如何实现fork的研究(二)【转】
转自:http://www.aichengxu.com/linux/7166015.htm 本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 前一篇关于li ...
- 嵌入式Linux系统Bootloader启动调试技术(回想)
嵌入式系统搭建过程中,对于系统平台搭建project师最初的一步一般是移植Bootloader ,当然移植有几个级别,通常最常见的是參考的EVM 的硬件有了改动(如更改了FLASH ,更改了SDRAM ...
- Linux From Scratch(从零开始构建Linux系统,简称LFS)(二)
七. 构建临时系统 1. 通用编译指南 a. 确认是否正确设置了 LFS 环境变量 echo $LFS b. 假定你已经正确地设置了宿主系统需求和符号链接 c. 对于每个软件包: (1). 确保解压软 ...
- linux系统编程之文件与io(二)
今天继续学习文件与io,话不多说,开始进入正题: 文件的read和write系统调用: 说明:函数中出现在size_t和ssize_t是针对系统定制的数据类型: 下面以一个实现文件简单拷贝的示 ...
- Linux系统调优——Memory内存(二)
(1).查看Memory(内存)运行状态相关工具 1)free命令查看内存使用情况 [root@youxi1 ~]# free -m //-m选项,以MB为单位显示 total used free s ...
- 可扩展的事件复用技术:epoll和kqueue
通常来说我喜欢Linux更甚于BSD系统,但是我真的想在Linux上拥有BSD的kqueue功能. 什么是事件复用技术 假设你有一个简单的web服务器,并且那里已经打开了两个socket连接.当服务器 ...
- ARM 开发板嵌入式linux系统与主机PC通过串口传输文件
本文转载自http://useless20.blog.163.com/blog/static/237409982010227127576/ 嵌入式linux系统与主机通过串口传输文件 我想如果要从PC ...
随机推荐
- oc文件基本读写及操作
代码: #import <Foundation/Foundation.h> //NSString 写文件 void stringWriteToFile(){ NSString *path ...
- 类库探源——System.Math 和 Random
一.System.Math Math类:为三角函数.对数函数和其他通用数学函数提供常数和静态方法 命名空间: System 程序集 : mscorlib.dll 继承关系: 常用属性: Math. ...
- 关于arcgis 9.3破解问题详解
对于初学GIS的同学,安装软件可能会遇到各种各样的问题,对于photoshop,autocad,sketchup,3dmax等软件我们的我们无非是输入特定序列号或者用工具随机生成特定序列号就可以破解, ...
- [转]CSS 模块
CSS 模块 如果你想知道 CSS 最近发展的转折点,你应该选择去观看 Christopher Chedeau 在2014年11月的 NationJS 大会上做的名称为 CSS in JS 的分享.不 ...
- v4l2简介
V4L是linux内核中关于视频设备的子系统,为linux下的视频驱动提供了统一的接口,使应用程序可以使用统一的API操作不同的视频设备,简化视频系统的开发与维护 V4L2相比与V4L有更好的扩展性和 ...
- STL unique使用问题
string strs[] = {"one","one","two","three","three" ...
- cenots 下的 lamp(备份与恢复)
用 putty连接数据库: mysql -uroot -p密码 create database yourdb DEFAULT CHARACTER SET utf8 COLLATE utf8_chine ...
- php 简单的验证码
注意事项: 验证的隐藏域的位置一定要在调用JS前.. 如: <input type="text" name="yzm" value="" ...
- HTML标签总结
HTML 基本文档 <!DOCTYPE html> <html> <head> <title>文档标题</title> </head& ...
- C# MySql 操作类
/* MySql 类 */ using System; using System.Collections.Generic; using System.Linq; using System.Text; ...