2025年libco源码分析(libyuv源码剖析)

libco源码分析(libyuv源码剖析)Libevent 源码剖析之 bufferevent CSDN 博客 Libevent 源码剖析之 evbuffer CSDN 博客 Libevent 源码剖析 event CSDN 博客 Libevent 源码剖析之 reactor CSDN 博客 Libevent 源码剖析之 iocp iocp 源码 CSDN 博客 Libevent 源码剖析之 epoll CSDN 博客 Libevent 源码剖析之 poll CSDN 博客 Libevent 源码剖析之 select CSDN 博客 nbsp nbsp 关于 libevent



Libevent源码剖析之bufferevent-CSDN博客

Libevent源码剖析之evbuffer-CSDN博客

Libevent源码剖析-event-CSDN博客

Libevent源码剖析之reactor-CSDN博客

Libevent源码剖析之iocp_iocp 源码-CSDN博客

Libevent源码剖析之epoll-CSDN博客

Libevent源码剖析之poll-CSDN博客

Libevent源码剖析之select-CSDN博客

    关于libevent,曾在项目中直接或间接使用过,360开源的evpp便是对libevent的c++封装,窃以为简单好用,但裸用的话本人在最近开源的缓存库C缓存库Github地址里使用过,感受是比较累。累的原因,一则是C语言所写,一则是对libevent源码还不够熟,踩坑较多。

    因此,从本文开始,拟对libevent写系列文章,目的是通过对libevent的源码分析,来加深理解。   

    后文均基于libevent2.1.18版本来剖析。

libraryversionlibevent2.1.18

    这里简要介绍下文章结构,算是提纲挈领。

  1. 首先,介绍libevent的核心部分,包括各平台IO多路复用,reactor原理,事件注册&收集&分发&处理,以及一些重要数据结构的实现原理,比如小根堆。
  2. 其次,会详细分析evbuffer的实现原理,以及为何需要evbuffer、它解决了什么问题。
  3. 再则,进一步探讨bufferevent的实现原理,以及相关api、为何需要bufferevent、它解决了什么问题;
  4. 最后,有了以上核心特性之后,我们便可以利用libevent开发任何基于tcp/udp的应用程序了,这里会以http server为例来介绍。

    此处简要介绍下libevent的各层次,及其职责:

  1. libevent的核心层,是基于各平台IO多路复用的reactor实现,其中iocp是proactor模型,libevent亦将其融入进核心层。
  2. 中间层,则是引入了evbuffer,本层是bufferevent外围及调用方和libevent的核心层的一个数据缓冲层。
  3. 最外层,便是bufferevent,本层是libevent库对外的模块,也就是直接面向库的使用者。

2.1.1 reactor

    reactor 模式 是一种设计模式,常用于处理多并发的 I/O 事件,尤其在高性能服务器或网络框架中广泛应用。它通过事件驱动机制和非阻塞 I/O 实现多个客户端请求的并发处理,而无需为每个连接分配一个独立的线程或进程。

核心组件

reactor 模式通常包含以下几个主要组件:

1. reactor (反应器):

  • 负责等待并检测 I/O 事件(如读、写、连接等)。当事件发生时,Reactor 将事件分派给合适的事件处理器(或回调函数)进行处理。
  • 一般通过事件多路复用机制(如 select、poll、epoll 在 Linux 上,kqueue 在 BSD 上,或 Windows 的 IOCP)来监视多个 I/O 操作是否准备就绪。

2. handlers (事件处理器):

  • 每个事件处理器都与某一类事件(如读、写、错误事件)关联。当 Reactor 检测到某类事件时,会调用相应的事件处理器来处理该事件。

3. demultiplexer (事件多路分离器):

  • 这是底层的操作系统功能,用来将多个事件(I/O 操作)组合并检测哪些事件已经准备就绪。常见的多路分离器有 select、poll、epoll 等。

4. event loop (事件循环):

  • 事件循环是 reactor 模式的核心机制,它持续循环等待新的事件并处理。整个 reactor 的流程是基于事件循环的不断运行。

工作流程

1. 注册事件

  • 服务器在启动时,将需要监听的 I/O 事件(如客户端连接请求、读写请求等)注册到 reactor 中。

2. 等待事件发生

  • reactor 通过事件多路分离器(如 epoll 或 select)持续监听多个 I/O 事件,当有事件就绪时,会通知 reactor。

