抖音粉丝购买网站,wordpress博客网址模板,wordpress 界面插件,电子商务网站设计要求部分内容来源#xff1a;JavaGuide select/poll/epoll 和 三种IO模型之间的关系是什么#xff1f;区分普通IO和IO多路复用普通IO#xff0c;即一个线程对应一个连接#xff0c;因为每个线程只处理一个客户端 socket#xff0c;目标明确#xff1a;线程中直接操作该 socke…部分内容来源JavaGuide
select/poll/epoll 和 三种IO模型之间的关系是什么区分普通IO和IO多路复用普通IO即一个线程对应一个连接因为每个线程只处理一个客户端 socket目标明确线程中直接操作该 socket 的 read() / write()无需关心其他连接也就是无需遍历文件描述符可以理解成一个线程只持有一个Socket的文件描述符而IO多路复用是一个线程持有多个Socket的文件描述符所以它有一个文件描述符集合内核要遍历这个文件描述集合检查每个Socket中是否有数据
区分select/poll/epoll和三种IO模型select/poll/epoll 是 如何遍历文件描述符寻找有数据的 Socket 的方法IO 模型是Socket 拿到数据后如何处理的策略IO多路复用是单线程如何高效管理和寻找多个Socket而IO模型是如何处理多个Socket的数据并且IO多路复用属于同步IOReactor是基于select/epoll这些IO多路复用机制实现的也就是基于同步IOProactor是基于异步IO机制实现的
IO多路复用的演进流程线程池实现资源复用如果要让服务器服务多个客户端那么最直接的方式就是为每一条连接创建线程其实创建进程也是可以的原理是一样的进程和线程的区别在于线程比较轻量级些线程的创建和线程间切换的成本要小些为了描述简述后面都以线程为例处理完业务逻辑后随着连接关闭后线程也同样要销毁了但是这样不停地创建和销毁线程不仅会带来性能开销也会造成浪费资源而且如果要连接几万条连接创建几万个线程去应对也是不现实的要这么解决这个问题呢我们可以使用「资源复用」的方式通过池化思想保存历史连接也就是不用再为每个连接创建线程而是创建一个「线程池」将连接分配给线程然后一个线程可以处理多个连接的业务。
线程怎样才能高效地处理多个连接的业务当一个连接对应一个线程时线程一般采用「read - 业务处理 - send」的处理流程如果当前连接没有数据可读那么线程会阻塞在 read 操作上socket 默认情况是阻塞 I/O不过这种阻塞方式并不影响其他线程。但是引入了线程池那么一个线程要处理多个连接的业务线程在处理某个连接的 read 操作时如果遇到没有数据可读就会发生阻塞那么线程就没办法继续处理其他连接的业务。要解决这一个问题最简单的方式就是将 socket 改成非阻塞然后线程不断地轮询调用 read 操作来判断是否有数据这种方式虽然该能够解决阻塞的问题但是解决的方式比较粗暴因为轮询是要消耗 CPU 的而且随着一个线程处理的连接越多轮询的效率就会越低为什么Socket要设置为非阻塞当socket设为非阻塞时即使没有数据可读read()也会立即返回一个【无数据的错误】如 Linux 的EAGAIN不会阻塞线程此时线程可以通过轮询处理多个连接线程循环遍历自己负责的所有socket描述符对每个socket调用read()如果read()返回数据就处理该连接的业务如果read()返回 “无数据”就跳过这个socket继续轮询下一个
监听连接上面的问题在于线程并不知道当前连接是否有数据可读从而需要每次通过 read 去试探那有没有办法在只有当连接上有数据的时候线程才去发起读请求呢答案是有的实现这一技术的就是 I/O 多路复用I/O 多路复用技术会用一个系统调用函数来监听我们所有关心的连接也就说可以在一个监控线程里面监控很多的连接
三种IO模型小区别IO多路复用属于网络监听模型,例如Redis实现网络监听的时候就是使用IO多路复用的当Redis启动的时候它会主动使用IO多路复用网络监听模型去监听每个 Socket 在内核中都有对应的接收缓冲区和发送缓冲区read从该 Socket 对应的接收缓冲区中读取数据到用户缓冲区
Reactor 是非阻塞同步网络模式而 Proactor 是异步网络模式这里先给大家复习下阻塞、非阻塞、同步、异步 I/O 的概念阻塞I/O先来看看阻塞 I/O当用户程序执行 read线程会被阻塞一直等到内核数据准备好并把数据从内核缓冲区拷贝到应用程序的缓冲区中当拷贝过程完成read 才会返回注意阻塞等待的是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程过程如下图
非阻塞 I/O知道了阻塞 I/O来看看非阻塞 I/O非阻塞的 read 请求在数据未准备好的情况下立即返回可以继续下执行此时应用程序不断轮询内核直到数据准备好内核将数据拷贝到应用程序缓冲区read 才可以获取到结果过程如下图注意这里最后一次 read 调用获取数据的过程是一个同步的过程是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程
异步IO如果 socket 设置了 O_NONBLOCK 标志那么就表示使用的是非阻塞 I/O 的方式访问而不做任何设置的话默认是阻塞 I/O因此无论 read 和 send 是阻塞 I/O还是非阻塞 I/O 都是同步调用因为在 read 调用时内核将数据从内核空间拷贝到用户空间的过程都是需要等待的也就是说这个过程是同步的如果内核实现的拷贝效率不高read 调用就会在这个同步过程中等待比较长的时间为什么这么说同步 的核心是用户线程必须亲自参与数据拷贝的等待过程并且用户 线程必须等待拷贝完成才能继续执行同步IO要等待的两个步骤「内核数据准备好」「数据从内核空间拷贝到用户空间」阻塞IO等待。你到咖啡店后发现咖啡还没做好于是站在柜台前一直等非阻塞IO轮询。你到咖啡店后如果没好那你就去附近晃悠一会儿然后再问咖啡时候做好了直到店员说好了
真正的异步 I/O 是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程都不用等待当我们发起 aio_read异步 I/O之后就立即返回内核自动将数据从内核空间拷贝到用户空间这个拷贝过程同样是异步的内核自动完成的和前面的同步操作不一样应用程序并不需要主动发起拷贝动作过程如下图
理解IO的简单例子举个你去饭堂吃饭的例子你好比应用程序饭堂好比操作系统
阻塞 I/O你去饭堂吃饭但是饭堂的菜还没做好然后你就一直在那里等啊等等了好长一段时间终于等到饭堂阿姨把菜端了出来数据准备的过程但是你还得继续等阿姨把菜内核空间打到你的饭盒里用户空间经历完这两个过程你才可以离开。
非阻塞 I/O你去了饭堂问阿姨菜做好了没有阿姨告诉你没你就离开了过几十分钟你又来饭堂问阿姨阿姨说做好了于是阿姨帮你把菜打到你的饭盒里这个过程你是得等待的
异步 I/O你让饭堂阿姨将菜做好并把菜打到饭盒里后把饭盒送到你面前整个过程你都不需要任何等待
很明显异步 I/O 比同步 I/O 性能更好因为异步 I/O 在「内核数据准备好」和「数据从内核空间拷贝到用户空间」这两个过程都不用等待Proactor 正是采用了异步 I/O 技术所以被称为异步网络模型
简单总结-IO多路复用的演进流程面试引导线程创建销毁的开销-池化思想-selectpoll存在的问题-轮询机制对比事件驱动机制-epoll基于红黑树和哈希表而不是遍历文件描述符-引出IO多路复用的两种实现即Reactor和Proactor
池化思想如果让一个服务器能够服务更多的服务端最直接的方式就是给每一条连接创建一个线程但是线程的创建和销毁是有一定的开销的会消耗CPU的时间片轮转的资源所以为了达到资源复用就出现了【池化思想】也就是我们的线程池
不要弄混执行任务的线程池和处理IO的线程池这两个东西只是分别处理的东西不同线程的职责可以根据任务类型划分有的线程专注于处理连接的 I/O 操作有的线程专注于执行业务逻辑任务
IO线程如何高效处理多个连接的业务一个连接对应一个线程时线程的处理流程「read - 业务处理 - send」线程池的目的是让一个线程照顾多个连接但一线程对应多连接时线程在处理某个连接的 read 操作时如果没有数据可读则会阻塞导致线程无法执行其它的IO任务简单解决方式将Socket换成非阻塞之后线程不断地轮询调用 read 操作来判断是否有数据但轮询是要消耗 CPU 的随着一个线程处理的连接越多轮询的效率就会越低当socket设为非阻塞时即使没有数据可读read()也会立即返回一个【无数据的错误】不会阻塞线程
监听连接问题所在线程并不知道当前连接是否有数据可读因此需要每次通过 read 去试探轮询我们可以引入一种事件驱动机制等连接来了我们再去执行这就是epoll
三种IO模型-快速复习小区别防止概念混淆IO多路复用属于网络监听模型,例如Redis实现网络监听的时候就是使用IO多路复用的当Redis启动的时候它会主动使用IO多路复用网络监听模型去监听每个 Socket 在内核中都有对应的接收缓冲区和发送缓冲区read从该 Socket 对应的接收缓冲区中读取数据到用户缓冲区
理解三种IO模型read 和 send 是阻塞 I/O还是非阻塞 I/O 都是同步调用因为在 read 调用时内核将数据从内核空间拷贝到用户空间的过程都是需要等待的也就是说这个过程是同步的如果内核实现的拷贝效率不高read 调用就会在这个同步过程中等待比较长的时间为什么这么说同步 的核心是用户线程必须亲自参与数据拷贝的等待过程并且用户 线程必须等待拷贝完成才能继续执行同步IO要等待的两个步骤「内核数据准备好」「数据从内核空间拷贝到用户空间」阻塞IO等待。你到咖啡店后发现咖啡还没做好于是站在柜台前一直等非阻塞IO轮询。你到咖啡店后如果没好那你就去附近晃悠一会儿然后再问咖啡时候做好了直到店员说好了
真正的异步 I/O 是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程都不用等待当我们发起 aio_read异步 I/O之后就立即返回内核自动将数据从内核空间拷贝到用户空间这个拷贝过程同样是异步的内核自动完成的和前面的同步操作不一样应用程序并不需要主动发起拷贝动作