select() - Unix、Linux 系統呼叫
廣告
名稱
select、pselect、FD_CLR、FD_ISSET、FD_SET、FD_ZERO - 同步 I/O 多路複用
語法
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#define _XOPEN_SOURCE 600
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
|
描述
select() 和 pselect() 允許程式監視多個檔案描述符,等待一個或多個檔案描述符準備就緒以進行某種型別的 I/O 操作(例如,輸入可能)。如果可以執行相應的 I/O 操作(例如,read(2))而不會阻塞,則檔案描述符被認為已準備就緒。
select() 和 pselect() 的操作相同,但有三個區別
序號 | 描述 |
(i) | select() 使用一個 struct timeval(秒和微秒)型別的超時,而 pselect() 使用一個 struct timespec(秒和納秒)型別的超時。 |
(ii) | select() 可能會更新 timeout 引數以指示剩餘時間。pselect() 不會更改此引數。 |
(iii) | select() 沒有 sigmask 引數,並且其行為類似於使用 NULL sigmask 呼叫 pselect()。 |
監視三組獨立的檔案描述符。列在 readfds 中的檔案描述符將被監視,以檢視是否有字元可供讀取(更準確地說,檢視讀取是否不會阻塞;特別是,檔案描述符在檔案結束時也已準備就緒),列在 writefds 中的檔案描述符將被監視,以檢視寫入是否不會阻塞,列在 exceptfds 中的檔案描述符將被監視,以檢視異常。退出時,集合會在原地修改以指示哪些檔案描述符實際上更改了狀態。如果沒有任何檔案描述符要監視相應的事件類別,則三組檔案描述符中的每一組都可以指定為 NULL。
提供了四個宏來操作集合。
這在 select() 返回後很有用。
nfds 是三組集合中編號最高的描述符加 1。
timeout 是 select() 返回之前經過時間的上限。它可以為零,導致 select() 立即返回。(這對於輪詢很有用。)如果 timeout 為 NULL(無超時),則 select() 可以無限期阻塞。
sigmask 是指向訊號掩碼的指標(請參閱 sigprocmask(2));如果它不為 NULL,則 pselect() 首先用 sigmask 指向的訊號掩碼替換當前訊號掩碼,然後執行“select”函式,然後恢復原始訊號掩碼。
除了 timeout 引數精度上的差異外,以下 pselect() 呼叫
ready = pselect(nfds, &readfds, &writefds, &exceptfds,
timeout, &sigmask);
|
等效於原子地執行以下呼叫
sigset_t origmask;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
|
需要 pselect() 的原因是,如果要等待訊號或檔案描述符準備就緒,則需要原子測試以防止競爭條件。(假設訊號處理程式設定全域性標誌並返回。然後,在呼叫 select() 之前測試此全域性標誌可能會無限期掛起,如果訊號恰好在測試後但呼叫之前到達。相反,pselect() 允許首先阻塞訊號,處理已到達的訊號,然後使用所需的 sigmask 呼叫 pselect(),從而避免了競爭條件。)
超時
涉及的時間結構在 <sys/time.h> 中定義,如下所示
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
|
和
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
|
(但是,請參見下面關於 POSIX.1-2001 版本的內容。)一些程式碼使用所有三組都為空、n 為零以及非 NULL timeout 呼叫 select() 作為以亞秒精度休眠的相當便攜的方式。
在 Linux 上,select() 會修改 timeout 以反映未休眠的時間;大多數其他實現不會這樣做。(POSIX.1-2001 允許這兩種行為。)這會導致在將讀取 timeout 的 Linux 程式碼移植到其他作業系統時出現問題,以及在將程式碼移植到 Linux 時出現問題,這些程式碼在迴圈中對多個 select() 重複使用 struct timeval 而無需重新初始化它。在 select() 返回後,認為 timeout 未定義。
返回值
如果成功,select() 和 pselect() 將返回三個返回的描述符集中包含的檔案描述符的數量(即,在 readfds、writefds、exceptfds 中設定的位的總數),如果在發生任何有趣的事情之前超時過期,則該數量可能為零。如果出錯,則返回 -1,並且 errno 被相應地設定;集合和 timeout 變得未定義,因此在錯誤後不要依賴其內容。
錯誤
標籤 | 描述 |
EBADF | 在一組中給出了無效的檔案描述符。(可能是已關閉的檔案描述符,或已發生錯誤的檔案描述符。) |
EINTR | 捕獲到訊號。 |
EINVAL | nfds 為負或 timeout 中包含的值無效。 |
ENOMEM | 無法為內部表分配記憶體。 |
示例
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don’t rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
return 0;
}
|
符合標準
select() 符合 POSIX.1-2001 和 4.4BSD
pselect() 在 POSIX.1g 和 POSIX.1-2001 中定義。
廣告
|