[Android] Graphic Buffer 的申请
前言:
MediaCodec 支持 texture mode,即MediaCodec解码video完毕后把 yuv 数据填入 GPU 共享出来的 graphic buffer 里面,app 会把 video 的 yuv数据 和 ui 的数据通过通过软件渲染组件(opengl等)发送给GPU 进行一并渲染。这样做的效率较低,但是稳定性较好。且性能取决于GPU的性能。
另外一种是 surface mode,就是常用的模式,这种模式下 app 不再获取 yuv 数据,而是只负责输送 es 数据并决定 render 还是 discard 解码后的数据,且是根据 bufferid 来判断的,根本没有机会接触解码后的 yuv 数据,所有的一切都在平台完成。这种办法效率搞且功耗也很低,因为不需要GPU的参与。大部分情况下都是这种模式。
本文简单理清一下 Graphic Buffer 的框架,以对 texture mode 中的 Graphic Buffer 申请流程有一个大致的概念。
Graphic Buffer 的申请
始于 ANativeWindow 类
frameworks/native/libs/nativewindow/include/system/window.h
struct ANativeWindow
{
#ifdef __cplusplusANativeWindow(): flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0){common.magic = ANDROID_NATIVE_WINDOW_MAGIC;common.version = sizeof(ANativeWindow);memset(common.reserved, 0, sizeof(common.reserved));}/* Implement the methods that sp<ANativeWindow> expects so that itcan be used to automatically refcount ANativeWindow's. */void incStrong(const void* /*id*/) const {common.incRef(const_cast<android_native_base_t*>(&common));}void decStrong(const void* /*id*/) const {common.decRef(const_cast<android_native_base_t*>(&common));}
#endifstruct android_native_base_t common;/* flags describing some attributes of this surface or its updater */const uint32_t flags;/* min swap interval supported by this updated */const int minSwapInterval;/* max swap interval supported by this updated */const int maxSwapInterval;/* horizontal and vertical resolution in DPI */const float xdpi;const float ydpi;/* Some storage reserved for the OEM's driver. */intptr_t oem[4];/** Set the swap interval for this surface.** Returns 0 on success or -errno on error.*/int (*setSwapInterval)(struct ANativeWindow* window,int interval);/** Hook called by EGL to acquire a buffer. After this call, the buffer* is not locked, so its content cannot be modified. This call may block if* no buffers are available.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new dequeueBuffer function that* outputs a fence file descriptor should be used in its place.*/int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer);/** hook called by EGL to lock a buffer. This MUST be called before modifying* the content of a buffer. The buffer must have been acquired with* dequeueBuffer first.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but it is essentially a no-op, and calls* to it should be removed.*/int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** Hook called by EGL when modifications to the render buffer are done.* This unlocks and post the buffer.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** Buffers MUST be queued in the same order than they were dequeued.** Returns 0 on success or -errno on error.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new queueBuffer function that* takes a fence file descriptor should be used in its place (pass a value* of -1 for the fence file descriptor if there is no valid one to pass).*/int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** hook used to retrieve information about the native window.** Returns 0 on success or -errno on error.*/int (*query)(const struct ANativeWindow* window,int what, int* value);/** hook used to perform various operations on the surface.* (*perform)() is a generic mechanism to add functionality to* ANativeWindow while keeping backward binary compatibility.** DO NOT CALL THIS HOOK DIRECTLY. Instead, use the helper functions* defined below.** (*perform)() returns -ENOENT if the 'what' parameter is not supported* by the surface's implementation.** See above for a list of valid operations, such as* NATIVE_WINDOW_SET_USAGE or NATIVE_WINDOW_CONNECT*/int (*perform)(struct ANativeWindow* window,int operation, ... );/** Hook used to cancel a buffer that has been dequeued.* No synchronization is performed between dequeue() and cancel(), so* either external synchronization is needed, or these functions must be* called from the same thread.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** XXX: This function is deprecated. It will continue to work for some* time for binary compatibility, but the new cancelBuffer function that* takes a fence file descriptor should be used in its place (pass a value* of -1 for the fence file descriptor if there is no valid one to pass).*/int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer);/** Hook called by EGL to acquire a buffer. This call may block if no* buffers are available.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The libsync fence file descriptor returned in the int pointed to by the* fenceFd argument will refer to the fence that must signal before the* dequeued buffer may be written to. A value of -1 indicates that the* caller may access the buffer immediately without waiting on a fence. If* a valid file descriptor is returned (i.e. any value except -1) then the* caller is responsible for closing the file descriptor.** Returns 0 on success or -errno on error.*/int (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer, int* fenceFd);/** Hook called by EGL when modifications to the render buffer are done.* This unlocks and post the buffer.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The fenceFd argument specifies a libsync fence file descriptor for a* fence that must signal before the buffer can be accessed. If the buffer* can be accessed immediately then a value of -1 should be used. The* caller must not use the file descriptor after it is passed to* queueBuffer, and the ANativeWindow implementation is responsible for* closing it.** Returns 0 on success or -errno on error.*/int (*queueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer, int fenceFd);/** Hook used to cancel a buffer that has been dequeued.* No synchronization is performed between dequeue() and cancel(), so* either external synchronization is needed, or these functions must be* called from the same thread.** The window holds a reference to the buffer between dequeueBuffer and* either queueBuffer or cancelBuffer, so clients only need their own* reference if they might use the buffer after queueing or canceling it.* Holding a reference to a buffer after queueing or canceling it is only* allowed if a specific buffer count has been set.** The fenceFd argument specifies a libsync fence file decsriptor for a* fence that must signal before the buffer can be accessed. If the buffer* can be accessed immediately then a value of -1 should be used.** Note that if the client has not waited on the fence that was returned* from dequeueBuffer, that same fence should be passed to cancelBuffer to* ensure that future uses of the buffer are preceded by a wait on that* fence. The caller must not use the file descriptor after it is passed* to cancelBuffer, and the ANativeWindow implementation is responsible for* closing it.** Returns 0 on success or -errno on error.*/int (*cancelBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer* buffer, int fenceFd);
};
上述代码中有很多函数指针,比如
int (*dequeueBuffer)(struct ANativeWindow* window,struct ANativeWindowBuffer** buffer, int* fenceFd);
这些指针的具体实现在其子类中有实现和设置,这里是一种 C 的 hook 写法,而不是 c++ 的虚函数写法,可能出于兼容性考虑。
\frameworks\native\libs\nativewindow\ANativeWindow.cpp 里面并不是 ANativeWindow的实现,而是对其封装,比如:
int ANativeWindow_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer, int* fenceFd) {return window->dequeueBuffer(window, buffer, fenceFd);
}
可以看到,这里调用了 ANativeWindow的dequeueBuffer函数,而这个函数在 ANativeWindow 结构里是一个函数指针,而这个函数指针又指向哪里呢?
Hook的实现者Surface
\frameworks\native\libs\gui\include\gui\Surface.h
\frameworks\native\libs\gui\Surface.cpp
class Surface: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{}
Surface 是 ANativeWindow的子类,但是考虑到 ANativeWindow通过 函数指针的方式实现多态,所以Surface里面一定有设置Hook的地方,那就是在构造函数里。
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp): mGraphicBufferProducer(bufferProducer),mCrop(Rect::EMPTY_RECT),mBufferAge(0),mGenerationNumber(0),mSharedBufferMode(false),mAutoRefresh(false),mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),mSharedBufferHasBeenQueued(false),mQueriedSupportedTimestamps(false),mFrameTimestampsSupportsPresent(false),mEnableFrameTimestamps(false),mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {// Initialize the ANativeWindow function pointers.ANativeWindow::setSwapInterval = hook_setSwapInterval;ANativeWindow::dequeueBuffer = hook_dequeueBuffer;ANativeWindow::cancelBuffer = hook_cancelBuffer;ANativeWindow::queueBuffer = hook_queueBuffer;ANativeWindow::query = hook_query;ANativeWindow::perform = hook_perform;ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
至此,所有调用 ANativeWindow_xxx 的入口都会被导入 Surface 里的相应接口中,进入 hook_xxx。
int Surface::hook_dequeueBuffer(ANativeWindow* window,ANativeWindowBuffer** buffer, int* fenceFd) {Surface* c = getSelf(window);return c->dequeueBuffer(buffer, fenceFd);
}
进而在进入 Surface 的 xxx 函数。从 xxx 函数开始,和 graphic service 打交道就正式开始了。
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {ATRACE_CALL();status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,reqFormat, reqUsage, &mBufferAge,enableFrameTimestamps ? &frameTimestamps: nullptr);}
Graphic Buffer 服务
Surface 的 所有和 Graphic Buffer 相关的接口都会使用到成员 mGraphicBufferProducer
sp<IGraphicBufferProducer> mGraphicBufferProducer;
这个成员的类型一看就是一个 binder Interface,继续看 IGraphicBufferProducer。
\frameworks\native\libs\gui\include\gui\IGraphicBufferProducer.h
class IGraphicBufferProducer : public IInterface
{
public:using HGraphicBufferProducerV1_0 =::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer;using HGraphicBufferProducerV2_0 =::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;。。。virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,PixelFormat format, uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) = 0;。。。}
至此,一个binder 模型已经形成。
Surface::mGraphicBufferProducer as Bp <--IGraphicBufferProducer BINDER--> ? as Bn
那么这里的Bn是什么呢?只需要在源码里搜一下哪些类继承IGraphicBufferProducer 即可。其中一个常用的类就是 BufferQueueProducer。
\frameworks\native\libs\gui\include\gui\BufferQueueProducer.h
class BufferQueueProducer : public BnGraphicBufferProducer,private IBinder::DeathRecipient {。。。virtual status_t queueBuffer(int slot,const QueueBufferInput& input, QueueBufferOutput* output);
。。。}
继承自BnGraphicBufferProducer,Bn开头,一看就知道是server端。
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,uint32_t width, uint32_t height, PixelFormat format,uint64_t usage, uint64_t* outBufferAge,FrameEventHistoryDelta* outTimestamps) {sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,{mConsumerName.string(), mConsumerName.size()});status_t error = graphicBuffer->initCheck();}
在上面的代码里有 new GraphicBuffer 和 initCheck() 这两个动作,这里就是在创建管理对象并进行初始化检查。
内存管理
\frameworks\native\libs\ui\include\ui\GraphicBuffer.h
class GraphicBuffer: public ANativeObjectBase<ANativeWindowBuffer, GraphicBuffer, RefBase>,public Flattenable<GraphicBuffer>
{GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,uint32_t inUsage, std::string requestorName = "<Unknown>");}
\frameworks\native\libs\ui\GraphicBuffer.cpp
GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,uint32_t inLayerCount, uint64_t inUsage, std::string requestorName): GraphicBuffer() {mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,std::move(requestorName));
}
status_t GraphicBuffer::initWithSize(uint32_t inWidth, uint32_t inHeight,PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,std::string requestorName)
{GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();uint32_t outStride = 0;status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,inUsage, &handle, &outStride, mId,std::move(requestorName));if (err == NO_ERROR) {mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);width = static_cast<int>(inWidth);height = static_cast<int>(inHeight);format = inFormat;layerCount = inLayerCount;usage = inUsage;usage_deprecated = int(usage);stride = static_cast<int>(outStride);}return err;
}
上面创建 Graphic Buffer 的过程中会有一个 initWithSize , 这是初始化的过程,在初始化过程里又又 allocator.allocate,可见是一个内存分配的过程。
内存分配 GraphicBufferAllocator
\frameworks\native\libs\ui\include\ui\GraphicBufferAllocator.h
\frameworks\native\libs\ui\GraphicBufferAllocator.cpp
class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
{}
allocate 方法如下
status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,PixelFormat format, uint32_t layerCount, uint64_t usage,buffer_handle_t* handle, uint32_t* stride,uint64_t /*graphicBufferId*/, std::string requestorName)
{。。。status_t error =mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
。。。}
mAllocator 是什么 ?
sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
又是一个 Interface ,这次是 hardware 的,因此其实现是由硬件完成的。具体细节参考 /hardware/interfaces/graphics/allocator/2.0/
相关文章:
[Android] Graphic Buffer 的申请
前言: MediaCodec 支持 texture mode,即MediaCodec解码video完毕后把 yuv 数据填入 GPU 共享出来的 graphic buffer 里面,app 会把 video 的 yuv数据 和 ui 的数据通过通过软件渲染组件(opengl等)发送给GPU 进行一并渲染。这样做的效率较低&…...
【大数据学习 | HBASE高级】storeFile文件的合并
Compaction 操作分成下面两种: Minor Compaction:是选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,对于删除、过期、多余版本的数据不进行清除。 Major Compaction:是指将所有的StoreFile合并成一个StoreFile&am…...
多平台编包动态引入依赖的解决方案
最近开发时遇到了这样的需求,A 平台需要引入一个 video.js,B 平台却是不需要的,那么面向 B 平台打包的时候把依赖装进去自然就不大合适。最好的方法是动态引入依赖,根据平台来判断要不要引入 动态引入依赖 很快啊,动…...
[单例模式]
目录 [设计模式] 单例模式 1. 饿汉模式 2. 懒汉模式 3. 单例模式的线程安全问题 [设计模式] 设计模式是软件工程中的一种常见做法, 它可以理解为"模板", 是针对一些常见的特定场景, 给出的一些比较好的固定的解决方案. 不同语言适用的设计模式是不一样的. 这里…...
速盾:游戏盾的功能和原理详解
速盾有一款专注于网络游戏安全的防护系统,它通过实时监测游戏网络流量和玩家行为,以及使用先进的算法和技术进行分析和识别,检测出各种外挂、作弊行为和恶意攻击,从而保障游戏的公平性和玩家的安全性。 速盾游戏盾的主要功能包括…...
Spleeter:音频分离的革命性工具
目录 什么是Spleeter?Spleeter的工作原理Spleeter的应用场景Spleeter的技术优势Spleeter的挑战与局限性结论 什么是Spleeter? Spleeter 是一个由 Deezer 开发的开源音频源分离工具。它基于深度学习技术,尤其是卷积神经网络(CNN&a…...
【笔记】自动驾驶预测与决策规划_Part6_不确定性感知的决策过程
文章目录 0. 前言1. 部分观测的马尔可夫决策过程1.1 POMDP的思想以及与MDP的联系1.1.1 MDP的过程回顾1.1.2 POMDP定义1.1.3 与MDP的联系及区别POMDP 视角MDP 视角决策次数对最优解的影响 1.2 POMDP的3种常规解法1.2.1 连续状态的“Belief MDP”方法1. 信念状态的定义2. Belief …...
openresty入门教程:access_by_lua_block
在OpenResty中,access_by_lua_block 是一个功能强大的指令,它允许你在Nginx的访问控制阶段执行Lua脚本。这个阶段发生在Nginx处理请求的过程中,紧接在rewrite阶段之后,但在请求被传递到后端服务器(如PHP、Node.js等&am…...
Caused by: org.apache.flink.api.common.io.ParseException: Row too short:
Flink版本 1.17.2 错误描述 Caused by: org.apache.flink.api.common.io.ParseException: Row too short: 通过flink中的flinkSql直接使用对应的connector去获取csv文件内容,报获取的数据太短了 可能原因 1.创建的表字段多于csv文件当中的表头 定位 在获取csv…...
hbase的安装与简单操作
好的,这里是关于 HBase 的安装和基本操作的详细步骤,分成几个更清晰的阶段: 第一部分:安装和配置 HBase 1. 环境准备 HBase 依赖于 Hadoop,因此首先确保 Hadoop 已经正确安装和配置。如果没有安装,请先下…...
PySpark本地开发环境搭建
一.前置事项 请注意,需要先实现Windows的本地JDK和Hadoop的安装。 二.windows安装Anaconda 资源:Miniconda3-py38-4.11.0-Windows-x86-64,在window使用的Anaconda资源-CSDN文库 右键以管理员身份运行,选择你的安装路径&#x…...
【进阶】Stable Diffusion 插件 Controlnet 安装使用教程(图像精准控制)
Stable Diffusion WebUI 的绘画插件 Controlnet 最近更新了 V1.1 版本,发布了 14 个优化模型,并新增了多个预处理器,让它的功能比之前更加好用了,最近几天又连续更新了 3 个新 Reference 预处理器,可以直接根据图像生产…...
调试、发布自己的 npm 包
查看 npm 的配置 npm config ls登录 whoami 查看当前登录的用户 npm whoamiaduser 登录 adduser 有以下参数: –scope 作用域–registry 注册地址 默认地址:https://registry.npmjs.org/,也可通过.npmrc文件配置 npm login 是 …...
拓扑学与DNA双螺旋结构的奇妙连接:从算法到分子模拟
拓扑的形变指的是通过连续地拉伸、弯曲或扭曲物体而不进行撕裂或粘合来改变其形状的一种数学变换。拓扑形变属于拓扑学的一个分支,研究在这些操作下保持不变的性质。简单来说,它关注的是物体“形状的本质”,而不是具体的几何形状。 拓扑形变…...
mysql数据库(四)单表查询
单表查询 文章目录 单表查询一、单表查询1.1 简单查询1.2where1.3group by1.4having1.5order by1.6limit 一、单表查询 记录的查询语法如下: SELECT DISTINCT(去重) 字段1,字段2… FROM 表名 WHERE 筛选条件 GROUP BY 分组 HAVING 分组筛选 ORDER BY 排序 LIMIT 限…...
JavaEE初阶---properties类+反射+注解
文章目录 1.配置文件properities2.快速上手3.常见方法3.1读取配置文件3.2获取k-v值3.3修改k-v值3.4unicode的说明 4.反射的引入4.1传统写法4.2反射的写法(初识)4.3反射的介绍4.4获得class类的方法4.5所有类型的class对象4.6类加载过程4.7类初始化的过程4…...
HarmonyOS一次开发多端部署三巨头之功能级一多开发和工程级一多开发
功能级一多开发与工程级一多开发 引言功能级一多开发SysCaps机制介绍能力集canlUse接口 工程级一多开发三层架构规范 引言 一次开发多端部署 定义:一套代码工程,一次开发上架,多端按需部署 目标:支撑开发者快速高效的开发多终端设…...
STL常用遍历算法
概述: 算法主要是由头文件<algorithm> <functional> <numeric>组成。 <algorithm>是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric>体积很小,只包括几个在序列上面进行简…...
前端开发中常见的ES6技术细节分享一
var、let、const之间有什么区别? var: 在ES5中,顶层对象的属性和全局变量是等价的,用var声明的变量既是全局变量,也是顶层变量 注意:顶层对象,在浏览器环境指的是window对象,在 Node 指的是g…...
行业类别-智慧城市-子类别智能交通-细分类别自动驾驶技术-应用场景城市公共交通优化
1.大纲分析 针对题目“8.0 行业类别-智慧城市-子类别智能交通-细分类别自动驾驶技术-应用场景城市公共交通优化”的大纲分析,可以从以下几个方面进行展开: 一、引言 简述智慧城市的概念及其重要性。强调智能交通在智慧城市中的核心地位。引出自动驾驶…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...
