Day10

进程和线程的区别

**进程(Process)和线程(Thread)**是操作系统中进行任务调度和资源管理的两个基本概念。理解它们的区别对于高效编程和系统设计非常重要。

  1. 定义

    进程:是操作系统分配资源的基本单位,每个进程都有独立的内存空间和系统资源。
    线程:是CPU调度的基本单位,一个进程可以包含多个线程,共享进程的资源。

  2. 内存和资源

    进程:拥有独立的地址空间,进程之间的数据互不影响。
    线程:共享进程的地址空间和资源,可以直接访问进程的数据。

  3. 通信方式

    进程间通信(IPC):需要借助管道、消息队列、共享内存等机制,复杂度较高。
    线程间通信:由于共享内存,通信更为高效,但需要注意同步和互斥。

  4. 创建和销毁

    进程:创建和销毁的开销较大,涉及到操作系统资源的分配和回收。
    线程:创建和销毁的开销较小,更为轻量级。

  5. 调度和切换

    进程切换:需要保存和恢复整个进程的上下文,开销较高。
    线程切换:只需保存和恢复线程的上下文,开销较低。

  6. 可靠性

    进程:一个进程的崩溃不会影响其他进程,可靠性更高。
    线程:一个线程的异常可能导致整个进程崩溃,需要更严格的同步机制。

总结
进程和线程各有优劣,进程适用于需要高隔离和安全性的场景,而线程适合需要高性能和高并发的场景。选择使用进程还是线程应根据具体需求和系统架构进行权衡。

并行和并发的区别

并行:多个任务同时执行,通常需要多核支持,适合需要加速的计算任务。
并发:多个任务交替执行,不一定需要多核,适合提高系统响应速度和任务切换效率的场景。

用户态和核心态

用户态(User Mode):指普通应用程序运行的模式。在用户态下,程序只能访问受限的资源,不能直接操作硬件或管理关键系统资源(如内存、CPU等)。用户态程序如果需要访问硬件或系统资源,需要通过系统调用(System Call)向操作系统内核请求。
核心态(Kernel Mode):指操作系统内核运行的模式。在核心态下,操作系统内核可以完全控制硬件设备和管理系统资源,如内存分配、进程调度等。核心态具有最高权限,可以直接与硬件交互,处理所有的低级操作。

用户态(User Mode) : 用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限。当应用程序需要执行某些需要特殊权限的操作,例如读写磁盘、网络通信等,就需要向操作系统发起系统调用请求,进入内核态。
内核态(Kernel Mode):内核态运行的进程几乎可以访问计算机的任何资源包括系统的内存空间、设备、驱动程序等,不受限制,拥有非常高的权限。当操作系统接收到进程的系统调用请求时,就会从用户态切换到内核态,执行相应的系统调用,并将结果返回给进程,最后再从内核态切换回用户态。

Day11

进程调度算法

调度算法影响的等待时间(进程在就绪队列中等待时间的总和),而不能影响进程真在使用CPU的时间和I/O时间。

先到先服务调度算法(FCFS,First Come, First Served) : 从就绪队列中选择一个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
短作业优先的调度算法(SJF,Shortest Job First) : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
时间片轮转调度算法(RR,Round-Robin) : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
多级反馈队列调度算法(MFQ,Multi-level Feedback Queue):前面介绍的几种进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成,因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。
优先级调度算法(Priority):为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。

进程调度算法主要分为非抢占式和抢占式两类

  1. 先来先服务(FCFS, First-Come, First-Served)

    类型:非抢占式
    特点:按照进程到达的顺序进行调度,最先到达的进程最先获得CPU执行。
    优点:实现简单,适用于少量任务。
    缺点:可能导致**“饥饿”问题**,即短作业在长作业之后到达时,可能会等待很长时间,平均等待时间可能较大。
    适用场景:适用于批处理系统,任务顺序执行不重要的场合。

  2. 最短作业优先(SJF, Shortest Job First)

    类型:非抢占式
    特点:每次选择估计运行时间最短的进程来执行,以减少平均等待时间。
    优点:可以提供较短的平均等待时间,是最优的非抢占式调度算法之一。
    缺点:难以准确估计每个进程的运行时间,容易导致**“长作业饥饿”**,即长作业可能一直得不到执行。
    适用场景:适用于作业长度已知的环境,如批处理作业。

  3. 最短剩余时间优先(SRTF, Shortest Remaining Time First)

    类型:抢占式
    特点:是SJF的抢占式版本。如果一个新进程的剩余时间比当前进程少,则抢占当前进程,执行剩余时间最短的进程。
    优点:可以更好地响应短作业,提高系统的整体效率。
    缺点:同样存在估计困难,并可能导致长作业饥饿问题。
    适用场景:适合时间片较短、需要及时响应的系统。

  4. 优先级调度(Priority Scheduling)

    类型:可抢占或非抢占式
    特点:每个进程都有一个优先级,优先级高的进程优先执行。调度器根据进程的优先级来选择进程。
    优点:可以有效管理不同重要性的任务,提高关键任务的响应速度。
    缺点:容易导致**“优先级反转”问题,即低优先级的进程阻塞了高优先级进程。也可能出现“饥饿”**问题,即低优先级进程长时间得不到调度。
    适用场景:适用于实时系统或某些需要区分任务优先级的环境。

  5. 轮转调度(Round Robin, RR)

    类型:抢占式
    特点:为每个进程分配一个固定的时间片(Quantum),进程按到达顺序轮流执行。如果进程在时间片内未执行完毕,则放回队列末尾,等待下一轮调度。
    优点:公平性高,每个进程都能在有限时间内得到执行,适合交互式系统。
    缺点:时间片的选择很关键,时间片过长会导致响应时间增加,过短则增加了上下文切换的开销。
    适用场景:适用于交互式系统或需要频繁响应用户请求的系统,如操作系统的时间分片管理。

  6. 多级反馈队列(Multilevel Feedback Queue)

    类型:抢占式
    特点:使用多个队列,根据进程的执行历史动态调整其优先级。新进程进入高优先级队列,如果没有完成,会逐步被移到低优先级队列。进程可以根据表现“上升”或“下降”优先级。
    优点:灵活性高,适应性强,能够动态调整进程优先级以更好地响应系统负载。
    缺点:实现复杂,且难以找到理想的调整策略,可能导致某些进程等待过久。
    适用场景:适用于需要处理不同类型任务、负载波动较大的环境,如操作系统中实际的进程调度器。

  7. 最高响应比优先调度(HRRN, Highest Response Ratio Next)

    类型:非抢占式
    特点:优先选择响应比最高的进程,响应比定义为 (等待时间 + 运行时间) / 运行时间。这意味着等待时间较长的进程会得到优先调度。
    优点:可以平衡短作业和长作业的调度,减少饥饿问题。
    缺点:实现相对复杂,计算响应比增加了调度开销。
    适用场景:适用于有长短作业混合的批处理系统。

