核心提示:select优点1)select()的可移植性更好,在某些Unix系统上不支持poll()2)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒。select缺点1) 单个进程可监视...
select优点
1)select()的可移植性更好,在某些Unix系统上不支持poll()
2)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒。
select缺点
1) 单个进程可监视的fd数量被限制。
2) 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
3) 对fd进行扫描时是线性扫描。fd剧增后,IO效率较低,因为每次调用都对fd进行线性扫描遍历,所以随着fd的增加会造成遍历速度慢的性能问题
4)select() 函数的超时参数在返回时也是未定义的,考虑到可移植性,每次在超时之后在下一次进入到select之前都需要重新设置超时参数。
api原型:
#include#include int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
每个参数不介绍了,这里有几点说明:
nfds这个参数必须是三个文件描述符集合中所包含的最大的文件描述符号+1。该参数让select()变得更有效率, 因为此时内核就不用去检查大于这个值得文件描述符号是否属于这些文件描述符集合。
一些宏
#includevoid FD_ZERO(fd_set *fdset); void FD_SET(int fd, fd_set *fdset); void FD_CLR(int fd, fd_set *fdset); int FD_ISSET(int fd, fd_set *fdset); //具体实现如下 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1L << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1L << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1L << ((n) % NFDBITS))) #define FD_ZERO(p) (__extension__ (void)({ \ size_t __i; \ char *__tmp = (char *)p; \ for (__i = 0; __i < sizeof (*(p)); ++__i) \ *__tmp++ = 0; \ }))
上述宏安如下方式工作
1、FD_ZERO将fdset所指的集合初始化为空
2、FD_SET将fd添加到fdset中
3、FD_CLR将fd从fdset中删除
4、FD_ISSET表示如果fd在fdset中则返回1,否则为0
当timeout为NULL, 或者指向的结构体字段为非0时, select()将阻塞直到有下列事件发生:
1、三个文件描述符集合至少有一个成为就绪态
2、该调用被信号处理例程打断
3、timeout中指定的时间上限已经超时了
常用的框架代码:
int fds[128]; const int len = 128; int startup(char *ip,int port) { int listen_sock = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in local; local.sin_family = AF_INET; local.sin_addr.s_addr = inet_addr(ip); local.sin_port = port; bind(&listen_sock,(struct sockaddr*)&local,sizeof(local)); if(listen(listen_sock,5) < 0) { perror("listen"); } return listen_sock; } int main() { if(argc != 3) { usage(argv[0]); exit(1); } int i = 0; for(;i < len;i++) { fds[i] = -1; } int listen_sock = startup(argv[1],atoi(argv[2])); fd_set rfds; fds[0] = listen_sock; int done; while(!done) { int max_fd = -1; FD_ZERO(&rfds); for(int i = 0;i < len;i++) { if(fds[i] != -1) { FD_SET(fds[i],&rfds); } max_fd = fd[i] > max_fd ? fd[i]:max_fd; } struct timeval timeout = {5,0}; switch(select(max_fd+1,&rfds,NULL,NULL,NULL)) { case 0: printf("timeout\n"); break; case -1: perror("select"); break; default: { if(FD_ISSET(listen_sock,&rfds)) { struct sockaddr_in peer; int len = sizeof(peer); int new_fd = accept(listen_sock,\ (struct sockaddr*)&peer,&len); if(new_fd > 0) { printf("get a new client->%s:%d\n",\ inet_addr(peer.sin_addr),\ ntohs(peer.sin_port)); for(int i = 0; i < len;i++) { if(fds[i] == -1) { fds[i] = new_fd; break; } } if(i == len) { close(new_fd); } } } else { char buf[1024]; for(int i = 0; i < len;i++) { if(i != 0 && FD_ISSET(fds[i],\ &rfds)) { ssize_t _s = read(fds[i],buf,sizeof(buf)) if(_s > 0) { buf[_s] = '\0'; printf("client:%s\n",buf); } else if(_s == 0) { printf("client:%d is close\n",fds[i]); close(fds[i]); fds[i] = -1; } else perror("read"); } } } } break; } } }