企业网站规划要求,盘锦网站建设咨询,代运营公司排行榜,佛山高端网站制作公司汽车软件中的CPU密集与IO密集任务
在汽车软件中#xff0c;涉及到ADAS的长期占用CPU的计算任务可以算的上是CPU密集型。
另外的#xff0c;众多SOA原子服务或者各种数据收集、处理、分发、log系统#xff0c;应该算是IO密集型任务。
寻求一些手段优化IO性能的原因
在过去…汽车软件中的CPU密集与IO密集任务
在汽车软件中涉及到ADAS的长期占用CPU的计算任务可以算的上是CPU密集型。
另外的众多SOA原子服务或者各种数据收集、处理、分发、log系统应该算是IO密集型任务。
寻求一些手段优化IO性能的原因
在过去开发应用或者中间件时使用Linux提供的接口例如直接socket一般就两种模式实质上是使用了同步IO
1、开启循环子线程阻塞在socket接收处。
2、线程开启循环周期轮询socket。
这种方式造成了一些弊端例如对每个socket都需要维护一个子线程系统给每个线程分配资源造成了资源浪费如内存。 虽然阻塞不占用CPU时间但是如果存在大量socket子线程线程调度会花费很多CPU时间在进程切换中。造成系统CPU负载率高。
潜在的解决方案
经过研究一些关键词引起关注。正如本文的标题。
Reactor 和 Proactor模式
Reactor模式和Proactor模式都是基于事件驱动的设计模式用于处理高并发环境下的I/O多路复用问题。然而它们在处理I/O事件的方式上有所不同。
Reactor模式是非阻塞同步网络模式它感知的是就绪可读写事件。具体来说当某个I/O事件例如可读就绪发生时需要应用进程主动调用相应的read方法来完成数据的读取。这个过程是同步的即读取完数据后应用进程才能处理数据。因此Reactor模式可以理解为“来了事件操作系统通知应用进程让应用进程来处理”。
Proactor模式则是异步网络模式它感知的是已完成的读写事件。在发起异步读写请求时需要传入数据缓冲区的地址等信息这样系统内核才可以自动帮我们把数据的读写工作完成。这里的读写工作全程由操作系统来做并不需要像Reactor那样还需要应用进程主动发起read/write来读写数据。操作系统完成读写工作后就会通知应用进程直接处理数据。因此Proactor模式可以理解为“来了事件操作系统来处理处理完再通知应用进程”。
总的来说Reactor和Proactor模式都是基于事件分发的网络编程模式区别在于Reactor模式是基于“待完成”的I/O事件而Proactor模式则是基于“已完成”的I/O事件。因此Proactor模式更加高效因为它避免了线程间的协作可以更快地响应I/O事件但是它的实现相对比较复杂。
IO复用
所谓IO复用目的是解决前述问题中的每个socket开一个线程的问题。
使用IO复用的机制或者进行IO复用设计如上文的Reactor模式使用一个进程监听多个socket。
例如使用一个线程监听多个事件源如socket当有事件发生或者可读后通知对应的程序处理。
落实到Linux内核来帮你做就变成了Linux IO复用机制以前有select poll。现在是epoll。
epoll 机制与Reactor
epoll
epoll是Linux下的一个I/O多路复用技术用于高效地处理大量并发连接。它提供了一组接口用于注册、修改和删除文件描述符的监听事件。
epoll的接口包括以下几个
epoll_create()创建一个新的epoll实例并返回一个指向它的文件描述符。epoll_ctl()注册、修改或删除文件描述符的监听事件。它需要传入epoll实例的文件描述符、文件描述符、事件类型和回调函数。epoll_wait()等待注册的文件描述符就绪并返回就绪的文件描述符列表。它需要传入epoll实例的文件描述符和最大等待时间。epoll_pwait()与epoll_wait类似但是可以设置超时时间并且可以同时处理多个事件。
使用epoll的一般步骤如下
创建一个epoll实例获取其文件描述符。使用epoll_ctl()函数注册需要监听的事件和回调函数。在需要处理事件的时候调用epoll_wait()或epoll_pwait()函数等待事件的发生。处理就绪的事件。
Reactor
Reactor模式是一种事件驱动的设计模式用于处理高并发环境下的I/O多路复用问题。它由以下几个组件组成
Reactor反应器是一个接口定义了注册事件处理器、分发事件和事件处理的方法。通常Reactor通过异步方式将事件分发给注册的事件处理器。Event Handler事件处理器是一个接口定义了事件的回调方法。事件处理器实现了Reactor接口并注册自己感兴趣的事件类型。当事件发生时Reactor调用事件处理器的回调方法来处理事件。Concrete Event Handler具体事件处理器是事件处理器的实现。在其内部实现了事件处理器的回调方法进行业务逻辑处理。Initiation Dispatcher初始分发器实际上就是Reactor角色。它本身定义了一些规范这些规范用于控制事件的调度方式。同时又提供了应用进行事件处理器的注册、删除等操作。它本身是整个事件处理器的核心所在Initiation Dispatcher会通过Synchronous Event Demultiplexer来等待事件的发生。
工作方式
应用程序创建Reactor对象并注册感兴趣的事件类型和对应的事件处理器。当事件发生时Initiation Dispatcher通过Synchronous Event Demultiplexer将事件传递给对应的事件处理器。事件处理器接收到事件后执行相应的业务逻辑处理。处理完成后事件处理器会再次注册自己感兴趣的事件类型以便后续继续处理事件。
可以看出epoll是一个近似于reactor的实现或者说基于epoll机制可以比较方便的实现一个reactor模式来实现事件驱动程序设计。
我们需要做的是在epoll 的wait返回后根据返回的socket 就绪列表去分发去调用对应的处理程序。
相较于异步IO来说在分发的时候这个socket还没有被读取需要应用程序去读取但是在调用读取的系统调用的时候也会存在一个阻塞读取数据例如从内核态拷贝到用户态的过程我觉得这对目前的工作来讲已经过于玄学了车端的应用没互联网那么夸张。
Proactor模式 异步IO Boost.asio 协程
Proactor模式是一种消息异步通知的设计模式它主要用于处理高并发环境下的I/O多路复用问题。以下是Proactor模式的各个组件及其功能和工作方式
Handle句柄用于标识socket连接或者是打开文件。在网络服务器中每个客户连接都会创建不同的套接字句柄当异步连接、读、写操作执行完成时完成事件会出现在这些句柄上。Asynchronous Operation Processor异步操作处理器负责执行异步操作一般由操作系统内核实现。Asynchronous Operation异步操作这是应用程序发出的服务请求比如异步的通过套接字句柄读写数据。当异步操作激活后操作不需要借用回调线程的控制即可执行。因此从回调者角度看操作的执行是异步的。Completion Event Queue完成事件队列异步操作完成的结果会放到队列中等待后续使用。Proactor主动器为应用程序提供事件循环从完成事件队列中取出异步操作的结果分别调用相应的后续处理逻辑。Completion Handler完成事件接口一般是由回调函数组成的接口。Concrete Completion Handler完成事件处理逻辑完成接口定义特定的应用处理逻辑。
在业务流程及时序图中应用程序启动后会调用异步处理器提供的异步操作接口来发起异步操作。异步操作处理器接收到操作请求后会执行相应的异步操作。当操作完成后会将完成事件放入完成事件队列中。Proactor会不断地轮询完成事件队列一旦发现有完成事件就会调用相应的回调函数来处理完成事件。
Boost.Asio是一个广泛使用的C库用于处理低级网络编程和并发任务。它实现了Proactor模式提供了一种高效和灵活的方式处理异步I/O操作。
在Boost.Asio中Proactor模式的应用主要体现在异步I/O操作的处理上。具体来说当应用程序发起一个异步操作如异步读或写时Boost.Asio会将其封装为一个异步操作对象并将其注册到异步事件处理器中。
异步事件处理器使用异步事件分发器Asynchronous Event Demultiplexer来等待事件完成。当异步操作完成时完成事件会被放入完成事件队列中。然后Proactor会调用异步事件分发器将完成事件返回给其调用者。
在处理完成事件时Proactor会调用相应的回调函数。
这个回调函数通常是由应用程序通过boost::bind创建的函数对象用于处理异步操作的结果。
通过这种方式Boost.Asio实现了高效的异步I/O处理使得应用程序可以在不阻塞主线程的情况下处理大量的并发连接和请求。同时Proactor模式还提供了回调函数的方式使得应用程序可以灵活地处理完成事件从而实现了异步编程。
据说底层是epoll。。
异步IO与协程
之前我想过一个问题异步IO可以在调用IO操作之后先干别的不用等待。
在同步按照时间顺序典型的C编程方式下我调用IO肯定是要IO的数据如果不等着拿数据这个时候能干嘛呢。
直到我看到了协程这个东西豁然开朗之前也困惑或协程这个在一个进程内跳来跳去执行的东西有什么用。
协程和异步IO结合可以进一步优化IO密集任务。
异步IO实际上就是给各个事件注册处理函数让程序在各个处理函数之间跳来跳去就是纯纯用户态的事情了甚至可以躲在用户进程里不出来根本没有切换进程开销岂不美哉。