3. 事件分派

  • 当有 I/O 事件就绪时,reactor 会调用对应的事件处理器(handler)进行处理。每个事件类型都会有其对应的处理器。

4. 处理事件

  • 事件处理器对具体的事件(如读写数据、处理请求)进行处理,完成后返回到 reactor 继续监听新的事件。

2.1.2 reactor与proactor的区别

reactor 模式与 proactor 模式 是两种不同的 I/O 模型:

reactor:

  • I/O 操作是由应用程序发起的,当 I/O 事件(如数据可读、可写)准备好时,reactor 通知应用程序进行处理。应用程序在事件触发时执行实际的 I/O 操作。
  • 同步IO模型

proactor:

  • I/O 操作由操作系统发起,应用程序只需提供完成后的处理逻辑。操作系统在 I/O 操作完成后通知应用程序进行处理。
  • 异步IO模型

在 reactor 模式下,事件的处理是由应用程序主动执行的;而在 proactor 模式下,事件处理通常由操作系统完成。

2.1.3 IO多路复用

    在以往的同步阻塞模式下,对IO的检测读写操作是由类似read()/write()系统调用来完成的,而在reactor模型里,对IO的检测则是通过IO多路复用系统调用来完成,read()/write()则具体执行读写操作

    换言之,IO多路复用的职责是IO检测,而由read()/write()来完成读写操作。    

2.1.3.1 select

    select 多路复用是一种用于监视多个文件描述符(如网络套接字、文件等)的 I/O 操作状态的系统调用。通过 select,程序可以同时等待多个 I/O 操作的就绪(如读、写或异常事件),从而避免为每个 I/O 操作创建独立的线程或进程。

    select 最常用于高并发场景下的事件驱动编程,例如网络服务器或实时通信系统。通过 select,应用可以一次性监视大量连接,处理 I/O 操作时不会阻塞。

  1. 注册文件描述符:程序将感兴趣的文件描述符(如网络连接的 socket 文件描述符)注册给 select。

  2. 监视文件描述符:select 进入阻塞状态,等待文件描述符上的 I/O 事件(如可读、可写或出现异常)发生。

  3. 事件就绪时返回:当某个文件描述符的 I/O 操作准备就绪时,select 返回,通知应用程序哪些文件描述符可以执行 I/O 操作。

  4. 处理事件:应用程序根据 select 返回的结果对已准备就绪的文件描述符执行相应的操作。

2.1.3.2 poll

    poll 多路复用是与 select 类似的系统调用,用于监控多个文件描述符上的 I/O 操作,允许程序同时等待多个事件(如可读、可写、或异常)。与 select 相比,poll 去除了文件描述符数量的限制,并提供了更灵活的接口。它是 select 的改进版本,适用于高并发场景。

  1. 监控多个文件描述符:poll 可以监控任意数量的文件描述符(不限于 select 的 1024 个限制)。

  2. 阻塞等待事件:程序调用 poll,阻塞等待指定的文件描述符变为就绪状态(如可读、可写或出现异常)。

  3. 事件返回:一旦文件描述符有事件发生,poll 返回,程序可以处理这些事件。

2.1.3.3 epoll

    epoll 是 Linux 提供的一种高效的多路复用机制,用于处理大量并发连接时的 I/O 事件。与传统的 select 和 poll 相比,epoll 的性能大幅提高,尤其在监控大量文件描述符时具有显著优势。

  1. 事件驱动:epoll 采用事件驱动模式(edge-triggered 和 level-triggered),只在文件描述符的状态发生变化时才通知应用程序,而不像 select 和 poll 需要每次调用时轮询所有文件描述符。

  2. O(1) 复杂度:epoll 的性能不随着监控文件描述符数量的增加而下降,在管理大量连接时表现出近乎恒定的时间复杂度。

  3. 内核事件表:epoll 通过内核维护的事件表,避免每次调用时重新遍历文件描述符列表。

2.1.3.4 iocp

I/O Completion Ports (IOCP) 是 Windows 上的一种高效 I/O 多路复用机制,用于处理大量并发连接和 I/O 操作。与 select、poll 等轮询机制不同,IOCP 采用了完成端口和回调机制来高效地处理异步 I/O 操作。

  1. 异步 I/O:IOCP 允许程序在执行 I/O 操作时不阻塞主线程,I/O 操作会在后台进行,完成后通过完成端口通知主线程。

  2. 线程池:IOCP 使用线程池来处理并发 I/O 事件,根据负载动态分配工作线程,从而最大化 CPU 和 I/O 资源的利用。

  3. 完成端口(Completion Ports):完成端口是 IOCP 的核心,它是一个操作系统提供的队列,异步 I/O 操作的结果会被推送到这个队列中,工作线程从中获取 I/O 结果并进行处理。

  4. 句柄绑定:每个 I/O 操作的文件句柄(socket、文件、管道等)可以绑定到一个完成端口,当操作完成后,系统将事件通知到相应的完成端口。

