最近在寫一個demo程序,調用select()來監聽socket狀態,流程以下:編程
r_set 初始化app
timeout 初始化3秒超時less
loop{socket
select(ntfs, &r_set, null, null, &timeout)ide
}oop
而後我驚奇的發現當對端發送消息時select()只會觸發一次,當下一次再有消息傳遞過來的時候不會被觸發,後來在網上搜索了一下說是要每一次循環都要初始化一次r_set,就能夠完成屢次觸發了:ui
timeout 初始化3秒超時this
loop{操作系統
r_set 初始化線程
select(ntfs, &r_set, null, null, &timeout)
}
同時超時也只有在第一次循環阻塞三秒,以後的循環徹底不會阻塞,我打日誌調試發如今第一次阻塞超時以後timeout 重置爲 0... ...
loop{
r_set 初始化
timeout 初始化3秒超時
select(ntfs, &r_set, null, null, &timeout)
}
這纔是正確姿式,可是我在網上看到不少例子並無重複初始化timeout,難道是我用了假select()?本着實事求是的原則我查看了一下man文檔,上面寫了這麼一句話:
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1 permits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.
好吧,確實很任性,但做用相似的C library 中的pselect()就不用這麼作:
C library/kernel differences
The pselect() interface described in this page is implemented by glibc. The underlying Linux system call is named pselect6(). This system call has somewhat different behavior from the glibc
wrapper function.
The Linux pselect6() system call modifies its timeout argument. However, the glibc wrapper function hides this behavior by using a local variable for the timeout argument that is passed to
the system call. Thus, the glibc pselect() function does not modify its timeout argument; this is the behavior required by POSIX.1-2001.
The final argument of the pselect6() system call is not a sigset_t * pointer, but is instead a structure of the form:
struct {
const sigset_t *ss; /* Pointer to signal set */
size_t ss_len; /* Size (in bytes) of object pointed
to by 'ss' */
};
This allows the system call to obtain both a pointer to the signal set and its size, while allowing for the fact that most architectures support a maximum of 6 arguments to a system call
它的意思是,pselect()在傳入時間參數的時候會付給一個參數temp,而後把temp傳進去,這樣就不會修改timeout參數了。
ps:
POSIX表示可移植操做系統接口(Portable Operating System Interface of UNIX,縮寫爲 POSIX ),POSIX標準定義了操做系統應該爲應用程序提供的接口標準,是IEEE爲要在各類UNIX操做系統上運行的軟件而定義的一系列API標準的總稱,其正式稱呼爲IEEE 1003,而國際標準名稱爲ISO/IEC 9945。
POSIX標準意在指望得到源代碼級別的軟件可移植性。換句話說,爲一個POSIX兼容的操做系統編寫的程序,應該能夠在任何其它的POSIX操做系統(即便是來自另外一個廠商)上編譯執行。
POSIX 並不侷限於 UNIX。許多其它的操做系統,例如 DEC OpenVMS 支持 POSIX 標準,尤爲是 IEEE Std. 1003.1-1990(1995 年修訂)或 POSIX.1,POSIX.1 提供了源代碼級別的 C 語言應用編程接口(API)給操做系統的服務程序,例如讀寫文件。POSIX.1 已經被國際標準化組織(International Standards Organization,ISO)所接受,被命名爲 ISO/IEC 9945-1:1990 標準。
最後,還有一點,在select監聽的socket,在其餘線程被close()掉,select是不會收到消息的:
Multithreaded applications If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another the file descriptor reopened between the time select() returned and the I/O operations was per‐ formed). On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary, any application that relies on a particular behavior in this scenario must be considered buggy.