字符串缓冲区太小怎么解决 您所在的位置:网站首页 字符串缓冲区大小 字符串缓冲区太小怎么解决

字符串缓冲区太小怎么解决

2024-07-15 18:26| 来源: 网络整理| 查看: 265

学习于: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默认的使用模式

看代码:

8139408428f02f47070691ed6d3d1a62.png

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

cd333081b857c02a443e590b97660d86.png

运行代码的的输出:

0a35d7fa77a14ca94a60635b5b0875b7.png

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

4d88aab7f493575c900e17692dfa1b8f.png

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

后面改为:

fb479fccbbe59ccd261eaf42a9a112e6.png

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

正确输出如下:

0c5e353c9564ca2f7fde0a993ac203ed.png

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

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

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

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

特点:

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

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

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

d7ca60fc9db357a19e616c6c4804718a.png

演示:

779ba885d78556f13e6795fe71d5ce6e.png

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

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

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

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

6632bac14d6d8a761a1ca224d0d5ad23.png

可是这又产生了一个问题: 这样的话,因为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属性又设置进去

下面是视频中的文档:

7ed377e6e059033f58cde345ccae8013.png

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

30691e5dbc9d34d30c2e431e6979195f.png

然后是接收数据那里:

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

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

f1cb1974c827f9966cc6020e48b6074a.png

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

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

a971d69eafc4759b78b46b4fbe71fc31.png

成功的返回:

8a8ad9a2d47c0d9ab19f0ace35b2e68e.png

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

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

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

简直perfect。

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

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

欢迎交流讨论。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有