本页使用了标题或全文手工转换

select (Unix)

维基百科,自由的百科全书
跳转至: 导航搜索

select是用于I/O多路转接的一个系统调用函数。在C程序中,它定义于sys/select.h或unistd.h。 使用select要使用这些文件和 sys/time.h。

声明

       int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
参数 描述
nfds sets的文件描述符的最大值
readfds fd_set type 类型,只读的描述符集
writefds fd_set type 类型,只写的描述符集
errorfds fd_set type 类型,错误的描述符集
timeout 超时等待时间

为了维护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 events */
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
运行模式 Edge Triggered (ET)、Lev Triggered (LT) LT
运行过程 int fd = epoll_create(xxA); //xxA可监听的socket

struct epoll_event events[xxxB];//可返回的事件数
while(1){

 int nfds = epoll_wait(  );   //wait event occur
for(int i=0; i<nfds; i++){
…. }//end for

}//end while

struct timeval tv;

fd_set rfds;
tv={5,0}; //set time out
while(1){

 FD_ZERO(&rfds);
if (!select()) continue;
for(int i=0;i<maxfds; i++){
...} //end for

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

参见[编辑]

外部链接[编辑]