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。提供了四個宏來操作集合。FD_ZERO() 清除集合。FD_SET() 和 FD_CLR() 分別向集合新增和刪除給定的檔案描述符。FD_ISSET() 測試檔案描述符是否是集合的一部分;這在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為零且timeout為非NULL的select()作為以亞秒精度睡眠的相當可移植的方式。 在Linux上,select()修改timeout以反映未睡眠的時間;大多數其他實現不會這樣做。(POSIX.1-2001允許這兩種行為。)這在將讀取timeout的Linux程式碼移植到其他作業系統時以及將程式碼移植到在迴圈中對多個select()重用struct timeval而不重新初始化它的Linux時都會導致問題。認為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(select()首次出現在4.2BSD中)。通常可以移植到/從支援BSD套接字層克隆(包括System V變體)的非BSD系統。但是,請注意,System V變體通常在退出前設定timeout變數,但BSD變體不會。
pselect()在POSIX.1g和POSIX.1-2001中定義。 備註fd_set是固定大小的緩衝區。使用負值或等於或大於FD_SETSIZE的fd值執行FD_CLR()或FD_SET()將導致未定義的行為。此外,POSIX要求fd是有效的檔案描述符。關於所涉及的型別,經典情況是timeval結構的兩個欄位是長整型(如上所示),並且該結構在<sys/time.h>中定義。POSIX.1-2001的情況是
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
|
其中結構在<sys/select.h>中定義,資料型別time_t和suseconds_t在<sys/types.h>中定義。 關於原型,經典情況是應該為select()包含<time.h>。POSIX.1-2001的情況是應該為select()和pselect()包含<sys/select.h>。Libc4和libc5沒有<sys/select.h>標頭檔案;在glibc 2.0及更高版本中,此標頭檔案存在。在glibc 2.0中,它無條件地為pselect()提供了錯誤的原型,在glibc 2.1-2.2.1中,它在定義_GNU_SOURCE時提供pselect(),在glibc 2.2.2-2.2.4中,它在定義_XOPEN_SOURCE且值為600或更大時提供它。毫無疑問,自從POSIX.1-2001以來,它應該預設提供原型。 版本
pselect()在核心2.6.16中新增到Linux。在此之前,在glibc中模擬了pselect()(但請參見BUG)。Linux備註Linux pselect()系統呼叫修改其timeout引數。但是,glibc包裝器函式透過使用傳遞給系統呼叫的超時引數的區域性變數來隱藏此行為。因此,glibc pselect()函式不會修改其超時引數;這是POSIX.1-2001所需的行為。錯誤Glibc 2.0提供了不帶sigmask引數的pselect()版本。從2.1版本開始,glibc提供了一個使用sigprocmask(2)和select()實現的pselect()模擬。此實現仍然容易受到pselect()旨在防止的競爭條件的影響。在缺乏pselect()的系統上,可以使用自管道技巧(其中訊號處理程式向管道的另一端(在主程式中由select()監控)寫入一個位元組)實現可靠的(且更可移植的)訊號捕獲。 在Linux下,select()可能會將套接字檔案描述符報告為“準備好讀取”,而隨後的讀取仍然會阻塞。例如,當資料到達但檢查後校驗和錯誤並被丟棄時,可能會發生這種情況。在其他情況下,檔案描述符也可能被錯誤地報告為已準備好。因此,在不應阻塞的套接字上使用O_NONBLOCK可能更安全。 參見
select_tut(2).
有關大致相關的資料,請參見accept(2)、connect(2)、poll(2)、read(2)、recv(2)、send(2)、sigprocmask(2)、write(2)、epoll(7)、feature_test_macros(7)
廣告
|