slect( )poll( )epoll( )函数详解

Posted 正在起飞的蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了slect( )poll( )epoll( )函数详解相关的知识,希望对你有一定的参考价值。

1、slect()函数

1.1、函数原型

	#include  <sys/time.h>

	int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

(1)maxfdp:指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错;
(2)readfds:要监视读操作的文件描述符集合;
(3)writefds:要监视写操作的文件描述符集合;
(4)errorfds:要监视出错操作的文件描述符集合;
(5)timeout:函数返回超时;
(6)返回值:负值–出错,0–超时返回,正值–有文件可读,或可写,或可读可写;
补充:fd_set有专门的宏来操作,不必深究细节;

1.2、timeout值说明

struct timeval
	      
        long tv_sec;   /*秒 */
        long tv_usec;  /*微秒 */
    

1.3、struct fd_set集合的操作宏

FD_ZERO(fd_set*);        //清空集合,相当于memset

FD_SET(int, fd_set*);    //将一个给定的文件描述符加入集合之中

FD_CLR(int,   fd_set*);  //将一个给定的文件描述符从集合中删除

FD_ISSET(int, fd_set*);  //检查集合中指定的文件描述符是否可以读写

1.4、示例代码

	int status = -1;
	fd_set fds_r;
	fd_set fds_w;
	
	//将集合清零
	FD_ZERO(&fds_r);
	FD_ZERO(&fds_w);
	
	//文件描述符fd1添加到读监视集合
	FD_SET(fd1,&fds_r); 
	
	//文件描述符fd1添加到写监视集合
	FD_SET(fd2,&fds_w); 
	
	//文件描述符的最大值加1
	maxfdp=fd1>fd2 ? fd1+1 : fd2+1;
	
	//监视集合中的文件描述符,直到有文件可读会可写才返回
	status = select(maxfdp,&fds_r, &fds_w, NULL, NULL)
	if(status <= 0)
	
		//出错,什么也不做
	
	else	//说明有文件描述符可读或者可写,需要轮询式的查询所有监视的文件描述符集合
	
		if (FD_ISSET(fd1,&fds_r))
		
			read(fd1, buf, size);
		
		 
		if (FD_ISSET(fd2,&fds_w))
		
			write(fd2, buf, size);
		
	

2、poll()函数

2.1、函数原型

	#include <poll.h>

	int poll(struct pollfd fd[], nfds_t nfds, int timeout);

(1)fd:是一个struct pollfd数组,保存要监控的文件描述符信息;
(2)nfds:要监视的文件描述符的数据;
(3)timeout:超时时间,单位是毫秒;
(4)返回值:要么超时返回,要么阻塞住;返回值n>0时,表示有n个文件描述符发生了相应的事件;

2.2、timeout值说明

(1)INFTIM:永远等待,阻塞住;
(2)0:立即返回,不阻塞进程;
(3)>0:阻塞指定时间后返回;

2.3、struct pollfd结构体

 struct pollfd

	  int fd; //要监视的文件描述符

	  short events; //要监视fd将发生的事件

	  short revents; //poll函数返回时,fd发生的的事件

  ;

2.4、事件标志

	POLLIN 普通或优先级带数据可读
	POLLRDNORM 普通数据可读
	POLLRDBAND 优先级带数据可读
	POLLPRI 高优先级数据可读
	POLLOUT 普通数据可写
	POLLWRNORM 普通数据可写
	POLLWRBAND 优先级带数据可写
	POLLERR 发生错误
	POLLHUP 发生挂起
	POLLNVAL 描述字不是一个打开的文件

常用的是POLLIN(可读)和POLLOUT(可写);

2.5、示例代码

	//要监控的文件描述符数量
	int fdCnt = 2; 
	
	//申请保存文件描述符的struct pollfd结构体
	struct pollfd *pollfd;	
	pollfd = (struct pollfd*)calloc(fdCnt, sizeof(struct pollfd));

	//fd1文件描述符监视是否可读
	pollfd[0].fd = fd1;
	pollfd[0].events = POLLIN;

	//fd2文件描述符监视是否可写
	pollfd[1].fd = fd2;
	pollfd[1].events = POLLOUT;
	
	//监视文件描述符并且阻塞,知道有文件描述符发生相应的事件
	numready = poll(pollfd, fdCnt, INFTIM);
	
	//轮询监视的文件描述符
	for(i=0; i< numready && fdCnt > 0; i++)
	
		if(pollfd[i].revents & POLLIN)
		
			read(pollfd[i].fd, buf, size)
			i++;
		
		
		if(pollfd[i].revents & POLLIN)
		
			write(pollfd[i].fd, buf, size)
			i++;
		
		
		fdCnt--;
	

3、epoll()函数

3.1、epoll_create()函数

//创建一个epoll对象,同时返回该对象的描述符
int epoll_create(int size);

(1)size:要监听的文件描述符的数量;
(2)返回值:创建的epoll对象描述符;

3.2、epoll_ctl()函数

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

(1)epfd:epoll_create()函数返回的epoll对象描述符;
(2)op:本次的操作;
(3)fd:要监听的文件描述符;
(4)event:关联要监听的fd,里面会对要监听的fd进行设置;

3.3、参数op

含义
EPOLL_CTL_ADD将参数 fd 指定的描述符添加到 epoll 对象中,同时将其关联到一个 epoll 事件对象——即参数 event 所指定的值
EPOLL_CTL_MOD修改描述符 fd 所关联的事件对象 event,前提是该 fd 已经添加到了 epoll 对象中
EPOLL_CTL_DEL将描述符 fd 从 epoll 对象中移除,此时参数 event 被忽略,也可指定为 NULL

3.4、fd监听的事件

含义
EPOLLIN监听 fd 是否可读
EPOLLOUT监听 fd 是否可写
EPOLLRDHUPLinux 2.6.17 后可用。监听流式套接字对象是否关闭或半关闭
EPOLLPRI监听是否有紧急数据可读

3.5、struct epoll_event结构体

typedef union epoll_data 
  void        *ptr;
  int          fd;	//监听的文件描述符fd
  uint32_t     u32;
  uint64_t     u64;
 epoll_data_t;

struct epoll_event 
  uint32_t     events;      /* fd要监听的事件 */
  epoll_data_t data;        /* 用户数据,和fd绑定 */
;

(1)events:fd要监听的事件;
(2)data:fd绑定的数据,将来epoll_wait()函数返回发生事件的fd时,会返回fd绑定的epoll_data_t结构体数据;

3.6、epoll_wait()函数

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

(1)epfd:epoll_create()函数返回的epoll对象描述符;
(2)events:外部定义的一个struct epoll_event结构体数据,将地址传进去,epoll_wait()函数在返回时会将发生
事件的fd信息填充到该结构体中;
(3)maxevents:告诉epoll_wait()函数,events数组有几个成员;
(4)timeout:超时时间;

3.7、示例代码

//创建epoll对象,最多监听4个文件描述符
int  epfd = epoll_create(4);

struct epoll_event ev;
ev.data.fd = fd1; // 注意这个值必须要指定,不然 epoll_wait 返回了你也不知道是谁发生了事件
ev.events = EPOLLIN; // 想监听可读事件,因为没有指定 EPOLLET 选项,所以默认是水平触发
epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &ev);

ev.data.fd = fd2; // 注意这个值必须要指定,不然 epoll_wait 返回了你也不知道是谁发生了事件
ev.events = EPOLLOUT; // 想监听可写事件,因为没有指定 EPOLLET 选项,所以默认是水平触发
epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &ev);

 // 事件数组 evts 用来保存 epoll_wait 返回的事件
 struct epoll_event evts[4];
  
// 开始等待事件发生,res 表示发生了事件的个数
int res = epoll_wait(epfd, evts, 4, -1);

// 开始处理所有事件
for (i = 0; i < res; ++i) 
  // 这个 fd 就是你一开始通过 event 的 data 成员传进去的。
  fd = evts[i].data.fd;
  if (evts[i].events & EPOLLIN) 
    	//读文件描述符
  
   if (evts[i].events & EPOLLOUT) 
   		//写文件描述符
   

2022深度学习开发者峰会 5月20日13:00让我们相聚云端,共襄盛会!

以上是关于slect( )poll( )epoll( )函数详解的主要内容,如果未能解决你的问题,请参考以下文章

select,poll,epoll

I/O多路转接模型 [select] [poll] [epoll]

I/O多路转接模型 [select] [poll] [epoll]

select poll和epoll区别

五种高阶IO模型以及多路转接技术(selectpoll和epoll)及其代码验证

五种高阶IO模型以及多路转接技术(selectpoll和epoll)及其代码验证