select poll 講解

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章