epoll三种工作模式

epoll三种工作模式

水平触发模式 LT

之前伪代码的问题。
假设有100个数据,但是某次读只能读50个字节。下一次再读50个字节。

  • 只要fd对应的缓冲区有数据,即判断缓冲区
  • epoll_wait返回
  • 返回的次数与发送的数据次数没有关系
client server 100bytes epoll_wait第1次返 50bytes epoll_wait第2次返 50bytes client server

用户虽然把数据收下来了,但是可能不一定知道epoll_wait返回了几次。
假设设置的读缓冲区比较小,就会出现上述分两次读的情况。

  • 打个比方:老师告诉你要做作业,“每次”都会提醒你下,你语文要做了,数学要做了,英语要做了。这里的老师就好比内核,会督促你把作业都做完。
  • 和边沿模式不同,老师就提醒你一次,语文要做哦。你只做了成语判断,还有阅读分析没做,但是她不会管你。一会儿可能数学作业要布置下来了,你只做了选择题,大题可能没做。
  • LT支持block和非block方式。这种模式,内核会告诉你一个fd是否就绪,如果你不做任何操作,内核还是会继续通知你,所以这种模式编程出错可能性要小一点。

边沿触发模式-ET

  • fd --默认具有阻塞属性
  • Client Send Data to Server
    • 发一次数据,server的epoll_wait返回一次
    • 不再乎数据是否读完,客户端发一次,就返回一次
    • “绞肉机”里面剩下的肉需要新的肉塞进去才能出来
    • 貌似不合理却合理:epoll_wait调用次数越多,系统开销越大。一般不单独使用
  • 存在即合理:epoll_wait调用次数越多,系统的开销越大。
  • 不单独用,配合非阻塞方式
  • 如果读不完
    • while(recv()); 内核缓冲区没有数据,数据读完之后会阻塞
    • 解决阻塞问题
      • 设置非阻塞 -fd (边沿非阻塞模式)
//修改监听的方式
tmp.events = EPOLL_IN | EPOLL_ET;  //只增加一个EPOLL_ET宏
tmp.events.cfd = connfd;           //accept进来的描述符

边沿非阻塞触发 ET+NO-BLOCK

  • 效率高,高速工作方式,只支持no-block socket。当fd从not ready–>ready时,内核通过epoll通知你。然后假设你已知晓该事件,并不会再为那个fd提供更多的就绪通知。如果一直不对这个fd做I/O操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)
  • 如何解决非阻塞
    • open()
      • 设置flags
      • 必须 O_WDRW | O_NOBLOCK //变成非阻塞
      • 适应终端文件: /dev/tty
    • fcntl
      • fcnt方式设置cfd非阻塞模式
//关键:设置cfd为非阻塞模式
int flag = fcntl(cfd, F_GETFL);
flag | = O_NOBLOCK;
fcntl(cfd, F_SETFL, flag);

struct epoll_event tmp_evt;
tmp_evt.event = EPOLLIN|EPOLLET;
tmp_evt.data.fd = cfd; //accept返回后的描述符
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp_evt);
/*... ... ...*/ 
//循环读取数据
while((recv_len = recv(fd, buf, sizeof(buf), 0))>0)
{
	write(STDOUT_FILENO, buf, recv_len ); //打印到终端
	send(fd, buf, len ,0); //返回给客户端
}
if(len == 0)
{
	printf("客户端断开连接\n");
	epoll_ctl(expd, EPOLL_CTL_DEL, fd, NULL);
	close(fd);
}else if(len = -1){
	printf("error\n");
	exit(1);
}

上述代码在读完数据后会退出:

  • 原因
    • 数据读完之后,又去读,但是现在是非阻塞情况,又去强行读缓冲区
    • 强行读了一个没有数据的缓冲区(fd),导致返回-1
    • 判断 error == EAGAIN
if(len == -1)
{
	if(error == EAGAIN)
	{
		printf("缓冲区数据已读完");	
	}
	else
	{
		printf("error\n");
		exit(1);
	}
}

文件描述符突破1024限制

  • select 需要编译内核才能突破1024限制
  • poll和epoll 可以突破1024
    • poll 内部链表
    • epoll红黑树
  • 查看文件描述符上线
    • cat /proc/sys/fs/file-max
    • 和机器硬件相关
  • 通过配置文件修改上限值
    • /etc/security/limits.conf
    • soft nofile 8000 //不能超过limits上限
    • hard nofile 8000
    • 重启或注销才生效
    • ulimit -n 3000

epoll反应堆模型

在struct epoll_event结构体中的

struct epoll_event{
	__uint32_t events; /*Epoll events*/
	epoll_data_t data; /*User data variable*/
}

typedef union epoll_data{
	void *ptr;
	int fd;				//epoll_wait返回后利用response_event[i].data.fd来判断某个fd就绪
	uint32_t u32;
	uint64_t u64;
}epoll_data_t;
//fake code-----------------------------------------------------------------------
rets=epoll_wait(gefd, response_events, max_events, -1);
for(int i=0;i<rets;i++)
{
	...
	response_events.data.fd = connfd; //利用的epoll_event.data.fd来帮助我们判断,
	...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值