做网站推广的销售怎么打电话,呼和浩特做网站的,重庆做网站建设哪里好,沧州网站建设公司排名参考面试官#xff1a;简单说一下阻塞IO、非阻塞IO、IO复用的区别 #xff1f;_unix环境编程 阻塞io和非阻塞io-CSDN博客
同步阻塞(BIO)
BIO 以流的方式处理数据
应用程序发起一个系统调用#xff08;recvform#xff09;#xff0c;这个时候应用程序会一直阻塞下去简单说一下阻塞IO、非阻塞IO、IO复用的区别 _unix环境编程 阻塞io和非阻塞io-CSDN博客
同步阻塞(BIO)
BIO 以流的方式处理数据
应用程序发起一个系统调用recvform这个时候应用程序会一直阻塞下去直到内核把数据准备好并将其从内核复制到用户空间复制完成后返回成功提示这个时候应用程序才会继续处理数据。
服务实现模式为一个连接对应一个线程即客户端发送一个连接服务端要有一个线程来处理。 缺点 一旦有高并发的大量请求,就会有如下问题 1线程不够用就算使用了线程池复用线程也无济于事(一台机器需要维护 1 万个连接相当于要维护 1 万个进程/线程操作系统就算死扛也是扛不住的)2阻塞I/O模式下会有大量的线程被阻塞一直在等待数据,这个时候的线程被挂起只能干等CPU利用率很低换句话说系统的吞吐量差3如果网络I/O堵塞或者有网络抖动或者网络故障等线程的阻塞时间可能很长整个系统也变的不可靠4服务器线程太多压力太大导致服务器宕机。
同步非阻塞(NIO)
NIO 以缓冲区的方式处理数据,缓冲区 I/O 的效率比流 I/O 高很多
应用进程需要不断询问内核数据是否就绪在内核数据还未就绪时应用进程还可以做其他事情。
服务实现模式是一个线程可以处理多个连接即客户端发送的连接都会注册到多路复用器上然后进行轮询连接有I/O请求就处理。 优点:模型简单实现难度低;与阻塞IO模型对比它在等待数据报的过程中进程并没有阻塞它可以做其他的事情。 缺点:轮询发送 recvform消耗CPU 资源。
I/O多路复用
在没有使用IO多路复用机制时有BIO、NIO两种实现方式但是会出现阻塞或者开销大的问题
参考这次答应我一举拿下 I/O 多路复用 (qq.com)
非阻塞IO模型需要进程不断地轮询发起recvform系统调用就会有很多的线程不断调用recvfrom 请求数据先不说服务器能不能扛得住这么多线程就算扛得住那么很明显这种方式是不是太浪费资源了线程是我们操作系统的宝贵资源大量的线程用来去读取数据了那么就意味着能做其它事情的线程就会少。
select/poll/epoll 这是三个多路复用接口都能实现 C10K 吗接下来我们分别说说它们。 select/poll
select 实现多路复用的方式是将已连接的 Socket 都放到一个文件描述符集合然后调用 select 函数将文件描述符集合拷贝到内核里让内核来检查是否有网络事件产生检查的方式很粗暴就是通过遍历文件描述符集合的方式当检查到有事件产生后将此 Socket 标记为可读或可写 接着再把整个文件描述符集合拷贝回用户态里然后用户态还需要再通过遍历的方法找到可读或可写的 Socket然后再对其处理。
所以对于 select 这种方式需要进行 2 次「遍历」文件描述符集合一次是在内核态里一个次是在用户态里 而且还会发生 2 次「拷贝」文件描述符集合先从用户空间传入内核空间由内核修改后再传出到用户空间中。
select 使用固定长度的 BitsMap表示文件描述符集合而且所支持的文件描述符的个数是有限制的在 Linux 系统中由内核中的 FD_SETSIZE 限制 默认最大值为 1024只能监听 0~1023 的文件描述符。
poll 不再用 BitsMap 来存储所关注的文件描述符取而代之用动态数组以链表形式来组织突破了 select 的文件描述符个数限制当然还会受到系统文件描述符限制。
但是 poll 和 select 并没有太大的本质区别都是使用「线性结构」存储进程关注的 Socket 集合因此都需要遍历文件描述符集合来找到可读或可写的 Socket时间复杂度为 O(n)而且也需要在用户态与内核态之间拷贝文件描述符集合这种方式随着并发数上来性能的损耗会呈指数级增长。
epoll
epoll 通过两个方面很好解决了 select/poll 的问题。
第一点epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字把需要监控的 socket 通过epoll_ctl()函数加入内核中的红黑树里红黑树是个高效的数据结构增删查一般时间复杂度是O(logn)通过对这棵黑红树进行操作这样就不需要像 select/poll 每次操作时都传入整个 socket 集合只需要传入一个待检测的 socket减少了内核和用户空间大量的数据拷贝和内存分配。
第二点 epoll 使用事件驱动的机制内核里维护了一个链表来记录就绪事件当某个 socket 有事件发生时通过回调函数内核会将其加入到这个就绪事件列表中当用户调用epoll_wait()函数时只会返回有事件发生的文件描述符集合不需要像 select/poll 那样轮询扫描整个 socket 集合大大提高了检测的效率。
从下图你可以看到 epoll 相关的接口作用 epoll 的方式即使监听的 Socket 数量越多的时候效率不会大幅度降低能够同时监听的 Socket 的数目也非常的多了上限就为系统定义的进程打开的最大文件描述符个数。因而epoll 被称为解决 C10K 问题的利器。
epoll 支持两种事件触发模式分别是边缘触发edge-triggeredET和水平触发level-triggeredLT。
这两个术语还挺抽象的其实它们的区别还是很好理解的。 使用边缘触发模式时当被监控的 Socket 描述符上有可读事件发生时服务器端只会从 epoll_wait 中苏醒一次即使进程没有调用 read 函数从内核读取数据也依然只苏醒一次因此我们程序要保证一次性将内核缓冲区的数据读取完 使用水平触发模式时当被监控的 Socket 上有可读事件发生时服务器端不断地从 epoll_wait 中苏醒直到内核缓冲区数据被 read 函数读完才结束目的是告诉我们有数据需要读取
举个例子你的快递被放到了一个快递箱里如果快递箱只会通过短信通知你一次即使你一直没有去取它也不会再发送第二条短信提醒你这个方式就是边缘触发如果快递箱发现你的快递没有被取出它就会不停地发短信通知你直到你取出了快递它才消停这个就是水平触发的方式。
这就是两者的区别水平触发的意思是只要满足事件的条件比如内核中有数据需要读就一直不断地把这个事件传递给用户而边缘触发的意思是只有第一次满足条件的时候才触发之后就不会再传递同样的事件了。
如果使用水平触发模式当内核通知文件描述符可读写时接下来还可以继续去检测它的状态看它是否依然可读或可写。所以在收到通知后没必要一次执行尽可能多的读写操作。
如果使用边缘触发模式I/O 事件发生时只会通知一次而且我们不知道到底能读写多少数据所以在收到通知后应尽可能地读写数据以免错失读写的机会。因此我们会循环从文件描述符读写数据那么如果文件描述符是阻塞的没有数据可读写时进程会阻塞在读写函数那里程序就没办法继续往下执行。所以边缘触发模式一般和非阻塞 I/O 搭配使用程序会一直执行 I/O 操作直到系统调用如 read 和 write返回错误错误类型为 EAGAIN 或 EWOULDBLOCK。
一般来说边缘触发的效率比水平触发的效率要高因为边缘触发可以减少 epoll_wait 的系统调用次数系统调用也是有一定的开销的的毕竟也存在上下文的切换。
select/poll 只有水平触发模式epoll 默认的触发模式是水平触发但是可以根据应用场景设置为边缘触发模式。