select()函數的作用
系統調用select和poll的後端實作,用這兩個系統調用來查詢裝置是否可讀寫,或是否處于某種狀态。如果poll為空,則驅動裝置會被認為即可讀又可寫,傳回值是一個狀态掩碼
如何使用select()函數?
select()函數的接口主要是建立在一種叫'fd_set'類型的基礎上。它('fd_set') 是一組檔案描述符(fd)的集合。由于fd_set類型的長度在不同平台上不同,是以應該用一組标準的宏定義來處理此類變量:
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
FD_CLR(fd, &set);
FD_ISSET(fd, &set);
在 過去,一個fd_set通常隻能包含少于等于32個檔案描述符,因為fd_set其實隻用了一個int的比特矢量來實作,在大多數情況下,檢查 fd_set能包括任意值的檔案描述符是系統的責任,但确定你的fd_set到底能放多少有時你應該檢查/修改宏FD_SETSIZE的值。*這個值是系 統相關的*,同時檢查你的系統中的select() 的man手冊。有一些系統對多于1024個檔案描述符的支援有問題。[譯者注: Linux就是這樣的系統!你會發現sizeof(fd_set)的結果是128(*8 = FD_SETSIZE=1024) 盡管很少你會遇到這種情況。]
select的基本接口十分簡單:
int select(int nfds, fd_set *readset, fd_set *writeset,
fd_set *exceptset, struct timeval *timeout);
其中:
nfds
需要檢查的檔案描述符個數,數值應該比是三組fd_set中最大數
更大,而不是實際檔案描述符的總數。
readset
用來檢查可讀性的一組檔案描述符。
writeset
用來檢查可寫性的一組檔案描述符。
exceptset
用來檢查意外狀态的檔案描述符。(注:錯誤并不是意外狀态)
timeout
NULL指針代表無限等待,否則是指向timeval結構的指針,代表最
長等待時間。(如果其中tv_sec和tv_usec都等于0, 則檔案描述符
的狀态不被影響,但函數并不挂起)
函數将傳回響應操作的對應操作檔案描述符的總數,且三組資料均在恰當位置被修改,隻有響應操作的那一些沒有修改。接着應該用FD_ISSET宏來查找傳回的檔案描述符組。
這裡是一個簡單的測試單個檔案描述符可讀性的例子:
int isready(int fd)
{
int rc;
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(fd,&fds);
// tv.tv_sec = tv.tv_usec = 0;
//rc = select(fd+1, &fds, NULL, NULL, &tv);
rc = select(fd+1, &fds, NULL, NULL, NULL);
if (rc < 0)
return -1;
return FD_ISSET(fd,&fds) ? 1 : 0;
}
當然如果我們把NULL指針作為fd_set傳入的話,這就表示我們對這種操作的發生不感興趣,但select() 還是會等待直到其發生或者超過等待時間。
[譯 者注:在Linux中,timeout指的是程式在非sleep狀态中度過的時間,而不是實際上過去的時間,這就會引起和非Linux平台移植上的時間不 等問題。移植問題還包括在System V風格中select()在函數退出前會把timeout設為未定義的 NULL狀态,而在BSD中則不是這樣, Linux在這點上遵從System V,是以在重複利用timeout指針問題上也應該注意。]
Linux下select調用的過程:
1.使用者層應用程式調用select(),底層調用poll())
2.核心層調用sys_select() ------> do_select()
最終調用檔案描述符fd對應的struct file類型變量的struct file_operations *f_op的poll函數。
poll指向的函數傳回目前可否讀寫的資訊。
1)如果目前可讀寫,傳回讀寫資訊。
2)如果目前不可讀寫,則阻塞程序,并等待驅動程式喚醒,重新調用poll函數,或逾時傳回。
3.驅動需要實作poll函數。
當驅動發現有資料可以讀寫時,通知核心層,核心層重新調用poll指向的函數查詢資訊。
poll_wait(filp,&wait_q,wait) // 此處将目前程序加入到等待隊列中,但并不阻塞
在中斷中使用wake_up_interruptible(&wait_q)喚醒等待隊列。 另: select的本質是采用32個整數的32位,即32*32= 1024來辨別,fd值為1-1024。當fd的值超過1024限制時,就必須修改FD_SETSIZE的大小。這個時候就可以辨別32*max值範圍的fd。
對于單程序多線程,每個線程處理多個fd的情況,select是不适合的。
1.所有的線程均是從1-32*max進行掃描,每個線程處理的均是一段fd值,這樣做有點浪費
2.1024上限問題,一個處理多個使用者的程序,fd值遠遠大于1024
是以這個時候應該采用poll,poll傳遞的是數組頭指針和該數組的長度,隻要數組的長度不是很長,性能還是很不錯的,因為poll一次在核心中申請4K(一個頁的大小來存放fd),盡量控制在4K以内
至于epoll還是poll的一種優化,傳回後不需要對所有的fd進行周遊,在核心中維持了fd的清單。select和poll是将這個核心清單維持在使用者态,然後傳遞到核心中。但是隻有在2.6的核心才支援。
epoll更适合于處理大量的fd ,且活躍fd不是很多的情況,畢竟fd較多還是一個串行的操作.