学习于:https://www.bilibili.com/video/av44660437/?p=9

前文:何柄融:多路复用I/O select poll epoll 何柄融:select poll epoll 再一次的了解

这里就不讲epoll的基础了,如果对epoll没了解的请去前文学习。

当时有:

epoll除了提供select/ poll那种IO事件的电平触发(Level Triggered) 外,还提供了边沿触发(Edge Triggered) ,这就使得用户空间程序有可能缓存IO状态,减少epoll_ wait/epoll_ pwait的调用,提高应用程序效率。(这个没怎么看得懂。。)

所以,特地来学习一下epoll的ET和LT。

epoll的三种工作模式:

1.水平触发模式(默认):Level Triggered LT

2.边沿触发模式 : Edge Triggered ET

3.边缘非阻塞触发

先来介绍这个默认使用的水平触发模式(LT)

特点:1.只要fd对应的缓冲区有数据,epoll_wait就返回

2.返回的次数与发送数据的次数没有关系。

3.epoll默认的使用模式

看代码:

核心:关键就在于处理已连接描述符时的缓冲区大小。之前我们都是使用1024个字节,而写的时候也不会太大。 而这里把1024改成了5,也就是说每次处理5个字节。而如果客户端一次发送过来100个字节,那么就会因为客户端的这一次发送,服务器这边要调用20次的epoll_wait(),因为 100个字节发送过来,服务器这边的已连接描述符这里会有个对应的缓冲区缓冲这100个字节,然后因为缓冲区有数据,所以就会出发epoll_wait()的返回,而返回之后服务器又只从缓冲区接收5个字节来处理。那么就会剩下95个字节。那么当服务器处理完这5个字节后,本来应该阻塞在epollwait这里,不断的唤醒沉睡的,可是由于缓冲区还有数据,所以又马上让epollwait返回,然后再一次处理这个文件描述符的事件。所以,这里就会多次调用epollwait。下面是一个比较形象的图:

运行代码的的输出:

注意到,每次都多了69.123.31,这属于乱码,具体解释如下:

printf函数:有时候会发现打印不出来。因为printf有一个缓冲区,好像是8k,当你打印字符串的时候,如果没加后面的 "n" ,那么它就有可能因为缓冲区没有满而导致不输出。如果加了后面的“n”,就会强制把字符串从缓冲区中输出。而有时候,n 也不能强制从缓冲区输出。(这个缓冲区其实很像我们在文件io这篇文章中提到的8k的缓冲区,机制都是一样的,缓冲区满/调用flush/进程结束 使数据刷新,关于这里 https://blog.csdn.net/u013790372/article/details/54645860 这篇文章写得不错,大家可以了解一下)

后面改为:

直接向标准输出写就ok了。

正确输出如下:

即多次读取数据,直到把缓冲区的数据读完。

而由前面的知识,我们知道epollwait调用次数越多,系统的开销越大。(总结一下就是:epollwait会进入内核空间检测内核文件描述符的事件,这里就有cpu的在系统调用时产生的上下文切换,估计还有些我不知道的东西。。)

然后,epoll的水平触发模式就到这里了。主要就是缓冲区有数据就epollwait返回。

然后来看边沿触发模式(ET)

特点:

客户端给server发数据,发一次数据,server的epoll_wait返回一次,不在乎客户端的缓冲区的数据是否读完。(其它客户端的连接也会导致返回和数据读取,不要忘了前面的基础)

也就是说,缓冲区里面可能存有大量的客户端之前发来的数据。这样就不太好。

修改后:如下图:也就是设置了结构里的events。或上 epollet ,这样就能变为边沿触发模式。这里是添加新连接的时候的修改,而代码前面一开始把监听集合添加进红黑树的时候,也要修改这个events。(此时代码的buf大小依然是5个字节)

演示:

具体就是:客户端一次性输入比较多的字符,服务端连进一个客户端,就返回一次epollwait,然后读取一次缓冲区的5个字节。后面客户端再次发送,还是读取缓冲区的前5个字节,也就是上次服务器接收了但是没有从缓冲区刷新出来的数据,而此次发送的数据被放在了缓冲区的最后面,所以没显示出来。(这个要理解清楚!)

现在来总结一下两者的区别:LT是我服务端一次只读这么多,我可以多次读你很多数据。消耗系统资源多。 ET是客户端发送一次或者连接一次我就返回一次,然后慢慢读。理解了前面的,其实基本上全部说下来都没关系。

然后我们来看看怎么解决EL读取的总是缓冲区的旧数据的问题:

下面这张图片改为while(recv())就可以了,也就是说,只要缓冲区有数据,我就把它全部读出来。这样就不会说每次都读取很少的字节了。

可是这又产生了一个问题: 这样的话,因为io默认设置是阻塞的啊,也就是说fd默认阻塞属性啊。那么在while(recv())这里,读完数据之后,如果没有数据来的话,就会阻塞在这里。那么就不会继续返回到外面的while(1)的大循环中了。也就不会再epollwait函数那里阻塞了(也就是没有办法委托再去内核检测了),这就完全把整个程序的功能弄错了。

所以,我们要解决阻塞问题,所以,必须设置fd为非阻塞。

所以,我们前面介绍其实是边缘阻塞模式。

接下来我们要介绍边缘非阻塞模式

我们要解决的是如何设置fd的非阻塞的属性。

让我们先来看看如何设置非阻塞(先脱离前面的问题先)

1.open()函数

它可以设置flags

必须 O_WDRE | O_NONBLOCK

终端文件 : /dev/tty 当前所操作的终端。前面已经讲得很多次了。(也就是说,终端文件就可以使用open来设置非阻塞)

比如 fd=open("/dev/tty" ,O_WDRE | O_NONBLOCK); 那么返回正常的话就是返回的fd就是非阻塞的。这个是针对终端而言的。

2. 由于上面的方法针对的是终端,而我们现在要操作的是内核缓冲区。

fcntl :1.复制文件描述符 2.设置文件描述符的属性为非阻塞。(第二个是我们要用的)

使用流程

int flag=fcntl(fd.F_GETFL); 第一步:获取文件描述符的flags属性 get flag

然后我们要把非阻塞属性给它设置进去

flag |=O_NONBLOCK 修改flag属性

fcntl(fd,F_SETFL.flags) 此处是吧flag属性又设置进去

下面是视频中的文档:

然后我们来到epoll使用代码上看怎么改:下面这里我们只修改已连接的文件描述符,不修改前面的监听描述符了。

然后是接收数据那里:

注意此时的buf还是5个字节

下面先是len>0的情况,len>0说明此时缓冲区有数据可读,所以不断地循环从缓冲区中接收5个字节,然后打印到终端,并且返回给客户端。

下面是len返回为0和-1的情况。len等于0说明客户端断开了连接,此时把此客户端对应的文件描述符从内核中的红黑树移除,然后关闭掉该文件描述符。

len等于-1。此时会有一个errno的量(好像在线程那里看见过),它如果等于EAGAIN,就说明此时是缓冲区数据读完,那么就跳出这里,回到epollwait中阻塞;如果不等于,那么就是出现异常了,那就退出程序。

成功的返回:

这样的话,我们就很完美地结束了epoll的边缘非阻塞的编程。

这样的话,边缘非阻塞效率最高

既没有LT的多次重复调用epoll_wait的系统开销大,也没有ET的读取到的都是旧数据或者是ET的阻塞。 客户端一次发送数据,我就一次性把发到缓冲区的数据全部接收并且处理。

简直perfect。

感觉那个视频很不错,最起码epoll的几种模式讲得很不错,推荐一下。

如果大家对java的nio有了解的话,我强烈建议来看看我这篇文章: 何柄融:nio基础和手写一个基于nio的demo , 然后你就会发现epoll和nio的编程居然是一模一样的,没任何差别。

欢迎交流讨论。

字符串缓冲区太小怎么解决_epoll的两种模式 ET和LT printf的缓冲区问题 边缘非阻塞模式...相关推荐

  1. clobzh字符串缓冲区太小的解决方法_用4K屏玩LOL英雄联盟游戏指针太小解决方法已找到...

    换了4K屏后玩LOL指针过小,游戏内可调指针大小根本没用,感觉游戏都不会打了,琢磨好久找到一个可行的方法给大家分享一下 找到游戏文件→右键游戏图标属性→兼容性→更改高DPI设置→勾选替代高DPI缩放行 ...

  2. ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小解决办法

    1.今天写的存储过程在执行过程中,报如下错误. exec PRO_T_008pro_update_add_delete(17,1,1,1,1,45.0,54.0,45.0,45.0,45.0,54.0 ...

  3. 生成awr报告时报ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小

    生成awr报告时报错: ERROR: ORA-06502: PL/SQL: 数字或值错误 :  字符串缓冲区太小 ORA-06512: 在 "SYS.DBMS_WORKLOAD_REPOSI ...

  4. 也谈Oracle异常: ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小

    也谈Oracle异常: ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小 参考文章: (1)也谈Oracle异常: ORA-06502: PL/SQL: 数字或值错误 : 字符 ...

  5. Oracle bug 使用max或min函数into到一个char类型报字符缓冲区太小的错误

    这个BUG出现会报错如下: selectto_char(max(RENEWAL_DATE)) intoM_YEAR_MONTH fromt_renewal_schedule; ORA-06502: P ...

  6. ORA-64203: 目标缓冲区太小, 无法容纳字符集转换之后的 CLOB 数据

    线上遇到bug,日志显示错误ORA-64203: Destination buffer too small to hold CLOB data after character set conversi ...

  7. win10系统中photoshop cs6中界面字体太小的解决方法

    win10系统中photoshop cs6中界面字体太小的解决方法 参考文章: (1)win10系统中photoshop cs6中界面字体太小的解决方法 (2)https://www.cnblogs. ...

  8. 笔记本高分屏字体模糊_4k/1080p屏幕字体模糊怎么办?win10高分屏笔记本字体模糊太小的解决办法...

    使用台式电脑的朋友应该还好,显示器尺寸本来就大,主要是针对一些使用笔记本的朋友.笔记本发展到现在的程度,基本上都是1080P或者4K的分辨率,但笔记本的屏幕尺寸往往只有14寸.15.6寸等.这么高的分 ...

  9. POJ2676,HDU4069解决数独的两种实现:DFS、DLX

    搜索实现:解决数独有两种思考策略,一种是枚举当前格能填的数字的种数,这里有一优化策略就是先搜索能填入种数小的格子:另一种是考虑处理某一行(列.宫)时,对于某一个没用过的数字,若该行(列.宫)只有一个可 ...

最新文章

  1. 男人们聚会时,都聊些什么?
  2. 洛谷 P3994 高速公路
  3. 解决yarn build时报错:内存溢出FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed
  4. eclipse控制台自动换行不分割单词_这 7 个实用的文档技巧,不掌握就太可惜了...
  5. P3302-[SDOI2013]森林【主席树,LCA,启发式合并】
  6. NUC1196 Sum【水题+数学题】
  7. 更改VMware硬盘大小
  8. 伪随机数生成算法-梅森旋转(Mersenne Twister/MT)
  9. 结合Android源码分析总结单例模式的几种实现方式
  10. 基于HT for Web的3D树的实现
  11. centos lnmp一键安装
  12. 开发一款APP软件,需要哪些技术支持呢?
  13. linux 截屏_如何在Linux上截图
  14. 2021毓英中学高考成绩查询,泉州知名中学2020高考成绩喜报龙虎榜
  15. 罗技产品序列号追溯条码扫描系统
  16. .NET 高级架构师0003 架构师之路(2)---架构师的职责
  17. StringWriter介绍
  18. 阿里云周宇:神龙计算平台智能运维体系建设
  19. 《互联网时代》 第十集 眺望
  20. 加密行业存在大量“违规行为”?全球加速整治,监管或将进入新时代

热门文章

  1. Android存储空间不足的解决办法
  2. 自由职业者在合作之前要弄懂的15个问题
  3. CentOS安装Pure-FTPd+MySQL
  4. casio dt-930 条码采集器 盘点软件源程序
  5. 编译安装samba-4.85
  6. 第5章-Vue.js交互及生命周期练习
  7. ReactNative学习笔记(一)环境搭建
  8. VirtualBox中修改Ubuntu-server分辨率
  9. Xfire方式开发和解析webservice
  10. JQuery学习系列(九)AJAX