Linux 平臺 select 操作
#include <sys/select.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)
select()
操作用于程序監(jiān)控相應(yīng)的文件描述符(file descriptor扁耐,fd)是否準(zhǔn)備好,即能否進(jìn)行無阻塞地讀取操作黄鳍。
文件描述符集(file descriptor set边器,fds)是函數(shù)最主要的參數(shù),它是一個 fd_set
類型的結(jié)構(gòu)體再膳,里面可以存儲最大 FD_SETSIZE(1024)
個 fd。為了安全地操作 fds曲横,系統(tǒng)提供了相應(yīng)的宏:
-
void FD_ZERO(fd_set *set)
- 用于清空 fds 里存儲的 fd喂柒,一般在初始化時清空 fds 。 -
void FD_SET(int fd, fd_set *set)
- 用于向 fds 里添加一個 fd 禾嫉。 -
void FD_CLR(int fd, fd_set *set)
- 用于從 fds 里清除一個 fd 灾杰。 -
int FD_ISSET(int fd, fd_set *set)
- 用于判斷 fd 是否在 fds 里,一般在 select() 調(diào)用結(jié)束之后使用熙参。
函數(shù)參數(shù)有三個 fds:
fds | 函數(shù)調(diào)用前 | 函數(shù)調(diào)用后 |
---|---|---|
readfds | 監(jiān)聽 readfds 里的 fd 是否有可讀的 | 可無阻塞讀的 fd |
writefds | 監(jiān)聽 writefds 里的 fd 是否有可寫的 | 可無阻塞寫的 fd |
exceptfds | 監(jiān)聽 exceptfds 里的 fd 是否有發(fā)送異常的(一般未使用) | 發(fā)生異常的 fd |
select()
函數(shù)內(nèi)部會修改 readfds, writefds 和 exceptfds 內(nèi)存儲的 fd艳吠,函數(shù)返回時這些 fds 內(nèi)存儲的是可以進(jìn)行相應(yīng)操作的 fd 。因此若希望在一個 loop 里重復(fù)監(jiān)聽某一個 fd尊惰,需要每次調(diào)用 select()
之前重新設(shè)置 fds 讲竿。
函數(shù)參數(shù) nfds
是三個 fds 里序號最大的 fd 的序號值 + 1 。
timeout
是一個struct timeval
的結(jié)構(gòu)體指針弄屡,它代表希望阻塞等待的時間。函數(shù)返回時鞋诗,Linux 平臺下的 timeout
會被重寫膀捷,用于存儲阻塞剩余未使用的時間,而其他平臺的實現(xiàn)一般不會這樣做削彬。因此考慮到兼容性全庸,一般認(rèn)為 select() 返回后秀仲,timeout 對象就沒用了,下次調(diào)用 select() 需要重新初始化 timeout 壶笼。
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
- 當(dāng)傳入的 timeout 里的字段值大于 0 神僵,select() 函數(shù)阻塞相應(yīng)的時間。當(dāng)一個 fd 準(zhǔn)備好覆劈,被信號處理中斷保礼,以及阻塞時間超時時,函數(shù)返回责语。
- 當(dāng)傳入的 timeout 里的
tv_sec
和tv_usec
都為 0 時炮障,select() 函數(shù)立即返回,用于輪詢的場景坤候。 - 當(dāng)傳入的 timeout 為 NULL 時胁赢,select() 永久阻塞,直到有 fd 準(zhǔn)備好白筹。
函數(shù)返回值可以為:
- EBADF - 錯誤的 fd 智末。
- EINTR - 捕獲到信號。
- EINVAL -
nfds
為負(fù)數(shù)或大于 RLIMIT_NOFILE 的限制徒河,timeout
的值不合理系馆。 - ENOMEN - 函數(shù)內(nèi)部動態(tài)分配內(nèi)存失敗。
一個 demo 如下虚青,簡單地監(jiān)控標(biāo)準(zhǔn)輸入:
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.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");
exit(EXIT_SUCCESS);
}
無任何輸入場景:
tangjia@FA001334:~/Jackistang$ ./tmp
No data within five seconds.
輸入數(shù)據(jù)場景:
tangjia@FA001334:~/Jackistang$ ./tmp
123456798
Data is available now.
參考: