- 一、进程和线程的区别?
- 二、协程与线程的区别?
- 三、并发和并行有什么区别?
- 四、进程和线程的切换流程?
- 五、为什么虚拟地址空间切换会比较耗时?
- 六、进程间通信方式有哪些?
- 七、进程间同步的方式有哪些?
- 八、线程同步的方式有哪些?
- 九、线程的分类?
- 十、什么是临界区,如何解决冲突?
- 十一、什么是死锁?死锁的产生条件?
- 十二、进程调度策略有哪几种?
- 十三、进程有哪些状态?
- 十四、什么是分页?
- 十五、什么是分段?
- 十六、分页和分段有什么区别?
- 十七、什么事交换空间?
- 十八、物理地址、逻辑地址、有效地址、线性地址、虚拟地址的区别?
- 十九、页面替换算法有哪些?
- 二十、什么事缓冲区溢出?有什么危害?
- 二十一、什么是虚拟内存?
- 二十二、虚拟内存的实现方式有哪些?
- 二十三、讲一讲IO多路复用?
- 二十四、硬连接和软连接有什么区别?
- 二十五、中断的处理过程?
- 二十六、中断和轮询有什么区别?
- 二十七、什么事用户态和内核态?
- 二十八、用户态和内核态是如何切换的?
- 二十九、Unix常见的IO模型:
- 三十、select、poll、epoll的区别?
一、进程和线程的区别?
- 调度:线程是系统调度的最小单位,进程是资源分配的最小单位。
- 切换:线程上下文切换比进程上下文切换快得多。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有资源,但是可以访问隶属于进程的资源。
系统开销:创建和销毁进程时,系统都要为之分配或回收系统资源,如内存空间、I/O设备等,OS所付出的开销显著大于在创建或者销毁线程时的开销,进程切换的开销也远大于线程切换的开销。
二、协程与线程的区别?
线程和进程都是同步机制,而协程是异步机制。
- 线程是抢占式,而协程是非抢占式,需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
- 一个线程可以有多个协程。
- 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的CPU资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。
- 协程能保留上一次调用时的状态。
三、并发和并行有什么区别?
- 多核才能并行。同一时刻多个线程在执行。
- 单核只能并发。同一时刻只有一个线程在执行。不过在时间线上多个线程交替执行。
四、进程和线程的切换流程?
- 进程切换分两步:
- 切换页表以使用新的地址空间,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
- 切换内核栈和硬件上下文。
- 对linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第一步是不需要做的,第二步是进程和线程切换都要做的。
因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一进程中的线程进行切换不涉及虚拟地址空间的转换。
五、为什么虚拟地址空间切换会比较耗时?
进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用Cache来缓存常用的地址映射,这样可以加速页表查找,这个Cache就是TLB(Translation Lookaside Buffer,TLB本质上就是一个Cache是用来加速页表查找的)。
由于每个进程都有自己的虚拟空间,那么显然每个进程都有自己的页表,那么进程切换后页表也要进行切换,页表切换后TLB就失效了,Cache失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程无需切换地址空间,因此我们通常说线程切换要比进程切换快,原因就在这里。
六、进程间通信方式有哪些?
管道:管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 管道可以分为两类:匿名管道和命名管道。匿名管道是单向的,只能在有亲缘关系的进程间通信;命名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
- 信号:信号是一种比较复杂的通信方式,信号可以在任何时候发给某一进程,而无需知道该进程的状态。
- 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 消息队列:消息队列是消息的链接表,包括Posix消息队列和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享内存:共享内存就是映射一段能被其他进程访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC(进程间通信)方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- Socket:与其他通信机制不同的是,它可用于不同机器间的进程通信。
优缺点:
临界区:通过对多线程 的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
- 优点:保证在某一时刻只有一个线程能访问数据的简便办法。
- 缺点:虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
- 互斥量:为协调共同对一个共享资源的单独访问而设计的,互斥量根临界区很相似,比临界区复杂,互斥对象只有一个,只有拥有互斥对象的线程才具有访问资源的权限。
- 优点:使用互斥不仅仅能够在一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
- 缺点:
- 互斥量是可以命名的,也就是说它可以跨越进程使用,所以创建互斥量需要的资源更多,所以如果只是为了在进程内部使用的话使用临界区会带来速度上的优势并能够减少资源占用量。
- 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根据用户购买的访问许可数量来决定有多少线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号量对象可以说是一种资源计数器。
- 信号量:为控制一个具有有限数量用户资源而设计。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。互斥量是信号量的一种特殊情况,当信号量的最大资源数=1就是互斥量了。
- 优点:适用于对Socket(套接字)程序中线程的同步。
- 缺点:
- 信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点;
- 信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难,加重了程序员的编码负担;
- 核心操作P-V分散在各用户程序的代码中,不易控制和管理,一旦错误,后果严重,且不易发现和纠正。
事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
优点:事件对象通知操作的方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。
八、线程同步的方式有哪些?
临界区:当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区位置,以此达到用原子方式操作共享资源的目的。
- 事件:事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。
- 互斥量:互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。
- 信号量:当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。
区别:
从线程的运行空间来说,分为用户级线程和内核级线程。
- 内核级线程:这类线程依赖于内核,又称为内核支持的线程或轻量级进程。无论是在用户程序中的线程还是系统进程中的线程,他们的创建、撤销和切换都由内核实现。比如intel i5-8250U是4核8线程,这里的线程就是内核级线程。
用户级线程:它仅存在于用户级中,这种线程是不依赖于操作系统核心的。应用进程利用线程库来完成其创建和管理,速度比较快,操作系统内核无法感知用户级线程的存在。
十、什么是临界区,如何解决冲突?
每个进程中访问临界资源的那段程序称为临界区,一次仅允许一个进程使用的资源称为临界资源。
解决冲突的办法:
死锁是在两个或多个并发进程中,如果每个进程持有某资源而等待其他进程释放它或它们现在保持着的资源,在未改变这种状态之间都不能向前推进,称这一组进程产生了死锁,通俗的讲就是两个或多个进程无限期的阻塞,互相等待的一种状态。
- 死锁产生的四个必要条件:
- 互斥条件:一个资源一次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系。
处理死锁的方法有:死锁预防、死锁避免、死锁检测、死锁解除、鸵鸟策略。
- 死锁的预防:基本思想就是确保死锁发生的四个必要条件中至少有一个不成立:
- 死锁避免:死锁预防通过约束资源请求,防止4个必要条件中至少一个的发生,可以通过直接或间接预防方法,但是都会导致低效的资源使用和低效的进程执行。而死锁避免则允许前三个必要提哦啊间,但是通过动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态。所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态时安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态,银行家算法时经典的死锁避免算法。
- 死锁检测:死锁预防策略时非常保守的,它们通过限制访问资源和进程上强加约束来解决死锁的问题。死锁检测则是完全相反,它不限制资源访问或约束进程行为,只要有可能,被请求的资源就被授权给进程。但是操作系统会周期性地执行一个算法检测前面的循环等待的条件。死锁检测算法是通过资源分配图来检测是否存在环来实现,从一个节点出发进行深入有限搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有存在环,也就是检测到死锁的发生。
- 死锁的解除:死锁的解除的常用方法就是终止进程和资源抢占,回滚。所谓进程终止就是简单地终止一个或多个进程以打破循环等待,包括两种方式:终止所有死锁进程和一次只终止一个进程知道取消死锁循环为止;所谓资源抢占就是从一个或者多个死锁进程那里抢占一个或多个资源。
- 鸵鸟策略:把头埋进沙子里,假装根本没发生问题。因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任何措施的方案会获得更高的性能。当发生死锁时不会对用户造成多大影响,或者发生死锁的概率很低,可以采取鸵鸟策略。大多数操作系统,包括Unix,Linux和Windows,处理死锁问题的办法仅仅是忽略它。
十二、进程调度策略有哪几种?
先来先服务:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但是不利于段作业,因为段作业必须一直等待前面的长作业执行完毕才能执行,而长作业有需要执行很长时间,造成了短作业必须一直等待前面的组的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过程。另外,对I/O密集型进程也比例,因为这种进程每次进行I/O操之后又得重新排队。
- 短作业有限:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
- 最短剩余时间有限:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
- 时间片轮转:将所有就绪的进行按FCFS的原则排成一个队列,每次调度时,把CPU时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟终端,调度程序便停止该进程的执行,并将它送完就绪队列的末尾,同时继续吧CPU时间分配给队首的进程。
- 时间片轮转算法的效率和时间片的大小由很大关系:因为进程切换都要保存进程的信息并且载入新的进程信息,如果时间片太小,会导致进程切换频繁,在进程切换上耗费大量时间和资源。而如果时间片过长,那么实时性就不能得到保证。
优先级调度:为每个进程分配一个优先级,按优先级尽性调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
十三、进程有哪些状态?
进程一共有5种状态,分别是创建、就绪、运行、阻塞、终止。
- 运行状态就是进程正在CPU上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
- 就绪状态就是说进程已处于准备运行的状态,即进程获得了除CPU之外的一切所需资源,一旦得到CPU即可运行。
- 阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待I/O完成。即CPU空闲, 该进程也不能运行。
- 运行态→阻塞态:往往是由于等待外设,等待住存等分配或等待人工干预而引起的。
- 阻塞态→就绪态:则是等待的条件已满足,只需分配到处理器后就能运行。
- 运行态→就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。
就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。
十四、什么是分页?
把内存空间划分为大小相等且固定的块,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。
访问分页系统中内存数据需要两次的内存访问:
分页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。
分段内存管理当中,地址是二维的,一维是段号,二维是段内地址;其中每个段的长度是不一样的,而且每个段内部都是从0开始编址的。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此页存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。
十六、分页和分段有什么区别?
分页对程序员是透明的,但是分段需要程序员显式划分每个段。
- 分页的地址空间是一维地址空间,分段是二维的。
- 页的大小不可变,段的大小可以动态改变。
分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
十七、什么事交换空间?
操作系统把物理内存(RAM)分成一块一块的小内存,每一块内存被称为页(page)。当内存资源不足时,LInux把某些页的内容转移至硬盘上的一块空间上,以释放内存空间。硬盘上的那块空间叫做交换空间,而这一过程被称为交换(swapping)。物理内存和交换空间的总容量就是虚拟内存的可用容量。
用途:
物理地址就是内存中真正的地址,它就相当于你家经纬度坐标,具有唯一性。不管哪种地址,最终都会映射为物理地址。
- 在实模式下,段基址+段内偏移被称为线性地址,不过此时的段基址不能称为真正的地址,而是会被称作为一个选择子的东西,选择子就是个索引,相当于数组的下表,通过这个索引能够在GDT中找到相应的段描述符,段描述符记录了段的起始、段的大小等信息,这样便得到了基地址。如果此时没有开启内存饿功能,那么这个线性地址可以直接当做物理地址来使用,直接访问内存。如果开启了分页功能,那么这个线性地址又多了一个字,这个名字就是虚拟地址。
- 不论在实模式还是保护模式下,段内偏移地址都叫做有效地址。有效地址也是逻辑地址。
线性地址可以看作是虚拟地址,虚拟地址不是真正的物理地址,但是虚拟地址会最终被映射为物理地址。下面是虚拟地址→物理地址的映射。
十九、页面替换算法有哪些?
在程序运行 过程中,如果要访问的页面不在内存中,就发生缺页终端从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
- 最优算法在当前页面中置换最后要访问的页面。不幸的是,没有办法来判定哪个页面是最后一个要访问的,因此实际上该算法不能使用。然而,它可以作为衡量其他算法的标准。
- NRU算法根据R位和M位的状态将页面分为四类。从编号最小的类别中随机选择一个页面。NRU算法易于实现,但是性能不是很好。存在更好的算法。
- FIFO会跟踪页面加载进入内存中的顺序,并把页面放入一个链表中。有可能删除存在时间最长但是还在使用的页面,因此这个算法也不是一个很好的选择。
- 第二次机会算法是对FIFO的一个修改,它会在删除页面之前检查这个页面是否仍在使用。如果页面正在使用,就会进行保留。这个改进大大提高了性能。
- 时钟算法是第二次机会算法的另一种实现形式,时钟算法和第二次机会算法的性能差不多,但是会话费更少的时间来执行算法。
- LRU算法是一个非常优秀的算法,但是没有特殊的硬件(TLB)很难实现。如果没有硬件,就不能使用LRU算法。
- NFU算法是一种近似LRU的算法,它的性能不是非常好。
- 老化算法是一种更接近LRU算法的实现,并且可以更好的实现,因此是一个很好的选择。
- 最后两种算法都使用了工作集算法。工作集算法提供了合理的性能开销,但是它的实现比较复杂。WSClock是另外一种变体,它不仅能够提供良好的性能,而且可以高效地实现。
最好的算法是老化算法和WSClock算法。他们分别是基于LRU和工作集算法。他们都具有良好的性能并且能够被有效的实现。还存在其他一些好的算法,但实际上这两个可能是最重要的。
二十、什么事缓冲区溢出?有什么危害?
缓冲区溢出事指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
- 危害有以下两点:
- 程序崩溃,导致拒绝服务
- 跳转并执行一段恶意代码
-
二十一、什么是虚拟内存?
虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存。
二十二、虚拟内存的实现方式有哪些?
虚拟内存中,允许将一个作业分多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或永久的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实需要建立在离散分配的内存管理方式的基础上。虚拟内存的实现有以下三种方式:
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
硬连接就是在目录下创建一个条目,记录着文件名与inode编号,这个inode就是源文件的inode。删除人意一个条目,文件还是存在,只要引用数量不为0.但是硬连接有限制,它不能跨越文件系统,也不能对目录进行链接。
符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为Windows的快捷方式。当源文件被删除了,链接文件就打不开了。因为记录的是路径,所以可以为目录建立符号链接。
二十五、中断的处理过程?
保护现场:将当前执行程序的相关数据保存在寄存器中,然后入栈。
- 开中断:以便执行中断时能响应较高级别的中断请求。
- 中断处理
- 关中断:保证恢复线程时不被新中断打扰。
-
二十六、中断和轮询有什么区别?
轮询:CPU对特定设备轮流询问。中断:通过特定事件提醒CPU。
轮询:效率低等待时间长,CPU利用率不高。中断:容易遗漏问题,CPU利用率不高。
二十七、什么事用户态和内核态?
用户态和内核态事操作系统的两种运行状态:
- 内核态:内核态运行的程序可以访问计算机的任何数据和资源,不受限制,包括外围设备,比如网卡、硬盘等。处于内核态的CPU可以从一个程序切换到另一个程序,并且占用CPU不会发生抢占情况。
- 用户态:用户态运行的程序只能受限地访问内存,只能直接读取用户程序的数据,并且不允许访问外围设备,用户态下的CPU不允许独占,也就是说CPU能够被其他程序获取。
将操作系统的运行状态分为用户态和内核态,主要是为了对访问能力进行限制,防止随意进行一些比较危险的操作导致系统的崩溃,比如设置时钟、内存清理,这些都需要在内核态下完成。
二十八、用户态和内核态是如何切换的?
所有的用户进程都是运行在用户态的,但是我们上面也说了,用户程序的访问能力有限,一些比较重要的比如从硬盘读取数据,从键盘获取数据的操作则是内核态才能做的事情,而这些数据却又对用户程序来说非常重要。所以就涉及到两种模式下的转换,即用户态→内核态→用户态,而唯一能够做这些操作的只有系统调用,而能够执行系统调用的就只有操作系统。
一般用户态→内核态的转换我们都称之为trap进内核,也被称之为陷阱指令(trap instruction)。他们的工作流程如下:
- 受限用户程序会调用glibc库,glibc是一个标准库,同时也是一套核心库,库中定义了很多关键API。
- glibc库知道针对不同体系结构调用系统调用的正确方法,它会根据体系结构应用程序的二进制接口设置用户进程传递的参数,来准备系统调用。
- 然后,glibc库调用软件中断指令(SWI),这个指令通过更新CPSR寄存器将模式改为超级用户模式,然后跳转到地址0x08处。
- 到目前为止,整个过程仍处于用户态下,在执行SWI指令后,允许进程执行内核代码,MMU限制允许内核虚拟内存访问。
- 从地址0X08开始,进程执行加载并跳转到中断处理程序,这个程序就是ARM中的vector_swi()。
- 在vector_swi()处,从SWI指令中提取系统调用号SCNO,然后使用SCNO作为系统调用表sys_call_table的索引,调转到系统调用函数。
- 执行系统调用完成后,将还原用户模式寄存器,然后再以用户模式执行。
二十九、Unix常见的IO模型:
对于一次IO访问(以Read举例),数据会先拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以锁,当一个read操作发生时,它会经历两个阶段:
- 等待数据准备就是(Waiting for the data to be ready)
- 将数据从内核拷贝到进程中(Copying the data from kernel to the process)
- 正因为这两个阶段,linux系统产生了下面五种网络模式的方案:
- 阻塞式IO模型(blocking IO model)
- 非阻塞式IO模型(noblocking IO model)
- IO复用式IO模型(IO multiplexing model)
- 信号驱动式IO模型(signal-driven IO model)
- 异步IO式IO模型(asynchronous IO model)
- 对于这几种IO模型的详细说明,可以参考这篇文章:https://juejin.cn/post/6942686874301857800#heading-13
其中,IO多路复用模型指的是:使用单个进程同时处理多个网络连接IO,他的原理就是select、poll、epoll不断轮询所负责的所有socket,当某个socket 有数据到达了,就通知用户进程。该模型的优势并不是对于单个链接能处理得更快,而是在于能处理更多的连接。
三十、select、poll、epoll的区别?
select:时间复杂度O(n)
- select仅仅知道有I/O事件发生,但并不知道是哪几个流,所以只能无差别轮询所有流,找出能读出数据或者写入数据的流,并对其进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
- poll:时间复杂度O(n)
- poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,但是它没有最大连接数的限制,原因是它基于链表来存储的。
- epoll:时间复杂度O(1)
- epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以说epoll实际上是事件驱动(每个事件关联上fd)的。
- select、poll、epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),就通知程序进行相应的读写操作,但select、poll、epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是 的,而异步I/O则无需自己负责进行读写,异步I/O的实现会把俄把数据从内核拷贝到用户空间。