进程间有哪些通信方式

  1. 管道(Pipe)

    类型:单向通信
    特点:管道是最早的进程间通信方式之一,允许一个进程将输出通过管道传输给另一个进程作为输入。管道分为两种:- 匿名管道:只能用于父进程和子进程之间的通信。数据在管道的一端写入,从另一端读取。
    命名管道(FIFO):允许无关进程之间通信,具有名称,可以在不同的进程中打开进行读写。
    优点:实现简单,适合父子进程通信。
    缺点:数据是单向传输,不能实现双向通信;匿名管道不能用于不相关的进程。

  2. 消息队列(Message Queue)

    类型:双向通信
    特点:消息队列是一种链表形式的消息存储结构,进程可以通过向队列发送消息或从队列接收消息来进行通信。消息队列是持久化的,系统重启后消息仍然存在。
    优点:消息队列允许多进程之间相互通信,并且能够按顺序读取消息,灵活性高。
    缺点:消息队列的大小有限,数据量过大时可能出现阻塞或丢失消息。

  3. 共享内存(Shared Memory)

    类型:双向通信
    特点:共享内存允许多个进程直接共享同一段内存区域。通过共享内存,多个进程可以访问和修改同一块内存数据,因此通信速度非常快。
    优点:效率高,不需要在进程间拷贝数据,特别适用于大数据量的通信。
    缺点:需要使用同步机制(如信号量或互斥锁)来防止竞争条件,因为多个进程同时访问共享内存可能导致数据一致性问题。

  4. 信号量(Semaphore)

    类型:控制通信
    特点:信号量是一种同步机制,通常用于协调多个进程对共享资源的访问,而不是直接传递数据。信号量可以用于解决进程间的同步和互斥问题,例如防止多个进程同时访问共享资源。
    优点:可以有效控制进程的执行顺序,解决进程间的竞争问题。
    缺点:不适合直接传递大量数据,通常与共享内存等其他IPC机制配合使用。

  5. 信号(Signal)

    类型:异步通信
    特点:信号是一种异步通知机制,用于向进程发送通知或中断。操作系统通过向进程发送信号来通知其发生了某些事件(如终止、挂起、时钟超时等),进程可以设置信号处理函数来响应信号。
    优点:可以在进程执行期间触发某些事件处理,灵活高效。
    缺点:信号传递的数据量有限,且信号处理机制相对复杂。

  6. 套接字(Socket)

    类型:双向通信
    特点:套接字最初用于网络通信,但也可以用于本地进程间通信。通过套接字,两个进程可以在不同的主机或同一主机上进行通信,支持TCP和UDP协议。
    优点:支持远程和本地进程通信,灵活性强,适合分布式系统。
    缺点:通信的开销较大,不如共享内存和管道效率高。

进程同步和互斥,以及如何实现进程同步和互斥

进程同步
进程同步是一种机制,用于协调多个进程的执行顺序,以确保它们在共享资源时可以正确且一致地执行。同步机制可以确保进程在执行到某些关键点时按特定顺序进行。例如,在生产者-消费者问题中,消费者不能在缓冲区为空时进行消费操作,必须等待生产者生产后才能消费。
互斥
互斥是一种确保只有一个进程能够访问某个共享资源的机制,以避免多个进程同时对资源进行操作导致的冲突。例如,多个进程同时写入同一个文件,可能会导致数据的混乱和丢失。通过互斥,操作系统可以保证同一时刻只有一个进程可以进入临界区(访问共享资源的代码段)。

Day12

什么是死锁,如何预防死锁

死锁(Deadlock)是指在多任务并发执行中,多个进程因相互持有对方需要的资源而陷入无限等待的状态,导致无法继续执行下去。这种情况类似于两个进程互相握住对方需要的锁,不愿意放手,从而阻止了对方的继续操作。
死锁产生的四个必要条件
死锁的产生需要满足以下四个条件,同时满足时系统就会发生死锁:

互斥条件(Mutual Exclusion):某些资源只能被一个进程独占,其他进程必须等待。
请求与保持条件(Hold and Wait):进程已经持有至少一个资源,同时还在请求其他资源,并且资源没有释放。
不剥夺条件(No Preemption):已经分配给某个进程的资源在未被该进程主动释放前,不能被强行剥夺。
循环等待条件(Circular Wait):存在一组进程,每个进程都在等待下一进程所持有的资源,从而形成一个环形链。

只有当这四个条件同时满足时,系统才会进入死锁状态。
如何预防死锁?
为了防止死锁,可以采用多种方法来破坏这些必要条件,通常包括以下几种策略:

  1. 破坏“请求与保持”条件

    一次性分配所有资源:要求进程在开始执行时一次性申请所需的所有资源,这样可以避免进程在等待其他资源时持有已有的资源。
    银行家算法:通过资源分配策略来检查资源请求是否会导致系统进入不安全状态。如果请求可能导致死锁,则拒绝分配,直到分配资源不会引发死锁。

  2. 破坏“循环等待”条件

    资源有序分配:为所有资源编号,进程只能按资源编号递增的顺序申请资源,保证不会形成环形等待。例如,若进程要申请资源,则必须首先释放编号较小的资源,再申请编号较大的资源。

  3. 破坏“不剥夺”条件

    允许资源抢占:如果进程请求的资源得不到满足,可以强行从其他进程中剥夺资源,迫使资源持有的进程释放资源,从而使死锁得以避免。这种策略通常不常见,因为它可能导致数据不一致

