您的位置:首页 > 其它

关于select中fd限制问题

2013-10-09 18:01 447 查看


关于select中fd限制问题

转载自:http://iredfox.diandian.com/post/2009-10-22/7402106




select
是多路复用,或异步模型中经常用到的一个系统调用。

基本原型为:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

该函数的一个缺点就是nfds不能太大。上限为1024.为什么会有这样的限制,今天就来一起看看select的具体实现。

再/usr/include/sys/select.h中可以看到select使用的基本数据的定义:

/* The fd_set member is required to be an array of longs. */

typedef long int __fd_mask;

/* Some versions of <linux/posix_types.h> define these macros. */

#undef __NFDBITS

#undef __FDELT

#undef __FDMASK

/* It's easier to assume 8-bit bytes than to get CHAR_BIT. */

#define __NFDBITS (8 *
sizeof (__fd_mask))

#define __FDELT(d)
((d) / __NFDBITS)

#define __FDMASK(d)
((__fd_mask) 1 << ((d) % __NFDBITS))

/* fd_set for
select and pselect.
*/

typedef struct

{

/* XPG4.2 requires this member name. Otherwise avoid the name

from the global namespace. */

#ifdef __USE_XOPEN

__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];

# define __FDS_BITS(set) ((set)->fds_bits)

#else

__fd_mask __fds_bits[__FD_SETSIZE /
__NFDBITS];

# define __FDS_BITS(set) ((set)->__fds_bits)

#endif

} fd_set;

/* Maximum number
of file descriptors in `fd_set'. */

#define FD_SETSIZE __FD_SETSIZE

#ifdef __USE_MISC

/* Sometimes the
fd_set member is assumed to have this type. */

typedef __fd_mask fd_mask;

/* Number of bits per
word of `fd_set' (some code assumes this
is 32). */

# define NFDBITS __NFDBITS

#endif

/* Access macros for `fd_set'. */

#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)

通过这段代码我们不难发现,select中关键数据结果fd_set.他的大小就决定了系统能承受的fd数量.

fe_set被定义为一个结构体,其唯一的一个成员变量是一个long
int的数组。

剩下问题就计算数组大小了。__FD_SETSIZE / __NFDBITS.

查看include/linux/posix_types.h,如下:

#undef __NFDBITS

#define __NFDBITS (8
* sizeof(unsigned long))

#undef __FD_SETSIZE

#define __FD_SETSIZE 1024

#undef __FDSET_LONGS

#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)

#undef __FDELT

#define __FDELT(d)
((d) / __NFDBITS)

#undef __FDMASK

#define __FDMASK(d)
(1UL << ((d) % __NFDBITS))

typedef struct {

unsigned long fds_bits [__FDSET_LONGS];

} __kernel_fd_set;

系统自己也定义了一个__kernel_fd_set数据结构。这里我们不关心这个。

这里可以看到__FD_SETSIZE 为 1024.于是之前的数组大小就可以计算出来是32.

总共可以使用的bit是32*32=1024.也就是说,select的最大fd是1023.(select调用的时候要加1)

现在再看下与fd_set相关的几个宏又是如何实现的.

在arch/alpha/include/asm/posix_types.h给出了简单的c函数实现方式:

#ifdef __KERNEL__

#ifndef __GNUC__

#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)]
|= __FDMASK(d))

#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)]
&= ~__FDMASK(d))

#define __FD_ISSET(d,
set) (((set)->fds_bits[__FDELT(d)] & __FDMASK(d))
!= 0)

#define __FD_ZERO(set)
\

((void) memset ((void *) (set), 0, sizeof (__kernel_fd_set)))

#else /* __GNUC__ */

/* With GNU C, use inline functions instead so args are evaluated only once: */

#undef __FD_SET

static __inline__ void __FD_SET(unsigned long
fd, __kernel_fd_set *fdsetp)

{

unsigned long _tmp = fd / __NFDBITS;

unsigned long _rem = fd % __NFDBITS;

fdsetp->fds_bits[_tmp] |= (1UL<<_rem);

}

#undef __FD_CLR

static __inline__ void __FD_CLR(unsigned long fd, __kernel_fd_set *fdsetp)

{

unsigned long _tmp = fd / __NFDBITS;

unsigned long _rem = fd % __NFDBITS;

fdsetp->fds_bits[_tmp] &= ~(1UL<<_rem);

}

#undef __FD_ISSET

static __inline__ int __FD_ISSET(unsigned long fd, const __kernel_fd_set *p)

{

unsigned long _tmp = fd / __NFDBITS;

unsigned long _rem = fd % __NFDBITS;

return (p->fds_bits[_tmp] & (1UL<<_rem)) != 0;

}

/*

* This will unroll the loop for the normal constant case (8 ints,

* for a 256-bit fd_set)

*/

#undef __FD_ZERO

static __inline__ void __FD_ZERO(__kernel_fd_set *p)

{

unsigned long *tmp = p->fds_bits;

int i;

if (__builtin_constant_p(__FDSET_LONGS)) {

switch (__FDSET_LONGS) {

case 16:

tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;

tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;

tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0;

tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0;

return;

case 8:

tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;

tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;

return;

case 4:

tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;

return;

}

}

i = __FDSET_LONGS;

while (i) {

i--;

*tmp = 0;

tmp++;

}

}

#endif /* __GNUC__ */

#endif /* __KERNEL__ */

再x86的系统架构中,是采用汇编语言实现的,效率更高。

include/asm-x86/posix_types_32.h

#undef __FD_SET

#define __FD_SET(fd,fdsetp) \

asm volatile("btsl %1,%0": \

"+m" (*(__kernel_fd_set *)(fdsetp)) \

: "r" ((int)(fd)))

#undef __FD_CLR

#define __FD_CLR(fd,fdsetp) \

asm volatile("btrl %1,%0": \

"+m" (*(__kernel_fd_set *)(fdsetp)) \

: "r" ((int) (fd)))

#undef __FD_ISSET

#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; \

}))

#undef __FD_ZERO

#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数量多的时候,select效率低。简单看一下select的实现就不难发现了.

fs/select.c

你可能发现了,select实现并不是放在net的目录下。select并未为网络通信而实现,而是对一般的fd都可用。也再次证明了一点,再linux/unix

下面一切都是文件。

int do_select(int n, fd_set_bits *fds, s64 *timeout)

{

.....

for (i = 0; i < n; ++rinp, ++routp, ++rexp)

{

.....

}

}

不用看具体的实现,也可以明白了,select是对从0开始的一直到n-1的每一个描述做检查。因此当n比较大的时候,这里的效率是比较低的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: