说一说select、poll、epoll

select核心代码:

int fd_arr[MAX_SIZE];
Init_Array(fd_arr);
fd_set set;
while(1)
{
	FD_ZERO(&set);
	int i  = 0 ;
	for( ; i < MAX_SIZE ; ++i)
	{
		if(fd_arr[i] != -1)
		FD_SET(fd_arr[i],&set);
	}
	struct timeval tm = {5,0};
	int maxfd = Get_Max(fd_arr);
	int n = select(maxfd+1,&set,NULL,NULL,&tm);
	if( n < 0 )
	{
		//错误处理
	}
	else if( n == 0 )
	{
        //超时处理
	}
	else
	{
        //有事件发生,等待处理
    }
}

 

poll核心代码:

struct pollfd fd_arr[MAX_SIZE];
Init_Array(fd_arr);
Fd_Add(fd_arr,sockfd);
while(1)
{
	int n = poll(fd_arr,MAX_SIZE,5000);
	if( n == -1 )
	{
		//错误处理
	}
	else if( n == 0 )
	{
		//超时处理
	}
	else
	{
        //事件发生,等待处理
    }
}

 

epoll核心代码:

int epfd = epoll_create(MAX_SIZE);
Fd_Add(epfd,sockfd);
struct epoll_event events[MAX_SIZE];
while(1)
{
	int n = epoll_wait(epfd,events,MAX_SIZE,5000);
	if( n == -1 )
	{
		//错误处理
	}
	else if( n == 0 )
	{
		//超时处理
	}
	else
	{
		int i = 0 ;
		for( ; i < n ; ++i )
		{
            //事件处理
        }
    }
}

 

 

三个io模型比起来,poll和epoll名字虽然很像,但其实select和poll在实现机制上更像是一家的。

 

select的缺点:

1、每次调用select都需要把fd数集从用户空间拷贝到内核空间,效率低。

2、单个进程能监听的文件描述符存在限制,默认1024(多了效率太差)。

3、select为水平触发,应用程序如果没有完成对一个描述符的io操作,之后每次调用select还是会将这些描述符通知进程。

poll采用链表保存文件描述符,所以没有了去除了对监听数目的限制,但以上其他缺点还是存在。

 

select/poll模式:每次在把连接的fd数集拷贝到系统内核,让操作系统内核去轮询这些描述符是否有事件发生,若有,则再从内核拷贝到用户空间,让服务器处理已发生的事件,如果是百万级的连接,这一过程消耗巨大。所以才有了epoll。

 

epoll模式:当调用epoll_create方法时,Linux内核会创建一个eventepoll结构体。这个结构体有两个重要的数据结构:

一颗红黑树,保存所有添加到内核中需要监控的事件,每当调用epoll_ctl添加事件时,这些事件都会被挂载到红黑树中,所有添加进红黑树的事件都会跟设备驱动程序建立回调关系,就是说相应的事件发生时就会调用这个方法ep_poll_callback,它会将发生的事件添加到rdlist双向链表中。

一个双向链表rdlist,保存通过epoll_wait需要返回的满足条件的事件。在epoll中,每一个事件都有一个epitem结构体。调用epoll_wait时,就是检查rdlist中有没有epitem元素,如果有,则返回。

 

epoll的LT和ET模式:

LT就是我们前面提到的水平触发,ET就是边缘触发。select和poll都属于水平触发。epoll既可以使用水平触发也可以使用边缘触发。

LT和ET的本质区别:对于采用LT模式工作的文件描述符,当epoll_wait检测到其上有事件发生并返回时,你可以不用立即处理,因为当下次epoll_wait返回时还是会通告应用程序此事件的发生。而采用ET模式的文件描述符,当epoll_wait检测到其上有事件发生通知应用程序后须立即处理该事件,因为后续的epoll_wait调用将不再通知此事件。可见ET模式大大降低了epoll的触发次数。

 

 

网络上说什么io复用编程复杂度较多进程、多线程高,这个说法个人看来真是太牵强了(更何况现在各种网络库类似于libevent给你封装的好好的)。并且,多线程你需不需要考虑线程安全问题,你是不是要加锁?进程间呢?需不需要同步控制?进程太多是不是特占资源?当然,有时候io复用、多进程、多线程是需要协同工作的呢。

 

你可能感兴趣的:(说一说select、poll、epoll)