FD_CLR(),FD_ISSET(), FD_SET(),FD_ZERO() 这几个函数配合 select() 使用,他们的作用是用来设置文件描述符集合。文件描述符集合类型表示为 fd_set 。
关于 select() 函数可参考:http://www.groad.net/bbs/read.php?tid-1064.html
关于 fd_set() 类型的说明可参考:http://www.groad.net/bbs/read.php?tid-1063.html
这些函数定义如下:
[C++] 纯文本查看 复制代码 #define FD_SET(fd,fdsetp) __FD_SET(fd,fdsetp)
#define FD_CLR(fd,fdsetp) __FD_CLR(fd,fdsetp)
#define FD_ISSET(fd,fdsetp) __FD_ISSET(fd,fdsetp)
#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)
这些函数在不同的平台上实现的方法有些不同。许多平台使用 C 语言来实现,而 x86 32位平台上则用汇编语言来实现,下面就对这个平台的实现进行说明。
FD_ZERO :
[C++] 纯文本查看 复制代码 #define __FD_ZERO(fdsetp) \
do { \
int __d0, __d1; \
asm volatile("cld ; rep ; stosl" \
: "=m" (*(__kernel_fd_set *)(fdsetp)), \
"=&c" (__d0), "=&D" (__d1) \
: "a" (0), "1" (__FDSET_LONGS), \
"2" ((__kernel_fd_set *)(fdsetp)) \
: "memory"); \
} while (0)
传递进宏中的参数的是文件描述符集合类型 fd_set 的指针 fdsetp 。对整个集合的清零方法是使用 stosl 指令,stosl 指令是将 eax 寄存器中的值不断的填充 edi 寄存器所指向的地址,然后这个地址自动加 4 (stosl 中的 l 后缀表明是双字递增),循环填充的次数由 ecx 中的值来指定。
FD_SET:
[C++] 纯文本查看 复制代码 #define __FD_SET(fd,fdsetp) \
asm volatile("btsl %1,%0": \
"+m" (*(__kernel_fd_set *)(fdsetp)) \
: "r" ((int)(fd)))
FD_CLR:
[C++] 纯文本查看 复制代码 #define __FD_CLR(fd,fdsetp) \
__asm__ __volatile__("btrl %1,%0": \
"+m" (*(__kernel_fd_set *) (fdsetp)):"r" ((int) (fd)))
上面两个宏都使用 btsl 来设置或清零文件描述符集中的相应位。
FD_ISSET:
[C++] 纯文本查看 复制代码 #define __FD_ISSET(fd,fdsetp) (__extension__ ({ \
unsigned char __result; \
__asm__ __volatile__("btl %1,%2 ; setb %0" \
:"=q" (__result) :"r" ((int) (fd)), \
"m" (*(__kernel_fd_set *) (fdsetp))); \
__result; }))
先使用 bt 指令测试 fd 指定的位是否为 1,若为 1,则设置 CF=1,然后 setb 设置 __result 为 1,否则 __result 为 0 。整个宏的返回值就是 __result 。
当使用 _ansi 标志编译带有 gcc 扩展特性的程序时,为了避免发出警告,要使用 __extension__ 标签。比如在 glibc 中使用带有 long long 类型声明的函数时就会使用该标签。
关于 bts, btr 指令参考:http://www.groad.net/bbs/read.php?tid-3294.html |