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
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
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);
參考文檔:
<a href="http://hi.baidu.com/operationsystem/blog/item/208eab9821da8f0e6f068cea.html" target="_blank">http://hi.baidu.com/operationsystem/blog/item/208eab9821da8f0e6f068cea.html</a>