几种典型的锁

在并发编程中,锁是一种常用的同步机制,用于控制多个线程或进程对共享资源的访问,以确保数据一致性和系统的正确性。以下是几种典型的锁及其特点:

  1. 互斥锁(Mutex)
    互斥锁(Mutex,Mutual Exclusion)是一种最常见的同步机制,用于确保同一时间只能有一个线程访问某个共享资源。互斥锁的操作分为加锁和解锁,只有持有锁的线程可以进入临界区,其他尝试获取锁的线程将被阻塞。

    特点:适合需要排他性访问的场景,保证线程安全。
    使用场景:线程池、数据库操作等需要防止多个线程同时操作共享数据的场景。

  2. 读写锁(读者写者锁,RWLock)
    读写锁(Read-Write Lock)是一种允许多个线程同时读取共享资源,但只允许一个线程进行写操作的锁。读操作是共享的,而写操作是独占的。

    特点:- 共享锁(读锁):多个线程可以同时持有读锁,前提是没有线程持有写锁。
    独占锁(写锁):写锁只能被一个线程持有,且在写锁持有期间,不允许其他线程持有读锁或写锁。
    使用场景:适合读多写少的场景,例如缓存、配置文件等需要频繁读取但不常修改的资源。

  3. 自旋锁(Spinlock)
    自旋锁是一种忙等待的锁,线程在尝试获取锁时会反复检查锁是否被释放,而不是进入阻塞状态。自旋锁会一直占用 CPU,直到获取到锁。

    特点:- 由于线程在等待时不会被阻塞,所以适合临界区执行时间很短的场景。
    会消耗大量 CPU 资源,因此不适合锁等待时间较长的场景。
    使用场景:适合多处理器系统中,临界区很小且竞争不激烈的情况,例如内核中的短时间锁定。

虚拟内存的理解

虚拟内存(Virtual Memory)是一种计算机系统内存管理技术,它通过在硬件和操作系统的配合下,将物理内存扩展为更大的地址空间,使得程序可以使用比实际物理内存更大的地址空间。虚拟内存为程序提供了一种抽象的内存地址空间,程序员可以假定每个进程都有一个连续且足够大的内存空间,而实际上,这些地址可能映射到不同的物理内存页或磁盘存储中。
虚拟内存的核心概念

地址空间抽象:
    虚拟内存为每个进程提供一个独立的、连续的地址空间,使得进程不需要关心物理内存的实际分配情况。这有助于简化编程,因为程序可以假设它独享整个内存空间,而不必处理其他程序的内存占用。
页(Page)和页框(Frame):
    虚拟内存将逻辑地址空间划分为固定大小的块,称为页(Page),而物理内存也被划分为相同大小的页框(Frame)。在程序执行时,虚拟页被映射到物理页框,页面的大小通常是4 KB或者更大。
页面表(Page Table):
    页面表是一种数据结构,用于存储虚拟地址到物理地址的映射关系。当进程访问内存时,CPU会通过页面表找到虚拟地址对应的物理地址。页面表存储在内存中,并在每个进程之间独立维护。

虚拟内存的工作机制

地址转换(Address Translation): CPU访问内存时,使用内存管理单元(MMU,Memory Management Unit)将虚拟地址转换为物理地址。地址转换的过程包括查询页面表,将虚拟地址分为页面号和偏移量,通过页面号找到对应的物理页框,再加上偏移量获得最终物理地址。
页面置换(Page Replacement): 当物理内存中的可用页框不足时,系统需要将不常用的页面移出内存,将它们暂存到磁盘中(也叫交换区)。当被移出的页面再次被访问时,会触发缺页中断(Page Fault),将页面从磁盘中重新加载到内存中。页面置换算法如LRU(最近最少使用)、FIFO(先进先出)等用于决定哪个页面被移出。
缺页中断(Page Fault): 当进程访问的虚拟页面不在物理内存中时,会触发缺页中断。这种情况通常是因为页面被换出到磁盘,或是进程首次访问新的内存区域。系统会响应缺页中断,将页面从磁盘中加载到内存中,更新页面表,然后继续执行进程。

