莱芜找工作网站/2345网址中国最好
select系統調用是用來讓我們的程序監視多個文件句柄的狀態變化的。程序會停在select這里等待,直到被監視的文件句柄有一個或多個發生了狀態改變。關於文件句柄,其實就是一個整數,我們最熟悉的句柄是0、1、2三個,0是標准輸入,1是標准輸出,2是標准錯誤輸出。0、1、2是整數表示的,對應的FILE *結構的表示就是stdin、stdout、stderr。
函數原型是:int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);參數n代表文件描述詞加1;參數readfds、writefds 和exceptfds 稱為描述詞組,是用來回傳該描述詞的讀,寫或例外的狀況。下面的宏提供了處理這三種描述詞組的方式:
FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位
FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否為真
FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位
FD_ZERO(fd_set *set);用來清除描述詞組set的全部位
參數timeout為結構timeval,用來設置select()的等待時間,其結構定義如下:
struct timeval
{
time_t tv_sec;
time_t tv_usec;
};
如果參數timeout設為NULL,則表示select()沒有timeout。
select函數執行結果:執行成功則返回文件描述詞狀態已改變的個數;如果返回0代表在描述詞狀態改變前已超過timeout時間,沒有返回;當有錯誤發生時則返回-1,錯誤原因存於errno,此時參數readfds,writefds,exceptfds和timeout的值變成不可預測。錯誤值可能為:
EBADF 文件描述詞為無效的或該文件已關閉
EINTR 此調用被信號所中斷
EINVAL 參數n 為負值。
ENOMEM 核心內存不足
常見的程序片段如下:
fs_set readset;
FD_ZERO(&readset);
FD_SET(fd,&readset);
select(fd+1,&readset,NULL,NULL,NULL);
if(FD_ISSET(fd,readset){……}
檢測鍵盤有無輸入,完整的程序如下:
#include
#include
#include
#include
#include
#include
intmain()
{
charbuf[10]="";
fd_set rdfds;
structtimeval tv;
intret;
FD_ZERO(&rdfds);
FD_SET(0,&rdfds); //文件描述符0表示stdin鍵盤輸入
tv.tv_sec = 3;
tv.tv_usec = 500;
ret = select(1,&rdfds,NULL,NULL,&tv); //第一個參數是監控句柄號+1
if(ret<0)
printf("selcet error\r\n");
elseif(ret == 0)
printf("timeout \r\n");
else
printf("ret = %d \r\n",ret);
if(FD_ISSET(0,&rdfds)){//監控輸入的確是已經發生了改變
printf(" reading");
read(0,buf,9); //從鍵盤讀取輸入
}
write(1,buf,strlen(buf)); //在終端中回顯
printf(" %d \r\n",strlen(buf));
return0;
}
用gcc編譯,執行./a.out。可以得到執行結果,如果不輸入,則一會兒會回顯:
如果有輸入,則回顯:
第一個"s"是打字輸入的回顯。后面開始執行,有個疑問是:在stdout的輸出為什么會出現在reading顯示的前面?論執行順序應該是后面才對。
===========================================================================================
阻塞(Block)
當進程調用一個阻塞的系統函數時,該進程被置於睡眠(Sleep)狀態,這時內核調度其它進程運行,直到該進程等待的事件發生了(比如網絡上接收到數據包,或者調用sleep 指定的睡眠時間到了)它才有可能繼續運行。與睡眠狀態相對的是運行(Running)狀態,在Linux內核中處於運行狀態的進程分為兩種情況: 正在被調度執行和就緒狀態。
假設一個進程同時監視多個設備,如果read(設備1)是阻塞的,那么只要設備1沒有數據到達就會一直阻塞在設備1的read 調用上,即使設備2有數據到達也不能處理,使用非阻塞I/O就可以避免設備2得不到及時處理。
在open 一個設備時指定了O_NONBLOCK 標志,read / write 就不會阻塞。以read 為例,如果設備暫時沒有數據可讀就返回-1,同時置errno 為EWOULDBLOCK(或者EAGAIN,這兩個宏定義的值相同),表示本來應該阻塞在這里,那么調用者不是阻塞在這里死等,這樣可以同時監視多個設備。
非阻塞I/O有一個缺點,如果所有設備都一直沒有數據到達,調用者需要反復查詢做無用功,如果阻塞在那里操作系統可以調度別的進程執行,就不會做無用功了。select(2) 函數可以阻塞地同時監視多個設備,還可以設定阻塞等待的超時時間,從而圓滿地解決了這個問題。