select (Unix)
此條目包含過多行話或專業術語,可能需要簡化或提出進一步解釋。 (2012年11月29日) |
此條目没有列出任何参考或来源。 (2010年5月5日) |
select是用于I/O多路转接的一个系统调用函数。
在C程序中,该系统调用在 sys/select.h 或 unistd.h 中声明,语法如下:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
参数 | 描述 |
---|---|
nfds | sets的文件描述符的最大值 |
readfds | fd_set 类型,包含了需要检查是否可读的描述符,输出时表示哪些描述符可读。可为 NULL。 |
writefds | fd_set 类型,包含了需要检查是否可写的描述符,输出时表示哪些描述符可写。可为 NULL。 |
errorfds | fd_set 类型,包含了需要检查是否出错的描述符,输出时表示哪些描述符出错。可为 NULL。 |
timeout | struct timeval 类型的结构体,表示等待检查完成的最长时间。 |
为了维护fd_set类型的参数,会使用下面四个宏:FD_SET(), FD_CLR(), FD_ZERO() 和 FD_ISSET()。
返回值:
这个函数将返回描述符集的个数, 如果超时返回为0,错误则返回-1。
参看:
- select(2)
- poll(2)
select与epoll的区别[编辑]
epoll | select | |
---|---|---|
概述 | epoll是个模块,由三个系统调用组成,内核中由用文件系统实现 | select是个系统调用 |
结构体定义 | typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t; struct epoll_event { __uint32_t events; // epoll 监听的事件类型 epoll_data_t data; /* User data variable */ }; | struct timeval{ long tv_sec;//second long tv_usec;//minisecond } typedef struct fd_set { u_int fd_count; int fd_array[FD_SETSIZE]; } //fd_array可SIZE*8个socket |
可用的事件 | EPOLLIN :表示对应的文件描述符可以读; | fd_set有三种类型: readfds, writefds, exceptionfds
|
操作函数 | 三个系统调用:epoll_create epoll_ctl epoll_wait | 一个系统调用:select 四个宏: FD_ZERO FD_SET FD_CLR FD_ISSET |
运行模式 | 边沿触发 (ET)、状态触发 (LT) | 状态触发 |
运行过程 | int fd = epoll_create(1); // 创建一个 epoll 实例,参数可以是任意正整数 struct epoll_event events[xxxB];// epoll 实例将发生的事件写入该数组 while(1){ int nfds = epoll_wait( ); // 等待事件发生 for(int i=0; i<nfds; i++){ … }//end for }//end while | struct timeval tv; fd_set rfds; tv={5,0}; // 设置超时 while(1){ FD_ZERO(&rfds); if (!select()) continue; for(int i=0;i<maxfds; i++){ ... } // 结束 for 循环 } // 结束 while 循环 |
优点 | 1)epoll_wait返回的都是有效数据,可直接从struct epoll_event[]中获取事件,效率高。 | |
缺点 | 每次select有数据要遍历全部socket | |
注意事项 | 每次取事件后,要重新注册此socket的事件epoll。(epoll_ctl) | 每次select之前要重置rfds的值。(FD_ZERO) |
说明:以上无论epoll_create, fd_set都受限于系统中单个进程能够打开的文件句柄数。
示例[编辑]
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <sys/select.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #define PORT "9421" /* function prototypes */ void die(const char*); int main(int argc, char **argv) { int sockfd, new, maxfd, on = 1, nready, i; struct addrinfo *res0, *res, hints; char buffer[BUFSIZ]; fd_set master, readfds; ssize_t nbytes; (void)memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; if(-1 == (getaddrinfo(NULL, PORT, &hints, &res0))) die("getaddrinfo()"); for(res = res0; res; res = res->ai_next) { if(-1 == (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol))) { perror("socket()"); continue; } if(-1 == (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int)))) { perror("setsockopt()"); continue; } if(-1 == (bind(sockfd, res->ai_addr, res->ai_addrlen))) { perror("bind"); continue; } break; } if(-1 == sockfd) exit(EXIT_FAILURE); freeaddrinfo(res0); if(-1 == (listen(sockfd, 32))) die("listen()"); if(-1 == (fcntl(sockfd, F_SETFD, O_NONBLOCK))) die("fcntl()"); FD_ZERO(&master); FD_ZERO(&readfds); FD_SET(sockfd, &master); maxfd = sockfd; while(1) { memcpy(&readfds, &master, sizeof(master)); (void)printf("running select()\n"); if(-1 == (nready = select(maxfd+1, &readfds, NULL, NULL, NULL))) die("select()"); (void)printf("Number of ready descriptor: %d\n", nready); for(i=0; i<=maxfd && nready>0; i++) { if(FD_ISSET(i, &readfds)) { nready--; if(i == sockfd) { (void)printf("Trying to accept() new connection(s)\n"); if(-1 == (new = accept(sockfd, NULL, NULL))) { if(EWOULDBLOCK != errno) die("accept()"); break; } else { if(-1 == (fcntl(new, F_SETFD, O_NONBLOCK))) die("fcntl()"); FD_SET(new, &master); if(maxfd < new) maxfd = new; } } else { (void)printf("recv() data from one of descriptors(s)\n"); nbytes = recv(i, buffer, sizeof(buffer), 0); if(nbytes <= 0) { if(EWOULDBLOCK != errno) die("recv()"); break; } buffer[nbytes] = '\0'; printf("%s", buffer); (void)printf("%zi bytes received.\n", nbytes); close(i); FD_CLR(i, &master); } } } } return 0; } void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); }