IO多路复用之select详解

一、select用途

select是IO多路复用模型中的一种,属于标准的IO多路复用,在Windows、Linux下都可以使用。IO多路复用同时监听多个IO,一次系统调用判定所有IO的可读写状态,会一直阻塞或者指定一个超时时间,直到一个或多个文件描述符集合称为就绪态。select避免了多次的系统调用和阻塞,但不适合文件数量庞大、IO流量频繁的时候,会随着fd的数量增大性能下降明显。

二、接口定义

windows定义

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 
const struct timeval *timeout)

linux定义

int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)

其他fd_set操作接口

FD_CLR(inr fd,fd_set* set);将描述符fd从fdset所指向的集合中移除
FD_SET(int fd,fd_set*set);将描述符fd添加到fdset所指向的集合中
FD_ZERO(fd_set *set); 将fdset指向的集合初始化为空
FD_ISSET(int fd,fd_set *set);测试描述词组set中相关fd 的位是否为真

select参数详解:

nfds:select中监视的文件句柄数,一般设为要监视的文件中的最大文件号加一。
readfds:检测读是否就绪的文件描述符集合
writefds :检测写是否就绪的文件描述符集合
exceptfds:检测异常情况是否发生的文件描述符集合
(1、信包模式下伪终端主设备上从设备状态发生改变;2、流式套接字接收到了带外数据)
timeout: 设为NULL,等待直到某个文件描述符发生变化;设为00毫秒,不等待直接返回;
设为大于0的值,有描述符变化或超时时间到才返回。
select返回值:负数表示有错误发生;大于等于0,表示有n个描述符就绪。具体是哪个,
还需要用FD_ISSET遍历判定。

三、windows的select源码

int select(int nfds, fd_set *readfds, fd_set *writefds, 
fd_set *exceptfds, const struct timeval *timeout)

#ifndef FD_SETSIZE
#define FD_SETSIZE      64
#endif /* FD_SETSIZE */
typedef struct fd_set {
    
    
        unsigned int fd_count;               /* how many are SET? */
        unsigned int  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

#define FD_ZERO(set) (((fd_set *)(set))->fd_count=0)
#define FD_SET(fd, set) do { \
    u_int __i; \
    for (__i = 0; __i < ((fd_set *)(set))->fd_count; __i++) { \
        if (((fd_set *)(set))->fd_array[__i] == (fd)) { \
            break; \
        } \
    } \
    if (__i == ((fd_set *)(set))->fd_count) { \
        if (((fd_set *)(set))->fd_count < FD_SETSIZE) { \
            ((fd_set *)(set))->fd_array[__i] = (fd); \
            ((fd_set *)(set))->fd_count++; \
        } \
    } \
} while(0, 0)

#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set *)(set))

四、linux的select源码

int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__exceptfds, struct timeval *__timeout);
		   
#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)
# 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)

  
#define __FD_SETSIZE		1024
#define __NFDBITS	(8 * (int) sizeof (__fd_mask))
# define __FDS_BITS(set) ((set)->fds_bits)
#define	__FD_ELT(d)	((d) / __NFDBITS)

五、select使用代码样例

#pragma once
#include  "stdafx.h"
#include <functional>
using namespace std;

#define KIT_FILE_EVENT_READABLE 0x01
#define KIT_FILE_EVENT_WRITABLE 0x02

class KitEventOpManager4Select 
{
    
    
private:
	int _maxFd;
	fd_set _readFds;
	fd_set _writeFds;
	fd_set _cloneReadFds;
	fd_set _cloneWriteFds;
	function<void(int, int)> _eventHandler;
private:
	
	void resetMaxFd(int changedFd)
	{
    
    
		if (changedFd != _maxFd)
		{
    
    
			return;
		}
		while (_maxFd >= 0)
		{
    
    
			if(FD_ISSET(_maxFd, &_readFds) > 0 || FD_ISSET(_maxFd, &_readFds) > 0)
			{
    
    
				break;
			}
			_maxFd--;
		}
	}
public:
	KitEventOpManager4Select(function<void(int,int)> eventHandler): _maxFd(0)
	{
    
    		
		FD_ZERO(&_readFds);
		FD_ZERO(&_writeFds);

		FD_ZERO(&_cloneReadFds);
		FD_ZERO(&_cloneWriteFds);
		this->_eventHandler = eventHandler;
		
	}	
	void setReadFd(int fd) 
	{
    
    
		FD_SET(fd, &_readFds);
		if (fd > _maxFd)
		{
    
    
			_maxFd = fd;
		}
	}
	void setWriteFd(int fd)
	{
    
    
		FD_SET(fd, &_writeFds);
		if (fd > _maxFd)
		{
    
    
			_maxFd = fd;
		}
	}

	void clearReadFd(int fd) 
	{
    
    
		FD_CLR(fd, &_readFds);
		resetMaxFd(fd);
	}
	void clearWriteFd(int fd) 
	{
    
    
		FD_CLR(fd, &_writeFds);
		resetMaxFd(fd);
	}


	void poll(const timeval *time)
	{
    
    
		memcpy(&_cloneReadFds, &_readFds, sizeof(fd_set));
		memcpy(&_cloneWriteFds, &_writeFds, sizeof(fd_set)); //fds会被修改,每次select的时候,需要克隆一份
		#ifdef __linux__
			timeval realTime = *time;
			int retval = select(_maxFd + 1, &_cloneReadFds, &_cloneWriteFds, NULL, &realTime);//select
		#else
			int retval = select(_maxFd + 1, &_cloneReadFds, &_cloneWriteFds, NULL, time);//select 
		#endif
		if (retval > 0)
		{
    
    
			int mask=0;
			for (int fd = 0; fd <= _maxFd; fd++)
			{
    
    
				if (FD_ISSET(fd, &_cloneReadFds))
				{
    
    
					mask |= KIT_FILE_EVENT_READABLE;
				
				}

				if (FD_ISSET(fd, &_cloneWriteFds))
				{
    
    
					mask |= KIT_FILE_EVENT_WRITABLE;
					
				}
				if (mask > 0)
				{
    
    
					_eventHandler(fd,mask);
				}

			}
		}
	}
};

猜你喜欢

转载自blog.csdn.net/gamekit/article/details/106271528