最近在寫一個demo程序,調用select()來監聽socket狀態,流程如下:
r_set 初始化
timeout 初始化3秒超時
loop{
select(ntfs, &r_set, null, null, &timeout)
}
然后我驚奇的發現當對端發送消息時select()只會觸發一次,當下一次再有消息傳遞過來的時候不會被觸發,后來在網上搜索了一下說是要每一次循環都要初始化一次r_set,就可以完成多次觸發了:
timeout 初始化3秒超時
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:
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.
