select()和poll()系統調用的本質一樣,前者在BSD UNIX中引入的,後者在System V中引入的。
一、select
應用程序中最廣泛用到的是BSD UNIX中引入的select()系統調用,其原型如下:
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
select的第一個參數maxfdp是文件描述符集中要被檢測的數目,這個值必須至少比待檢測的最大文件描述符大1;
參數readfds指定了需要被讀監測的文件描述符集;參數writefds指定了需要被寫監測的文件描述符集;而參數errorfds指定了可能出現異常情況的文件描述符集。
timeout參數是一個指向struct timeval類型的指針,它可以使select()在等待timeout時間後若沒有文件描述符準備好則返回。
struct timeval數據結構的定義如下:
struct timeval
{
int tv_sec; //秒單位
int tv_usec; //微秒單位
};
timeout取不同的值,該調用就表現不同的性質:
1.timeout爲0,調用立即返回;
2.timeout爲NULL,select()調用就阻塞,直到知道有文件描述符就緒;(當有文件描述符就緒時,會向這個函數發送信號,以喚醒此函數。)
3.timeout爲正整數,就是一般的定時器。
select的返回值有如下情況:
1.正常情況下返回就緒的文件描述符個數;
2.經過了timeout時長後仍無設備準備好,返回值爲0;
3.如果select被某個信號中斷,它將返回-1並設置errno爲EINTR。
4.如果出錯,返回-1並設置相應的errno。
select()函數的接口主要是建立在一種叫fd_set結構體的基礎上。這個結構體是一組文件描述符(fd)的集合。因爲fd_set類型的長度在不同平臺上是不同的,此應該用一組標準的宏定義來處理這個類變量。
我們來了解fd_set這個結構的定義:
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
#define __NFDBITS (8 * sizeof(unsigned long)) //32
#define __FD_SETSIZE 1024// 每個進程能打開的文件描述符的上限,可以更改
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)// 32
typedef __kernel_fd_set fd_set;
對用fd_set定義的readfds、writefds、errorfds操作集進行操作最好使用封裝的通用宏來處理:
FD_ZERO(fd_set *set);//將文件描述符集fd_set中的值置0,如此以來對應所有位都被設置爲0;
FD_SET(int fd,fd_set *set);//將一個文件描述符加入文件描述集中;
FD_CLR(int fd,fd_set *set)//將一個文件描述符從文件描述符集中清除;
FD_ISSET(int fd,fd_set *set)//判斷文件描述符是否被置位。
下面是一個典型的程序片段:
FD_ZERO(&readset);
FD_SET(fd,&readset);
select(fd+1,&readset,NULL,NULL,NULL);
if(FD_ISSET(fd,readset){……}
需要注意的是每次調用select之前都需要重新設定fd_set集合。
二、poll
函數原型
#include <poll.h>
int poll(struct pollfd * fdarray, unsigned long nfds, int timeout);
返回值說明: >0準備好描述字的個數;
=0超時;
= -1表示出錯。
第一個參數是一個pollfd結構體數組,其中包括了你想測試的文件描述符和事件, 事件由結構中事件域events來確定,調用後實際發生的時間將被填寫在結構體的revents域。
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 實際發生了的事件 */
};
等待事件的掩碼:
POLLIN 普通或優先級帶數據可讀
POLLRDNORM普通數據可讀
POLLRDBAND優先級帶數據可讀
POLLPRI 高優先級數據可讀
POLLOUT 普通或優先級帶數據可寫
POLLWRNORM普通數據可寫
POLLWRBAND 優先級帶數據可寫
POLLERR發生錯誤
POLLHUP發生掛起
POLLVAL 描述字不是一個打開的文件
第一部分爲處理輸入的四個常值,第二部分是處理輸出的三個常值,第三部分是處理錯誤的三個常值。
poll處理三個級別的數據,普通normal,優先級帶priority band,高優先級high priority,這些都是出於流的實現。
第二個參數nfds用來指定第一個參數數組元素個數。
第三個參數指定poll函數在返回前等待多長時間,單位爲毫秒。當等待時間爲0時,poll()函數立即返回,爲-1則使poll()一直阻塞直到一個指定事件發生。
如果沒有事件發生,revents會被清空,所以你不必多此一舉。
例子如下:
int poll_two_normal(int fd1,int fd2)
{
struct pollfd poll_list[2];
int retval;
poll_list[0].fd = fd1;
poll_list[1].fd = fd2;
poll_list[0].events = POLLIN|POLLPRI;
poll_list[1].events = POLLIN|POLLPRI;
while(1)
{
retval = poll(poll_list,(unsigned long)2,-1);
/* retval 總是大於0或爲-1,因爲我們在阻塞中工作 */
if(retval < 0)
{
fprintf(stderr,"poll錯誤: %s/n",strerror(errno));
return -1;
}
if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||
((poll_list[0].revents&POLLERR) == POLLERR) ||
((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||
((poll_list[1].revents&POLLHUP) == POLLHUP) ||
((poll_list[1].revents&POLLERR) == POLLERR) ||
((poll_list[1].revents&POLLNVAL) == POLLNVAL))
return 0;
if((poll_list[0].revents&POLLIN) == POLLIN)
handle(poll_list[0].fd,NORMAL_DATA);
if((poll_list[0].revents&POLLPRI) == POLLPRI)
handle(poll_list[0].fd,HIPRI_DATA);
if((poll_list[1].revents&POLLIN) == POLLIN)
handle(poll_list[1].fd,NORMAL_DATA);
if((poll_list[1].revents&POLLPRI) == POLLPRI)
handle(poll_list[1].fd,HIPRI_DATA);
}
}
參考文檔:
http://apps.hi.baidu.com/share/detail/5467807
http://hi.baidu.com/operationsystem/blog/item/208eab9821da8f0e6f068cea.html
http://hi.baidu.com/%CB%BC%BF%BCde%C2%AB%CE%AD/blog/item/7c5bd12755c0a24aad34ded8.html