关于I/O多路复用 select/poll epoll 函数

 
select/poll  epoll 函数 [本质上都是同步I/O] 
//epoll是linux特有的,而select和poll是在POSIX中规定的,跨平台支持更好

对于socket连接很多,并且连接基本都是活跃的,select / poll 的性能与 epoll 是差不多的
当有大量的 idle-connection,epoll效率高。
//区别就在于活跃量,select/poll 遍历所有文件描述符,epoll管理活跃的文件描述符

同步I/O:都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的
异步I/O:则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间(需要使用mmap函数进行虚拟内存映射)

与多线程(TPC(Thread Per Connection)模型)和多进程(典型的Apache模型(Process Per Connection,简称PPC))相比,
	I/O 多路复用的最大优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。



1. select()函数  	synchronous I/O multiplexing 
	//内核通过轮询方式对long类型的数组进行维护文件描述符
	
	#include  
	
	int select(int nfds, 
				fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 
				struct timeval *timeout);

第一个参数:要监视的文件描述符的范围,取监视的描述符数的最大值+1,在Linux上最大值一般为1024。

第二个参数:监视的可读描述符集合,只要有文件描述符读操作准备就绪,会自动修改地址内容。
第三个参数:监视的可写描述符集合。
第四个参数:监视的错误异常描述符集合。

				以上三个参数若不需要使用,直接设为 NULL 


第五个参数: 超时时间,告知内核等待所指定描述字中的任何一个就绪可花多少时间。
			
			struct timeval {
			  long	  tv_sec;		  /* seconds */
			  long	  tv_usec;		  /* microseconds */
			};

返回值:成功:返回就绪文件描述符数量(3个描述符集合)
		超时返回 0
		失败返回-1

函数功能:
	监视并等待多个文件描述符的属性变化 (读 写 错误异常)


与select()搭配使用的宏定义
	//清空集合  
	void FD_ZERO(fd_set *fdset);   
  
	//将一个给定的文件描述符加入集合之中  
	void FD_SET(int fd, fd_set *fdset);  
  
	//将一个给定的文件描述符从集合中删除  
	void FD_CLR(int fd, fd_set *fdset);  
  
	//检查集合中指定的文件描述符是否可以读写   
	int FD_ISSET(int fd, fd_set *fdset); 


三种可能场景:
	永远等待下去:timeout 设置为空指针 NULL,且没有一个描述符准备好。
	等待固定时间:timeout 设置为某个固定时间,描述符属性有变化返回。超时描述符属性未有变化,返回 0
	不等待(不阻塞):timeout 设置为 0 分 0 微妙,描述符属性有变化返回。描述符属性未有变化,返回 0



2. poll()函数 	wait for some event on a file descriptor
	//与select()类似,内核通过链表进行维护,理论上对文件描述符的最大数量没有限制,但是数量过大后性能也是会下降

	#include 

	int poll(struct pollfd *fds, nfds_t nfds, int timeout); 

第一个参数:被监控的文件描述符的属性,通过传递多个结构体,来实现对多个文件描述符进行监控
			struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
			}

			fd:被监视的文件描述符,
			events:设置文件描述符的属性,通过以下参数进行位或运算进行设置
					POLLIN     - 普通或优先带数据可读 
					POLLRDNORM - 普通数据可读
					POLLRDBAND - 优先级带数据可读
					POLLPRI    - 高优先级数据可读
					
					POLLOUT    - 普通或优先带数据可写
					POLLWRNORM - 普通数据可写
					POLLWRBAND - 优先级带数据可写
					
			revents:返回文件描述符的属性变化,通过读取该变量来查看文件描述符的变化
					events的设置参数也适用于revents
					
					revents特有的错误事件数值
						POLLERR  - 发生错误
						POLLHUP  - 发生挂起
						POLLNVAL - 描述符不是打开的文件

第二个参数:用来指定第一个参数的结构体数量

第三个参数:指定等待的毫秒数
			-1 永远等待,直到事件返回
			 0 立即返回
			>0 指定的毫秒数

函数功能:
	监视并等待多个文件描述符的属性变化



3. epoll()函数	I/O event notification facility
	//内核将epoll中的事件与设备网卡驱动程序建立回调关系,当相应的事件发生时会调用这个回调方法。
	//这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中

	#include 

	/* open an epoll file descriptor */
	1. int epoll_create(int size); 

参数:监听的数量,size > 0

函数功能: 
	生成 epoll 专用的文件描述符


	/* control interface for an epoll descriptor */
	2. int epoll_ctl(int epfd, int op, int fd, 
					 struct epoll_event *event);

第一个参数:epoll描述符,epoll_create()的返回值

第二个参数:对第三个参数fd进行操作,通过以下宏定义的方式
			EPOLL_CTL_ADD - 注册新的 fd 到 epfd 中;
			EPOLL_CTL_MOD - 修改已经注册的fd的监听事件;
			EPOLL_CTL_DEL - 从 epfd 中删除一个 fd;

第三个参数:需要监听的fd

第四个参数:描述链接文件描述符fd对象的事件

			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 */
            };

			events 通过以下参数进行位或运算进行设置
				EPOLLIN :对应的文件描述符可读
				EPOLLOUT:对应的文件描述符可写
				EPOLLPRI:对应的文件描述符有紧急的数据可读 
				EPOLLERR:对应的文件描述符发生错误
				EPOLLHUP:对应的文件描述符被挂起
				EPOLLET :将EPOLL设为边缘触发(Edge Trigger)模式,
							默认为水平触发(Level Trigger)
				EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,
								需要再次把这个socket加入到EPOLL队列里

函数功能:
	操作 EPOLL 文件描述符


	/* wait  for  an  I/O  event on an epoll filedescriptor */
	3. int epoll_wait(int epfd, struct epoll_event *events,
						int maxevents, int timeout);

第一个参数:epoll描述符,epoll_create()的返回值

第二个参数:分配好的 epoll_event 结构体数组,epoll 将会把发生的事件赋值到events 数组中
			(events 不可以是空指针,内核只负责把数据复制到这个 events 数组中,不会去帮助我们在用户态中分配内存)

第三个参数:event的个数

第四个参数:超时时间,毫秒
			设为-1,将阻塞

函数功能:
	等待事件的产生,收集在 epoll 监控的事件中已经发生的事件


水平触发(Level Trigger)
	LT 模式:支持block和no-block socket。
		当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。
		下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。
		效率会低于ET触发,尤其在大并发,大流量的情况下。
		但是LT对代码编写要求比较低,不容易出现问题。
		LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。

边缘触发(Edge Trigger)
	ET 模式:只支持no-block socket。
	当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。
	如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。
	该模式效率非常高,尤其在高并发,大流量的情况下,会比LT少很多epoll的系统调用。
	但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。

 

你可能感兴趣的:(net,linux)