本单位二级网站建设管理制度中国旅游网站建设

当前位置: 首页 > news >正文

本单位二级网站建设管理制度,中国旅游网站建设,外贸网站建设优化推广,企业网站规划书操作系统IO模型与实现原理 阻塞IO 模型 应用程序调用一个IO函数#xff0c;导致应用程序阻塞#xff0c;等待数据准备好。如果数据没有准备好#xff0c;一直等待….数据准备好了#xff0c;从内核拷贝到用户空间,IO函数返回成功指示。 当调用recv()函数时#xff0c;系…操作系统IO模型与实现原理 阻塞IO 模型 应用程序调用一个IO函数导致应用程序阻塞等待数据准备好。如果数据没有准备好一直等待….数据准备好了从内核拷贝到用户空间,IO函数返回成功指示。 当调用recv()函数时系统首先查是否有准备好的数据。如果数据没有准备好那么系统就处于等待状态。当数据准备好后将数据从系统缓冲区复制到用户空间然后该函数返回。在套接应用程序中当调用recv()函数时未必用户空间就已经存在数据那么此时recv()函数就会处于等待状态。 非阻塞IO模型 我们把一个SOCKET接口设置为非阻塞就是告诉内核当所请求的I/O操作无法完成时不要将进程睡眠而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好如果没有准备好继续测试直到数据准备好为止。在这个不断测试的过程中会大量的占用CPU的时间。上述模型绝不被推荐。 把SOCKET设置为非阻塞模式即通知系统内核在调用Windows Sockets API时不要让线程睡眠而应该让函数立即返回。在返回时该函数返回一个错误代码。图所示一个非阻塞模式套接字多次调用recv()函数的过程。前三次调用recv()函数时内核数据还没有准备好。因此该函数立即返回WSAEWOULDBLOCK错误代码。第四次调用recv()函数时数据已经准备好被复制到应用程序的缓冲区中recv()函数返回成功指示应用程序开始处理数据。 IO复用模型 主要是通过select和epoll对一个IO端口两次调用两次返回比阻塞IO并没有什么优越性关键是能实现同时对多个IO端口进行监听 I/O复用模型会用到select、poll、epoll函数这几个函数也会使进程阻塞但是和阻塞I/O所不同的的这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作多个写操作的I/O函数进行检测直到有数据可读或可写时才真正调用I/O操作函数。 当用户进程调用了select那么整个进程会被block而同时kernel会“监视”所有select负责的socket当任何一个socket中的数据准备好了select就会返回。这个时候用户进程再调用read操作将数据从kernel拷贝到用户进程。 这个图和blocking IO的图其实并没有太大的不同事实上还更差一些。因为这里需要使用两个系统调用(select和recvfrom)而blocking IO只调用了一个系统调用(recvfrom)。但是用select的优势在于它可以同时处理多个connection。select/epoll的优势并不是对于单个连接能处理得更快而是在于能处理更多的连接。 信号驱动IO模型 两次调用两次返回 允许套接口进行信号驱动I/O,并安装一个信号处理函数进程继续运行并不阻塞。当数据准备好时进程会收到一个SIGIO信号可以在信号处理函数中调用I/O操作函数处理数据。 异步IO模型 简单进程/线程模型 这是一种非常简单的模式服务器启动后监听端口阻塞在accept上当新网络连接建立后accept返回新连接服务器启动一个新的进程/线程专门负责这个连接。从性能和伸缩性来说这种模式是非常糟糕的原因在于 进程/线程创建和销毁的时间操作系统创建一个进程/线程显然需要时间在一个繁忙的服务器上如果每秒都有大量的连接建立和断开采用每个进程/线程处理一个客户连接的模式每个新连接都要创建创建一个进程/线程当连接断开时销毁对应的线程/进程。创建和销毁进程/线程的操作消耗了大量的CPU资源。使用链接池和线程池可以缓解这个问题。内存占用主要包含两方面一个是内核数据结构所占用的内存空间另外一个是Stack所占用的内存。有些应用的调用栈很深比如Java应用经常能看到几十上百层的调用栈。上下文切换的开销上下文切换时操作系统的调度器中断当前线程选择另外一个可运行的线程在CPU上继续运行。调度器需要保存当前线程的现场信息然后选择一个可运行的线程再将新线程的状态恢复到寄存器中。保存和恢复现场所需要的时间和CPU型号有关选择一个可运行的线程则完全是软件操作Linux 2.6才开始使用常量时间的调度算法。 以上是上下文切换的直接开销。除此之外还有一些间接开销比如上下文切换导致相关的缓存失效影响程序的性能但是此类的很多间接开销很难衡量。 有意思的是这种模式虽然性能极差但却依然是我们今天最常见到的模式很多Web程序都是这样的方式在运行。 select/poll 另外一种方式是使用select/poll在一个线程内处理多个客户连接。select和poll能够监控多个socket文件描述符当某个文件描述符就绪select/soll从阻塞状态返回通知应用程序可以处理用户连接了。使用这种方式我们只需要一个线程就可以处理大量的连接避免了多进程/线程的开销。之所以把select和poll放在一起说原因在于两者非常相似性能上基本没有区别唯一的区别在于poll突破了select 1024个文件描述符的限制然而当文件描述符数量增加时poll性能急剧下降因此所谓突破1024个文件描述符实际上毫无意义。select/poll并不完美依然存在很多问题 每次调用select/poll都要把文件描述符的集合从用户地址空间复制到内核地址空间select/poll返回后调用方必须遍历所有的文件描述符逐一判断文件描述符是否可读/可写。 这两个限制让select/poll完全失去了伸缩性。连接数越多文件描述符就越多文件描述符越多每次调用select/poll所带来的用户空间到内核空间的复制开销越大。最严重的是当报文达到select/poll返回之后必须遍历所有的文件描述符。假设现在有1万个连接其中只一个连接发送了请求但是select/poll就要把1万个连接全部检查一遍。 epoll epoll是如何提供一个高性能可伸缩的IO多路复用机制呢首先epoll引入了epoll instance这个概念epoll instance在内核中关联了一组要监听的文件描述符配置interest list这样的好处在于每次要增加一个要监听的文件描述符不需要把所有的文件描述符都配置一次然后从用户地址空间复制到内核地址空间只需要把单个文件描述符复制到内核地址空间复制开销从O(n)降到了O(1)。 注册完文件描述符后调用epoll_wait开始等待文件描述符事件。epoll_wait可以只返回已经ready的文件描述符因此在epoll_wait返回之后程序只需要处理真正需要处理的文件描述符而不用把所有的文件描述符全部遍历一遍。假设在全部N个文件描述符中只有一个文件描述符Readyselect/poll要执行N次循环epoll只需要一次。 epoll出现之后Linux上才真正有了一个可伸缩的IO多路复用机制。基于epoll能够支撑的网络连接数取决于硬件资源的配置而不再受限于内核的实现机制。CPU越强内存越大能支撑的连接数越多。 select、poll、epoll的区别 1、支持一个进程所能打开的最大连接数 select 单个进程所能打开的最大连接数有FD_SETSIZE宏定义其大小是32个整数的大小在32位的机器上大小就是32*32同理64位机器上FD_SETSIZE为32*64可以对进行修改然后重新编译内核但是性能可能会受到影响。 poll poll本质上和select没有区别但是它没有最大连接数的限制原因是它是基于链表来存储的 epoll 连接数有上限但是很大1G内存的机器上可以打开10万左右的连接2G内存的机器可以打开20万左右的连接
2、FD剧增后带来的IO效率问题 select 因为每次调用时都会对连接进行线性遍历所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 poll 同上 epoll 因为epoll内核中实现是根据每个fd上的callback函数来实现的只有活跃的socket才会主动调用callback所以在活跃socket较少的情况下使用epoll没有前面两者的线性下降的性能问题但是所有socket都很活跃的情况下可能会有性能问题。
3、消息传递方式 select 内核需要将消息传递到用户空间都需要内核拷贝动作 poll 同上 epoll epoll通过内核和用户空间共享一块内存来实现的。 什么是TCP粘包半包 假设场景使用程序用客户端发送100遍消息 假设客户端分别发送了两个数据包D1和D2给服务端由于服务端一次读取到的字节数是不确定的故可能存在以下4种情况。 1服务端分两次读取到了两个独立的数据包分别是D1和D2没有粘包和拆包 2服务端一次接收到了两个数据包D1和D2粘合在一起被称为TCP粘包 3服务端分两次读取到了两个数据包第一次读取到了完整的D1包和D2包的部分内容第二次读取到了D2包的剩余内容这被称为TCP拆包 4服务端分两次读取到了两个数据包第一次读取到了D1包的部分内容D1_1第二次读取到了D1包的剩余内容D1_2和D2包的整包。 如果此时服务端TCP接收滑窗非常小而数据包D1和D2比较大很有可能会发生第五种可能即服务端分多次才能将D1和D2包接收完全期间发生多次拆包。 TCP粘包/半包发生的原因 由于TCP协议本身的机制面向连接的可靠地协议-三次握手机制客户端与服务器会维持一个连接Channel数据在连接不断开的情况下可以持续不断地将多个数据包发往服务器但是如果发送的网络数据包太小那么他本身会启用Nagle算法可配置是否启用对较小的数据包进行合并基于此TCP的网络延迟要UDP的高些然后再发送超时或者包大小足够。那么这样的话服务器在接收到消息数据流的时候就无法区分哪些数据包是客户端自己分开发送的这样产生了粘包服务器在接收到数据库后放到缓冲区中如果消息没有被及时从缓存区取走下次在取数据的时候可能就会出现一次取出多个数据包的情况造成粘包现象 UDP本身作为无连接的不可靠的传输协议适合频繁发送较小的数据包他不会对数据包进行合并发送也就没有Nagle算法之说了他直接是一端发送什么数据直接就发出去了既然他不会对数据合并每一个数据包都是完整的数据UDP头IP头等等发一次数据封装一次也就没有粘包一说了。 分包产生的原因就简单的多可能是IP分片传输导致的也可能是传输过程中丢失部分包导致出现的半包还有可能就是一个包可能被分成了两次传输在取数据的时候先取到了一部分还可能与接收的缓冲区大小有关系总之就是一个数据包被分成了多次接收。 更具体的原因有三个分别如下。

  1. 应用程序写入数据的字节大小大于套接字发送缓冲区的大小
  2. 进行MSS大小的TCP分段。MSS是最大报文段长度的缩写。MSS是TCP报文段中的数据字段的最大长度。数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是TCP报文段的最大长度而是MSSTCP报文段长度-TCP首部长度
  3. 以太网的payload大于MTU进行IP分片。MTU指一种通信协议的某一层上面所能通过的最大数据包大小。如果IP层有一个数据包要传而且数据的长度比链路层的MTU大那么IP层就会进行分片把数据包分成托干片让每一片都不超过MTU。注意IP分片可以发生在原始发送端主机上也可以发生在中间路由器上。 解决粘包半包问题 由于底层的TCP无法理解上层的业务数据所以在底层是无法保证数据包不被拆分和重组的这个问题只能通过上层的应用协议栈设计来解决根据业界的主流协议的解决方案可以归纳如下。 1在包尾增加分割符比如回车换行符进行分割例如FTP协议linebase包和delimiter包下分别使用LineBasedFrameDecoder和DelimiterBasedFrameDecoder 2消息定长例如每个报文的大小为固定长度200字节如果不够空位补空格fixed包下使用FixedLengthFrameDecoder 3将消息分为消息头和消息体消息头中包含表示消息总长度或者消息体长度的字段通常设计思路为消息头的第一个字段使用int32来表示消息的总长度LengthFieldBasedFrameDecoder。