API

1
2
3
4
5
6
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

select

  • nfds 3个描述符集合中最大的描述符+1,用来减少select遍历次数

  • readfds 监听对应描述符集合的读就绪(包括end-of-file即tcp断开)

  • writefds 监听对应描述符集合的写就绪

  • exceptfds 监听对应描述符集合的异常

对比epoll

  • 通过不同的set来监听对应事件,增加了数据拷贝量

  • 通过修改传入的set,来返回对应IO就绪套接字(所以对于应用层代码来说,还需要额外保存下原set)

  • 通过4个辅助宏来管理描述符,和判断哪个描述符上IO就绪

BUGS:

  • 在linux中,select可能返回对应的描述符读就绪,但实际读失败。可能原因有,数据到达但校验失败被丢弃或者被惊醒

优缺点

优点

  • 跨平台,普遍支持

缺点

  • 描述符大小有限制(也就限制了数量),因为内部采用bitmap(固定数组)实现,只能预分配固定大小的

    描述符必须小于FD_SETSIZE

  • 效率较低

    • 从api上看,监听套接字的多个事件时,需要更多的拷贝,因为select是通过set来区分事件的

    • 每次调用都需要从应用层拷贝描述符集合到内核

    • 每次调用返回,都需要遍历整个描述符集合比较

要点

fd_set实现(/usr/include/sys/select.h)

  • 描述符采用整数数组实现,通过bitmap来存储对应描述符

  • FD_SETSIZE默认为1024

    /usr/include/bits/typesizes.h
    #define __FD_SETSIZE        1024
    

源码分析

/usr/include/sys/select.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* 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 this macros. */
#undef __NFDBITS
/* It's easier to assume 8-bit bytes than to get CHAR_BIT. */
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
#define __FD_ELT(d) ((d) / __NFDBITS)
#define __FD_MASK(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)

/usr/include/bits/select.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#if defined __GNUC__ && __GNUC__ >= 2

# if __WORDSIZE == 64
# define __FD_ZERO_STOS "stosq"
# else
# define __FD_ZERO_STOS "stosl"
# endif

# define __FD_ZERO(fdsp) \
do { \
int __d0, __d1; \
__asm__ __volatile__ ("cld; rep; " __FD_ZERO_STOS \
: "=c" (__d0), "=D" (__d1) \
: "a" (0), "0" (sizeof (fd_set) \
/ sizeof (__fd_mask)), \
"1" (&__FDS_BITS (fdsp)[0]) \
: "memory"); \
} while (0)


#else /* ! GNU CC */

/* We don't use `memset' because this would require a prototype and
the array isn't too big. */

# define __FD_ZERO(set) \
do { \
unsigned int __i; \
fd_set *__arr = (set); \
for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) \
__FDS_BITS (__arr)[__i] = 0; \
} while (0)


#endif /* GNU CC */

#define __FD_SET(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))

#define __FD_CLR(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))

#define __FD_ISSET(d, set) \
((__FDS_BITS (set)[__FD_ELT (d)] & __FD_MASK (d)) != 0)

留言

2016-04-18