zoukankan      html  css  js  c++  java
  • 关于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比较大的时候,这里的效率是比较低的。 
  • 相关阅读:
    Git 几个常用操作
    Ubuntu16.04安装YouCompleteMe
    常用命令总结
    启动Kernel提示Bad Data CRC
    linux4.15.1编译init/mounts报错
    编译Linux-4.15.1内核时遇到:“error : openssl/bio.h :No such file or folder”
    添加mtdparts引起的问题
    arm-linux-ld:u-boot.lds:1: ignoring invalid character `#' in expression
    smartgit的安装
    ubuntu下安装wine
  • 原文地址:https://www.cnblogs.com/yeta/p/3616728.html
Copyright © 2011-2022 走看看