虚拟内存的优点

内存扩展: 虚拟内存允许进程使用比实际物理内存更大的内存空间。程序员可以编写使用大内存的数据结构,而不必担心物理内存的大小限制。
内存保护: 虚拟内存为每个进程提供了独立的虚拟地址空间,不同进程之间的内存不会互相干扰,增加了进程的隔离性和安全性。这有助于防止一个进程访问或修改其他进程的数据。
内存共享: 不同进程可以共享某些内存页。例如,系统中的共享库(如DLL)可以被多个进程加载,虚拟内存机制可以确保这些进程共享同一份物理页,从而节省内存。
内存利用率: 虚拟内存机制使得程序无需全部加载到物理内存中就能运行,通过按需加载和页面置换等机制,可以大幅提高物理内存的利用效率。

虚拟内存的缺点

性能开销: 虚拟内存需要进行虚拟地址到物理地址的转换,涉及查找页面表、硬件MMU转换等操作,存在一定的性能开销。同时,缺页中断涉及到从磁盘加载页面,速度相对较慢,可能会影响系统性能。
复杂性增加: 虚拟内存的实现涉及到内存管理单元、页面表、页面置换算法等多个组件,增加了系统的实现复杂性和开销。

Day13

线程同步的方式

线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。
下面是几种常见的线程同步的方式:
互斥锁(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
读写锁(Read-Write Lock):允许多个线程同时读取共享资源,但只有一个线程可以对共享资源进行写操作。
信号量(Semaphore):它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
屏障(Barrier):屏障是一种同步原语,用于等待多个线程到达某个点再一起继续执行。当一个线程到达屏障时,它会停止执行并等待其他线程到达屏障,直到所有线程都到达屏障后,它们才会一起继续执行。比如 Java 中的 CyclicBarrier 是这种机制。
事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。

页面置换算法

  1. 最佳置换算法(OPT - Optimal Page Replacement)

    描述:最佳置换算法是一种理论上的最优页面置换策略,每次选择那些在未来最远时间才会被访问的页面进行替换,从而保证缺页中断次数最少。
    优点:可以达到最小的缺页率。
    缺点:由于需要预知未来的页面访问情况,这个算法无法在实际系统中实现,只用于理论对比。

  2. 先进先出算法(FIFO - First-In-First-Out)

    描述:先进先出算法将最早进入内存的页面替换出去。当新页面需要加载但内存已满时,选择最先进入的页面进行置换。
    优点:实现简单。
    缺点:缺乏考虑页面使用频率,可能会将一些常用的页面替换出去,导致较高的缺页率。这种现象被称为 Belady 异常,即增加页框数反而可能增加缺页次数。

  3. 最近最久未使用算法(LRU - Least Recently Used)

    描述:LRU算法基于页面的历史使用情况,选择最近最久未使用的页面进行替换。它假设最久未使用的页面在未来也不太可能被访问。
    优点:通常表现优于FIFO,缺页次数较少。
    缺点:实现起来较为复杂,需要维护页面的使用时间或使用计数,涉及较多开销。

  4. 时钟算法(Clock or Second-Chance Algorithm)

    描述:时钟算法是对FIFO的改进,它为每个页面维护一个“使用位”(reference bit)。页面首次被加载时,使用位被设置为1。当要替换页面时,检查页面的使用位,如果是0则将其替换;如果是1则将使用位置为0,继续扫描下一个页面。
    优点:实现简单,性能接近LRU,内存开销较低。
    缺点:在极端情况下,可能需要多次扫描页面,增加了算法的执行时间。

  5. 最不常用算法(LFU - Least Frequently Used)

    描述:LFU算法选择访问次数最少的页面进行置换,假设访问次数少的页面在未来也不太可能被使用。每个页面有一个计数器,每次页面被访问时计数器增加。
    优点:在某些访问模式下可以有效减少缺页次数。
    缺点:对老页面不公平,即使长时间不再被访问,其计数仍然很高。另外,维护每个页面的计数器有一定的内存和时间开销。

  6. 最近未使用算法(NRU - Not Recently Used)

    描述:NRU算法将页面分为四类:- 类0:未被修改且最近未被访问的页面。
    类1:未被修改但最近被访问的页面。
    类2:被修改但最近未被访问的页面。
    类3:被修改且最近被访问的页面。 NRU会优先选择类编号最低的页面进行置换。
    优点:实现简单,利用“修改位”和“引用位”进行分级管理。
    缺点:每次只对页面进行简单的分类,替换策略相对粗糙,不能很好地处理复杂的页面访问模式。

Day15

Linux命令

文件与目录管理 ls:列出目录内容 cd:改变当前目录 mkdir:创建新目录。 pwd:显示当前所在目录的完整路径。
文件内容查看与编辑 cat:查看文件内容
权限管理 chmod:更改文件或目录的权限。
系统信息查看 top / htop:实时显示系统的资源使用情况(CPU、内存、进程等) df:显示文件系统的磁盘使用情况。 free:查看内存使用情况。 ps-ef:查看当前系统中的进程。
网络管理 netstat / ss:查看网络连接状态。 端口是否被占用
进程管理 kill:终止进程 kill -9 # 强制终止指定的进程

查看某个端口号有没有被占用

在 Linux 中可以使用多种方法来查看端口是否被占用:

netstat:使用 netstat -tuln | grep :<端口号>。
lsof:使用 lsof -i :<端口号> 查看端口占用情况。
ss:使用 ss -tuln | grep :<端口号>。
netcat:使用 nc -zv 127.0.0.1 <端口号>。
fuser:使用 fuser <端口号>/tcp。

select,poll,epoll

  1. select
    select 是一种最早的 I/O 多路复用机制,它允许程序检查一组文件描述符的状态(可读、可写、错误)。
    特点

    文件描述符限制:select 的文件描述符数量有上限(通常是 1024,可以通过重新编译内核来增加,但操作复杂)。
    线性扫描:select 每次调用时,都需要遍历所有的文件描述符以检查其状态。对于大量文件描述符来说,性能不佳。
    传入与传出集合相同:select 使用传入的 fd_set 结构体来标识需要监控的文件描述符。每次调用 select 后,fd_set 都会被修改,程序需要重新设置监控的描述符。

使用场景
select 适合于小规模、简单的 I/O 复用任务,比如文件描述符数量较少(小于 1024)且对性能要求不高的场景。
poll
poll 是 select 的增强版,提供了更灵活的接口,不再有文件描述符数量的限制。
特点

无文件描述符数量限制:poll 使用一个动态的数组来存储文件描述符,因此理论上没有数量限制,受限于系统资源。
线性扫描:poll 需要遍历所有的文件描述符来检查其状态,因此性能也不理想,在高并发场景下效率不高。
事件重置问题:与 select 类似,每次调用 poll 后都需要重新填充文件描述符的数组。

使用场景
poll 适用于比 select 更复杂一些的场景,尤其是需要监控较多文件描述符的场景。尽管它解决了文件描述符数量的限制,但对于大量并发连接来说性能依旧不够理想。
epoll
epoll 是 Linux 提供的一种高效的 I/O 多路复用机制,专为处理大量文件描述符的场景设计。
特点

无文件描述符限制:epoll 也没有文件描述符数量的限制,可以处理大规模文件描述符的监控。
事件驱动机制:epoll 采用事件驱动的回调机制,将活跃的文件描述符通知给应用程序,这使得 epoll 在大量文件描述符中仅需关注少数活跃描述符,大幅提升了性能。
效率高:epoll 通过内核和用户空间共享内存来减少数据复制的开销,并通过 epoll_wait 来返回有事件的文件描述符,避免线性遍历的开销。
两种工作模式:- LT(Level Triggered,电平触发):默认模式,类似于 poll,当文件描述符有数据时会一直通知。
    ET(Edge Triggered,边缘触发):高效模式,只有状态从未准备好变为准备好时才会通知,需要程序反复读取直到没有数据。

使用场景
epoll 适合于大规模并发连接的场景,如高性能的网络服务器和高并发的 I/O 多路复用任务。在处理成千上万个连接时,它的性能远远优于 select 和 poll。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