2.1.3.5 devpoll

/dev/poll 是 Solaris 操作系统上的一种 I/O 多路复用机制,类似于 Linux 上的 poll 和 epoll,但在性能和可扩展性上进行了优化。它为需要监视大量文件描述符(如网络服务器等高并发场景)的应用程序提供了一种高效的事件通知机制。

  1. 文件描述符监控:与传统的 poll 不同,/dev/poll 通过一个持久的设备文件(/dev/poll)来跟踪文件描述符的状态,而不是每次调用都需要重新传入一组文件描述符。程序只需一次性注册需要监控的文件描述符,之后可以多次调用等待事件,极大地减少了系统调用的开销。

  2. 持久性:注册的文件描述符是持久的,直到被手动移除或文件关闭,因此不需要像传统 poll 那样在每次事件检测时都传递整个文件描述符集。

  3. 事件通知:当文件描述符的状态发生变化时,/dev/poll 会通知应用程序,允许其处理相应的 I/O 操作。

2.1.3.6 devport

    dev/port 是 Solaris 操作系统中的一种设备接口,主要用于直接访问 I/O 端口,但它不是一种多路复用机制。与多路复用相关的机制主要包括 select、poll、epoll、/dev/poll、IOCP 等。因为 /dev/port 的主要用途与 I/O 端口的直接访问有关,而不是事件驱动的 I/O 多路复用,所以它与多路复用没有直接的联系。

    因此,本书不表,感兴趣的读者可查询libevent2.1.18相关源码。

2.1.3.7 kqueue
2.1.3.8 win32select 

    TODO : 后续补充 

2.1.4 evbuffer

    evbuffer模块,在libevent库中,是介于reactor模块和bufferevent模块之间,属于中间层。

    在非阻塞的网络程序里,网络库应该能够缓存业务app的数据,以便在网络可用时再由网络库将数据发送出去(或者将数据接收后缓存起来再通知业务app),如此便不至于阻塞业务程序。

    这就是evbuffer的职责,也就是evbuffer要解决的问题。

    evbuffer的原理,一言以蔽之,就是用若干段堆上分配的非连续空间,以链条的方式来存储连续数据。

    libevent对evbuffer的实现,其功能是比较丰富的:

  • 接收栈上内存数据;
  • 接收堆上内存数据;
  • 接收evbuffer;
  • 接收可变参数数据;
  • 接收整个文件;
  • 接收片段文件;

2.1.5 bufferevent

    bufferevent模块,是libevent的最外层,是直接面向调用方的。换言之,若要用libevent开发程序,bufferevent是程序员最常打交道的。

    bufferevent,在libevent库的使用方和evbuffer之间,起着承上启下的作用。

2.1.6 evconnlistener

    在基于libevent实现tcp的服务端程序时,如果裸用系统调用的话,需要每次都要通过socket()来创建fd,再通过bind()来绑定,再通过listen()来监听,然后通过accept()来接收客户端连接,最后再通过send()/recv()来收发数据,完了再通过close()关闭fd。

    这些都是例行程序,libevent通过evconnlistener来完成此功能。换言之,当我们基于libevent实现tcp的服务端app时,直接用evconnlistener来实现一个acceptor即可,几行代码即可完成。

    值得一提的是,若选择的是epoll,则缺省用ET边缘模式来提高效率。

2.1.7 重要数据结构

    然后,来介绍下libevent的重要数据结构,比如event_baseeventeventopevconnlisteneriocp相关等等。

http server

    在libevent实现了reactor和evbuffer以及bufferevent特性之后,这就是一个完整的网络库了,我们就可以利用libevent库来开发任何tcp或udp程序了。

    libevent的官方,利用libevent实现了3个例子:http客户端和服务器,dns和rpc。本文以http为例来介绍,其他例子可类比理解。

    值得一提的是,libevent实现的http,是基于http1.2协议以下,只实现了http1.1标准部分,未实现http的流式传输。

编程小号
上一篇 2025-02-15 08:57
下一篇 2025-02-26 12:33

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/49619.html