)linux select poll

來源:互聯網
上載者:User

以前寫伺服器程式直接就都寫成多線程的了,沒考慮過其他方式,也沒考慮到底哪種方式好;

前些日子看些人說windows下面用完成連接埠、Linux下面用epoll,這些效率高。

其它環境一說就是select;似乎很多人不願意提多線程方式,也許被傳說中的線程同步嚇得吧;

我個人還是偏向多線程方式,這樣不但可以監視多連接埠,還可以分離商務邏輯,便於調試維護。

----------------------------------------------------------------------------------------------------

 

看明白這幾個東西,需要一個環境,描述問題;

就是我的程式需要同時處理兩個或兩個以上的檔案描述符;

 

這幾種方法都能解決,除了多線程方式外,都要依靠非阻塞I/O;

 

fd = open(filename , O_RDONLY|O_NONBLOCK);

 

輪詢的方法就可以當個傳說中的笑話來聽,沒有人會這麼用。

 

#define BLKSIZE 1024

int nbytes;

char buf[BLKSIZE];

int sign_done = 0;

while(!sign_done){

     nbytes = read(fd,buf,BLKSIZE);

     if( ( ( nbytes<0 ) && (errno != EAGAIN ) && (errno != EINTR) ) || !nbytes)

               do_something_process_error();

     else

               do_something_process_data();

 

     //if should end loop .set sign_done to 1;

}

 

fd是非阻塞的,讀不到東西的話就一直讀,一直佔用CPU資源。除了浪費,沒有價值了吧;

 

第二種方法是使用SIGPOLL訊號的非同步I/O;

SIGPOLL是SISTEM V的訊號,BSD系統用SIGIO;

當系統知道有東西需要你讀的時候,就發個SIGPOLL訊號來通知;

 

int sigpoll_received = 0;

static void poll_handler(int signo){   //這個是SIGPOLL的訊號處理函數

    sigpoll_received = 1;

}

 

int fd1, fd2 ;

int fd1_done=0 , fd2_done = 0;

sigset_t oldmask , newmask , zeromask;

struct sigaction newact;

 

fd1 = open (filename1 , O_RDONLY | O_NONBLOCK);

fd2 = open (filename2 , O_RDONLY | O_NONBLOCK);

sigemptyset ( &newmask );

sigaddset ( &newmask , SIGPOLL);

sigprocmask (SIG_BLOCK , &newmask , &oldmask); //要在實際讀取之前阻塞SIGPOLL訊號

 

newact.sa_handler = poll_handler;

sigemptyset(&newact.sa_mask);

newact.sa_flags = 0;

sigaction ( SIGPOLL , &newact , NULL ); //給SIGPOLL訊號安裝處理器

 

ioctl(fd1 , I_SETSIG , S_INPUT | S_HANGUP );   //設定當有東西讀時產生 SIGPOLL訊號

ioctl(fd2 , I_SETSIG , S_INPUT | S_HANGUP );

 

sigemptyset ( &zeromask ) ;

while ( !fd1_done || !fd2_done ){

    if ( !fd1_done)

          deal_with_fd1_and_set_fd1done_sign;

    if ( !fd12done)

 

 

          deal_with_fd2_and_set_fd1done_sign;

    while ( !sigpoll_received && ( !fd1_done || !fd2_done ) )

       sigsuspend ( &zeromask ) ; //沒東西讀的時候,阻塞在這裡一直等到SIGPOLL訊號來到

    sigpoll_received = 0;

}

 

 

 

第三種方法是使用select系統調用;

select是BSD系統的,但大多數系統都支援,可能是因為spec1170的原因吧;

 

#include

#include

int select (int nfds , fd_set *readfds, fd_set *writefds, fd_set * exceptfds, struct timeval * timeout) ;

 

第一個參數nfds是檔案描述符集中要檢測的掩碼位元,這是因為以前描述符集是作為一個整數位屏蔽碼實現的;

也就是每一位表示一個描述符,但那種方法沒辦法處理多於32個描述符(原來洋人也有不用大腦思考問題的時候^_^);

現在描述符一般用整數數組的位域表示;這個nfds的數值要比實際要檢測的描述符數多一,具體為啥要去看系統調用的源碼了(今天俺不看了);

後面三個(fd_set*)類型的就是實際的描述符集了,讀監控、寫監控、異常監控三個單獨的集;

最後是逾時時間,到時間函數就返回了,很多人說這個可以當成定時器用,比alarm好用;

select被訊號中斷時返回-1,並設errno 為 EINTR ;

 

fd_set readset;

int maxfd, fd1 , fd2;

maxfd = fd1;

if (fd2 > maxfd ) maxfd = fd2;   //找出最大的描述符

while (1) {

    FD_ZERO(&readset); /*由於select返回時會清除描述符集中無資料的描述符,所以每次select之前都要重設描述符集 */

    FD_SET(fd1 , &readset );

    FD_SET(fd2 , &readset );

    if ( (select (maxfd+1, &readset , NULL, NULL, NULL) == -1 ) && (errno != EINTR) )

        /* deal with error */

    else{

        if( FD_ISSET(fd1 , &readset ) )    /* FD_ISSET(2) 檢查指定的描述符是否被設定了 */

            /* get and process data *//*這裡的問題是處理這些資料的時候,進程會阻塞在這裡,其他連接埠的資料的處理沒有辦法重疊操作*/

        if( FD_ISSET(fd2 , &readset ) )

            /* get and process */

    }

}

 

 

 

 

 

Poll是SVR4的東西,是和select幾乎一樣的東西,僅僅是對描述符使用的方式不一樣;

 

#include

#include

int poll (struct pollfd *fds, size_t nfds , int timeout);

struct pollfd {
         int fd;        /* 檔案描述符 */
         short events; /* 等待的事件 */
         short revents; /* 實際發生了的事件 */
};

 

poll用pollfd結構數組提供描述符,並且分別使用輸入和輸出的訊息掩碼,這樣不用像select那樣每次調用之前都設定一次。

 

struct pollfd * fds;

short errmsk;

int idx;

errmsk = POLLERR|POLLHUP;

fds = (void *)calloc(num_fds , sizeof(struct pollfd));

for(idx = 0 ; idx

    (fds + idx)->fd = *****; /*設定描述符*/

    (fds + idx)->events = POLLRDNORM; /* 設定要監聽的事件 */

    (fds + idx)->revents = 0;

}

while (situation ){

    if( ( num_ret = poll ( fds , num_fds , INFTIM ) ) == -1 ) && (errno != EINTR ) )

        break;

    for ( idx = 0 ; idx < num_fds && num_ret >0 ; idx++){

        if( (fds + idx )->events && (fds + idx )->revents ) { /* 確實有訊息來,有事件發生,而不是錯誤 */

            if( (fds + idx)->revents & errmsk ) {

                 /* 錯誤 */

                 (fds + idx)->revents = 0; /*清理掉 */

            }

             else if( (fds +idx )->revents & POLLRDNORM ){

                  /* 正常處理資料 */

             }

        }

    }

 

}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.