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:

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.