关于select中fd限制问题

关于select中fd限制问题


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比较 大的时候,这里的效率是比较低的。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值