
- UID
- 849969
|

poll和select的使用和差异
作者:武汉华嵌技术部
使用非阻塞I/O 的应用程序常常使用poll, select。poll和select本质上有相同的功能:每个允许一个进程来决定它是否可读或者写一个或多个文件而不阻塞。这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写。因此,它们常常用在必须使用多输入输出流的应用程序,而不必粘连在它们任何一个上. 相同的功能常常由多个函数提供,因为2 个是由不同的团队在几乎相同时间完成的:select 在BSD Unix 中引入,而poll 是System V 的解决方案。
那么以下分别来分析下select和poll的各自用法。
首先来分析下select:
1、函数原型如下:
#include <sys/select.h>
int select(int maxfdp1, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict exceptfds,
struct timeval *restrict tvptr);
2、select做了些什么?分两次来讲:
⑴、传向select的参数告知内核,我们所关心的描述符;对于每个描述符我们所关心的状态;愿意等待多长时间。
⑵、select返回时,内核告诉我们已经准备好的描述符的数量;对于读、写或异常这三个状态中的每一个,哪里些描述符已经准备好。
3、select函数参数分析:
⑴最后一个参数,它指定愿意等待的时间。
struct timeval{
long tv_sec; //seconds
long tv_usec; //and microseconds
};
tvptr == NULL时,永远等待。
tvptr->tv_sec == 0 && tvptr->tv_usec == 0时,完全不等待。
tvptr->tv_sec != 0 || tvptr->tv_usec != 0时,等待指定的秒数和微秒数。
⑵、中间的三个参数readfds、writefds、exceptfds是指向描述符集的指针。这三个参数描述符集说明了我们关心的可读、可写或处于异常条件的各个描述符。每个描述符集存放在一个fd_set数据类型中。这各数据类型为每一可能的描述符保持了一个bit位。
对fd_set数据类型可以进行的处理是:分配一个这种类型的变量;将这种类型的一个变量值赋予同类型的另一个变量;或对于这种类型的变量使用下列四个函数中的一个。
#include <sys/select.h>
int FD_ISSET(int fd, fd_set *fdset);
返回值:若fd在描述符集中则返回非0值,否则返回0。
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
以上这些接口的作用分别是:FD_ZERO将一个指定的fd_set变量的所有位设置为0。FD_SET设置一个fd_set变量的指定位。FD_CLR将一指定位清除。FD_ISSET测试一指定位是否设置。
这三个参数中的任意一个或全部都可以是空指针,这表示对相应状态并不关心。如果所有三个指针都是空指针,则select提供了较sleep更精确的计时器。
⑶、第一个参数maxfdp1的意思是“最大描述符加1”。在三个描述符集中找到最大描述符编号值,然后加1,这就是第一个参数。
4、select返回值分析:
⑴、返回-1,表示出错。
⑵、返回0,表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超过,则发生这种情况。此时,所有描述符集皆被清0。
⑶、正返回值表示已经准备好的描述符数,该值是三个描述符集中已经准备好的描述符数之和,所以如果同一描述符已经准备好读和写,那么在返回值班中将其计为2.在这种情况下,三个描述符集中仍旧打开的位对应于已经准备好的描述符。
5、select使用的片段代码如下:
fd_set inset, tmp_inset;//声名两个描述符集
FD_ZERO(&inset);//清0
FD_SET(sockfd, &inset);//设置sockfd的指定位
if (!(select(MAX_SOCK_FD, &tmp_inset, NULL, NULL, NULL) > 0))
//在这里等着,等到描述符集中的任何一个描述符有反应了,就返回、、//正值
{
perror("select");
close(sockfd);
exit(1);
}
分析完select,现在分析下poll:
函数原型如下:
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
poll函数参数分析:
⑴最后一个参数,它指定愿意等待的时间。
timeout == -1时,永远等待。
timeout == 0时,不等待。
timeout > 0时,等待timeout毫秒。(如果系统不提供望毫秒分辨,则timeout值取整到最近的支持值)
⑵与select不同,poll不是为每个状态构造一个描述符集,面是构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。pollfd结构如下:
struct pollfd{
int fd; //file descriptor to check, or < 0 to ignore
short events; //events of interest on fd
short revents;//events that occurred on fd
};
应将每个数组元素的events成员设置如下表的值。通过这些值告诉内核我们对该描述符关心的产什么。返回时,内核设置revents成员,以说明对于该描述符已经发生了什么事件。
标志名
| 输入至
events?
| 从revents得到结果?
| 说明
| POLLIN
| Yes
| Yes
| 不阻塞地可读除高优先级外的数据
| POLLRDNORM
| Yes
| Yes
| 不阻塞地可读普通数据
| POLLRDBAND
| Yes
| Yes
| 不阻塞地可读非0优先级波段数据
| POLLPRI
| Yes
| Yes
| 不阻塞地可读高优先级数据
| POLLOUT
| Yes
| Yes
| 不阻塞地可写普通数据
| POLLWRNORM
| Yes
| Yes
| 与POLLOUT相同
| POLLWRBAND
| Yes
| Yes
| 不阻塞的可写非0优先级波段数据
| POLLERR
|
| Yes
| 已出错
| POLLHUP
|
| Yes
| 已挂断
| POLLNVAL
|
| Yes
| 描述符不引用一打开文件
| ⑶nfds说明fdarray数组中的元素数。
poll返回值分析:
⑴、返回0时,超时返回。
⑵、返回-1时,出错返回。
⑶、返回正值时,表示准备就绪的描述符数。
poll使用的片段代码如下:
struct pollfd pollfds[1]; //声明一个struct pollfd结构数组
pollfds[0].fd = fd ; //初始化struct pollfd数组第0个元素成员
pollfds[0].events = POLLIN;//初始化对该描述符所关心的
while (!terminate) {
if (poll(pollfds, 1, 100) <= 0)/*正常返回,得到的是准备就绪的描述符数。如果时间到了还没有描述符有反应,那么就返回-1,结束本次循环。*/
continue;
……………
……………
}
注意:
执行select后,如果成功了,其改变了描述符集中的内容,也就让没有反应的描述符位清0。而poll在执行结束后,并没有改变events中的内容,而是把执行的事件设置给revents来告知客户。
更多技术文章敬请关注:武汉华嵌-嵌入式培训专家,国内领先的嵌入式服务机构,
http://www.embedhq.org |
|