该select()和pselect()系统调用修改其参数(在“ fd_set *”参数),所以输入值告诉系统文件描述符检查和返回值告诉程序员哪些文件描述符当前可用它。
select()
pselect()
fd_set *
如果要为同一组文件描述符重复调用它们,则需要确保每个调用都有一个描述符的新副本。显而易见的方法是使用结构副本:
fd_set ref_set_rd; fd_set ref_set_wr; fd_set ref_set_er; ... ...code to set the reference fd_set_xx values... ... while (!done) { fd_set act_set_rd = ref_set_rd; fd_set act_set_wr = ref_set_wr; fd_set act_set_er = ref_set_er; int bits_set = select(max_fd, &act_set_rd, &act_set_wr, &act_set_er, &timeout); if (bits_set > 0) { ...process the output values of act_set_xx... } }
(已 编辑以删除不正确的struct fd_set引用-如“ R ..”所指出。)
struct fd_set
我的问题:
fd_set
我担心,以免存在隐藏的内存分配或类似的意外情况。(有宏/功能FD_SET(),FD_CLR(),FD_ZERO()和FD_ISSET()可以掩盖应用程序的内部结构。)
我可以看到MacOS X(Darwin)是安全的;因此,其他基于BSD的系统可能是安全的。您可以通过记录已知在答案中安全的其他系统来提供帮助。
(我确实不太关心它fd_set与8192个以上打开文件描述符的配合情况-默认打开文件的最大数量仅为256个,但最大数量为“无限制”。而且,由于结构为1 KB,因此复制代码的效率并不高,但是在每个循环中遍历文件描述符列表来重新创建输入掩码也不一定有效,也许select()当打开了这么多文件描述符时您就无法执行,尽管那是当您最有可能需要此功能。)
有一个相关的SO问题-询问[“ poll()vsselect()”],它解决了与此问题不同的一系列问题。
请注意,在MacOS X(大概是BSD)上,有一个FD_COPY()带有有效原型的宏或函数:
FD_COPY()
extern void FD_COPY(const restrict fd_set *from, restrict fd_set *to);
在尚不可用的平台上,可能值得进行仿真。
由于structfd_set这只是一个常规的C结构,因此应该总是可以的。我个人不喜欢通过=运算符进行结构复制,因为我在许多无法访问正常的编译器内在函数的平台上工作。memcpy()在我的书中,显式使用而不是让编译器插入函数调用是一种更好的方法。
structfd_set
=
memcpy()
在C规范中,第 6.5.16.1 节“ 简单赋值” (为简便起见,在此处进行了编辑):
以下条件之一应成立: … 左操作数具有与右类型兼容的结构或联合类型的合格或不合格版本; … 在 简单赋值 (=)中,将右操作数的值转换为赋值表达式的类型,并替换存储在由左操作数指定的对象中的值。 如果从另一个对象中读取存储在一个对象中的值,而该对象与第一个对象的存储有任何重叠,则该重叠应准确无误,并且两个对象应具有兼容类型的合格或不合格版本;否则,行为是不确定的。
以下条件之一应成立:
…
在 简单赋值 (=)中,将右操作数的值转换为赋值表达式的类型,并替换存储在由左操作数指定的对象中的值。
如果从另一个对象中读取存储在一个对象中的值,而该对象与第一个对象的存储有任何重叠,则该重叠应准确无误,并且两个对象应具有兼容类型的合格或不合格版本;否则,行为是不确定的。
因此,只要您struct fd_set实际上是一个常规Cstruct,您就可以保证成功。但是,它的确取决于编译器发出某种代码来执行此操作,或者取决于memcpy()它用于结构分配的任何内在函数。如果您的平台由于某种原因无法链接到编译器的内部库,则可能无法正常工作。
struct
如果您打开的文件描述符数量多于文件描述符的数量,则您将需要技巧struct fd_set。linux 手册页说:
An fd_set是固定大小的缓冲区。执行FD_CLR()或FD_SET()具有值fd为负或等于或大于FD_SETSIZE将导致未定义的行为。此外,POSIX必须fd是有效的文件描述符。
FD_CLR()
FD_SET()
fd
FD_SETSIZE
如下所述,证明您的代码在所有系统上都是安全的可能并不值得。 FD_COPY()提供此类用途,并且可以保证始终保证:
FD_COPY(&fdset_orig, &fdset_copy)用的&fdset_copy副本替换已分配的文件描述符集&fdset_orig。
FD_COPY(&fdset_orig, &fdset_copy)
&fdset_copy
&fdset_orig