Linux Select之坑
最近在写一个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.