select (Unix)

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 :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI: 表示对应的文件描述符有紧急的数据可读;
EPOLLERR: 表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: ET的epoll工作模式;

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); } 

参见[编辑]

外部链接[编辑]