Android图像显示SurfaceFlinger总结

1 介绍

1.1 框架中位置

​​Android图像显示系统框架图
上图为Android的图形显示系统框架图。
首先上层应用通过ViewRoot的scheduleTraversals函数发起绘制任务,并通过HWUI调用OpenGL接口将绘制数据传递给GPU处理;SF会接收所有应用更新的绘制数据,并根据Z-Order、透明度、大小、位置等参数计算出每个应用图层在最终合成图像中的位置;SF完成图层处理后会把所有的应用图层提供给HWC,由HWC来决定这些图层的合成策略并调用屏显驱动做合成;最后屏显驱动会将合成的最终画面送到硬件屏幕显示。

Android系统通过buffer缓冲区来保存图形信息,这个buffer的内存空间是有SF向图形内存分配器Gralloc申请的,而Gralloc是通过ION Driver从kernel中开辟出一块共享内存。在整个图形显示流程中,buffer缓冲区会在App、SF、HWC之间来回流转。

1.2 职责

  • surfaceFlinger是一个服务进程
  • 负责管理应用程序窗口的创建、显示、更新和销毁等操作
  • 向上和WindowManager交互,向下和HwComposer交互。
    SF会接收所有应用更新的绘制数据,并根据Z-Order、透明度、大小、位置等参数计算出每个应用图层在最终合成图像中的位置;
    SF完成图层处理后会把所有的应用图层提供给HWC,由HWC来决定这些图层的合成策略并调用屏显驱动做合成

负责显示系统界面和应用程序的图形渲染工作。

  • 图像合成:SurfaceFlinger 接收来自多个应用程序和系统服务的图像缓冲区,根据它们的位置、大小、透明度、Z轴顺序等属性,将它们合成到一个最终的缓冲区中,然后发送到显示设备上 。
  • 管理 Surface:SurfaceFlinger 管理所有 Surface 对象,包括视频、图片等,并为每个 Surface 对象分配 BufferQueue(缓冲区队列),确保每个 Surface 都能按时完成显示 。
  • 三缓冲与 VSYNC 机制:SurfaceFlinger 采用双缓冲区机制,即前台和后台两个缓冲区,以及 VSYNC 机制,以提高渲染效率和质量,减少屏幕撕裂现象 。
  • 硬件加速:SurfaceFlinger 提供多种硬件加速技术,如 OpenGL ES、Vulkan 等,使应用程序能够更快地渲染 UI 界面 。
  • 窗口叠加与透明度支持:SurfaceFlinger 支持窗口叠加、透明度、混合模式等特性,以支持复杂的多层 UI 界面 。
  • 与 Hardware Composer 通信:SurfaceFlinger 与硬件抽象层 Hardware Composer 进行通信,利用硬件加速的方式来合成 Surface,提高性能和节省电量 。
  • 处理输入事件:SurfaceFlinger 中的 EventThread 方法负责处理输入事件,将输入事件分发给对应的窗口或 Surface 进行处理

2 源码分析

首先看下系统中SurfaceFlinger的进程
在这里插入图片描述
代码中入口:
SF是一个独立的可执行程序,路径是/system/bin/surfaceflinger,开机过程中init进程会解析surfaceflinger.rc,启动surfaceflinger进程。
frameworks/native/services/surfaceflinger/surfaceflinger.rc,

service surfaceflinger /system/bin/surfaceflingerclass core animationuser systemgroup graphics drmrpc readproccapabilities SYS_NICEonrestart restart --only-if-running zygotetask_profiles HighPerformancesocket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0on property:vendor.debug.sf.restart=1restart surfaceflingeron property:init.svc.zygote=restarting && property:debug.sf.toomanylayers=1restart surfaceflingersetprop debug.sf.toomanylayers "0"

2.1 SurfaceFlinger的初始化

init.rc中调用到SF的main函数,这里是SF进程的入口。
路径:frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

int main(int, char**) {startGraphicsAllocatorService(); //启动图形服务...sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger(); //1. 创建SF实例flinger->init(); //2. 对SF实例进行初始化// publish surface flinger//3. 将SF添加到serviceManager中。 这样客户端通过getService()+SurfaceFlinger的名称,// 就可以获取SF的远程代理对象,进而和SF服务跨进程通信//static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } @SurfaceFinger.hsp<IServiceManager> sm(defaultServiceManager());sm->addService(String16(SurfaceFlinger::getServiceName()),  flinger,  false,IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);// publish gui::ISurfaceComposer, the new AIDL interface//4. 新建SurfaceComposerAIDL对象,注册到serviceManager中sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger);sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);// 启动Display服务startDisplayService(); // dependency on SF getting registered above...// run surface flinger in this threadflinger->run(); //5. 启动SF线程
}

2.2 SurfaceFlinger构造函数详解

先来看下SurfaceFlinger的类关系图
//frameworks/native/services/surfaceflinger/SurfaceFlinger.h
在这里插入图片描述
ISurfaceComposer的接口定义:
它的职责是:

class ISurfaceComposer: public IInterface {
public:DECLARE_META_INTERFACE(SurfaceComposer)static constexpr size_t MAX_LAYERS = 4096;...//Create a connection with SurfaceFlinger.virtual sp<ISurfaceComposerClient> createConnection() = 0;...

2.2.1 createSurfaceFlinger详解

接下来看下surfaceflinger::createSurfaceFlinger()具体做了什么?
新建一个SurfaceFlinger实例,将DefaultFactory 做为参数传入,其中DefaultFactory继承了surfaceflinger::Factory类。
主要做了什么?

  • 创建CompositionEngine
  • 添加了WindowInfosListenerInvoker回调
//frameworks/native/services/surfaceflinger/SurfaceFlingerFactory.cpp
sp<SurfaceFlinger> createSurfaceFlinger() {static DefaultFactory factory;return new SurfaceFlinger(factory);
}SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag): mFactory(factory),mPid(getpid()),mInterceptor(mFactory.createSurfaceInterceptor()),mTimeStats(std::make_shared<impl::TimeStats>()),mFrameTracer(mFactory.createFrameTracer()),mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),mCompositionEngine(mFactory.createCompositionEngine()), //创建CompositionEngine...//添加了WindowInfosListenerInvoker回调mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) {ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
}
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
//SurfaceFlinger构造函数中对一些成员变量进行赋值初始化。
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {ALOGI("SurfaceFlinger is starting");hasSyncFramework = running_without_sync_framework(true);dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);...
}

2.2.2 SurfaceFlinger的init详解

flinger->init()对SF实例进行初始化,具体做了什么?

void SurfaceFlinger::init() {ALOGI(  "SurfaceFlinger's main thread ready to run. ""Initializing graphics H/W...");Mutex::Autolock _l(mStateLock);//构造renderengine的builderauto builder = renderengine::RenderEngineCreationArgs::Builder().setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat)).setImageCacheSize(maxFrameBufferAcquiredBuffers).setUseColorManagerment(useColorManagement).setEnableProtectedContext(enable_protected_contents(false)).setPrecacheToneMapperShaderOnly(false).setSupportsBackgroundBlur(mSupportsBlur).setContextPriority(useContextPriority? renderengine::RenderEngine::ContextPriority::REALTIME: renderengine::RenderEngine::ContextPriority::MEDIUM);...//创建renderEngine,并给mCompositionEngine设置RenderEnginemCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));...//调用mCompositionEngine来createHWComposer,给mCompositionEngine设置回调函数。思考:这里回调是什么接口?作用是?mCompositionEngine->setTimeStats(mTimeStats);mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));mCompositionEngine->getHwComposer().setCallback(*this);...// Process any initial hotplug and resulting display changes.processDisplayHotplugEventsLocked(); //重要调用,第一次会做一次插拔检测const auto display = getDefaultDisplayDeviceLocked();LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");const auto displayId = display->getPhysicalId();LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),  "Primary display is disconnected.");// initialize our drawing statemDrawingState = mCurrentState; //更新drawState, 思考:其中共有几种状态?都是什么时候切换的?initializeDisplays();...ALOGV("Done initializing");
}

2.2.3 processDisplayHotplugEventsLocked详解

//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::processDisplayHotplugEventsLocked() {for (const auto& event : mPendingHotplugEvents) {std::optional<DisplayIdentificationInfo> info =getHwComposer().onHotplug(event.hwcDisplayId, event.connection);if (!info) { continue; }//获取displayId和token const auto displayId = info->id;const auto token = mPhysicalDisplayTokens.get(displayId);if (event.connection == hal::Connection::CONNECTED) { //如果此时有显示屏幕连接... //} else { //如果此时没有显示屏幕连接ALOGV("Removing display %s", to_string(displayId).c_str());if (const ssize_t index = mCurrentState.displays.indexOfKey(token->get()); index >= 0) {const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);mInterceptor->saveDisplayDeletion(state.sequenceId);mCurrentState.displays.removeItemsAt(index);}mPhysicalDisplayTokens.erase(displayId);}processDisplayChangesLocked(); //主要步骤,处理显示改变}mPendingHotplugEvents.clear();
}
//
void SurfaceFlinger::processDisplayChangesLocked() {const KeyedVector<wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);if (!curr.isIdenticalTo(draw)) {mVisibleRegionsDirty = true;// find the displays that were removed (ie: in drawing state but not in current state)// also handle displays that changed (ie: displays that are in both lists)for (size_t i = 0; i < draw.size(); i++) {const wp<IBinder>& displayToken = draw.keyAt(i);const ssize_t j = curr.indexOfKey(displayToken);if (j < 0) {// in drawing state but not in current stateprocessDisplayRemoved(displayToken);} else {// this display is in both lists. see if something changed.const DisplayDeviceState& currentState = curr[j];const DisplayDeviceState& drawingState = draw[i];processDisplayChanged(displayToken, currentState, drawingState); //核心步骤,调用processDisplayAdded}}...}mDrawingState.displays = mCurrentState.displays;
}

2.2.4 processDisplayAdded详解

主要做了几件事:
1)通过CompositionEngine创建display
2)创建BufferQueue 和 DisplaySurface对象
3)调用initscheduler来初始化整个Vsync系统

void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,const DisplayDeviceState& state) {compositionengine::DisplayCreationArgsBuilder builder; builder.setPixels(resolution);builder.setIsSecure(state.isSecure);builder.setPowerAdvisor(mPowerAdvisor.get());builder.setName(state.displayName);// 1. 通过CompositionEngine创建display,具体display参数通过builder设置auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);sp<compositionengine::DisplaySurface> displaySurface;sp<IGraphicBufferProducer> producer; //为什么有producer 和BqProducer,两者有什么区别?sp<IGraphicBufferProducer> bqProducer;sp<IGraphicBufferConsumer> bqConsumer;//2. 创建BufferQueuegetFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);//3. 创建DisplaySurface对象if (state.isVirtual()) {const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,bqProducer, bqConsumer, state.displayName);displaySurface = surface;producer = std::move(surface);} else {const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());displaySurface = sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,state.physical->activeMode->getResolution(),ui::Size(maxGraphicsWidth, maxGraphicsHeight));producer = bqProducer;}...auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state,displaySurface, producer);if (display->isPrimary()) {initScheduler(display); //主要步骤,调用initscheduler来初始化整个Vsync系统}mDisplays.try_emplace(displayToken, std::move(display));
}

2.2.5 initScheduler详解

initscheduler初始化整个Vsync系统,即对Vsync产生机制初始化,主要做几件事:
1)如果没有mScheduler ,创建mScheduler,有的话直接使用
2)创建APP连接
3)创建appSf连接
4)初始化Vsync

void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {if (mScheduler) {// If the scheduler is already initialized, this means that we received// a hotplug(connected) on the primary display. In that case we should// update the scheduler with the most recent display information.ALOGW("Scheduler already initialized, updating instead");mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());return;}//如果没有mScheduler ,创建mSchedulermScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),static_cast<ISchedulerCallback&>(*this),features);{auto configs = display->holdRefreshRateConfigs();if (configs->kernelIdleTimerController().has_value()) {features |= Feature::kKernelIdleTimer;}mScheduler->createVsyncSchedule(features);mScheduler->setRefreshRateConfigs(std::move(configs));}setVsyncEnabled(false);mScheduler->startTimers();const auto configs = mVsyncConfiguration->getCurrentConfigs();const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();//创建APP连接,它用于连接生产者和消费者,相互通信,回调消息。 思考:这里的生产者和消费者分别是?mAppConnectionHandle =  mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),/*workDuration=*/configs.late.appWorkDuration,/*readyDuration=*/configs.late.sfWorkDuration,impl::EventThread::InterceptVSyncsCallback());//创建appSf连接,它用于连接生产者和消费者,相互通信,回调消息。mSfConnectionHandle = mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),/*readyDuration=*/configs.late.sfWorkDuration,[this](nsecs_t timestamp) {mInterceptor->saveVSyncEvent(timestamp);});//初始化Vsync,initVsync主要作用是绑定一个回调函数MessageQueue::vsyncCallback 到VSyncDispatch上,回调名字"sf"mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),configs.late.sfWorkDuration);mFpsReporter = new FpsReporter(*mFrameTimeline, *this); mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
}

思考几个问题:
1)创建mScheduler的用处?
他是一个调度器,是SF的重要部分之一,负责协调和优化渲染流程,以确保流畅的图形更新和响应。

2)mScheduler->createConnection()具体做了什么?做什么用?
分别创建APP和appSf的连接,它用于连接生产者和消费者,相互通信,回调消息。 思考:这里的生产者和消费者分别是?

3)mScheduler->initVsync()做了什么?
initVsync主要作用是绑定一个回调函数 MessageQueue::vsyncCallback 到VSyncDispatch上,回调名字"sf"

2.2.6 mScheduler详解

职责:Scheduler是SurfaceFlinger的调度器, 管理和调度与屏幕渲染相关的任务。
SurfaceFlinger 是 Android 图形系统的核心组件之一,负责合成显示到屏幕上的各种图形层。Scheduler 作为其重要部分,负责协调和优化渲染流程,以确保流畅的图形更新和响应。

Scheduler 的主要作用包括:

  • 任务调度:
    Scheduler 负责安排不同的渲染任务的执行顺序。它确定何时执行图形合成、缓冲区交换等操作,以优化性能并减少延迟。
  • 帧同步:
    确保图形渲染与显示的刷新率同步。Scheduler 调整渲染的时间,以保持帧率稳定,减少撕裂和卡顿现象。它通常会利用硬件的垂直同步(V-Sync)信号来确保每一帧都能准确地显示在屏幕上。
  • 时间片管理:
    Scheduler 处理与时间相关的任务,如管理和分配每个任务的时间片。它负责在合成图层时进行时间上的协调,确保每一层图形在正确的时间被渲染。

围绕上面三个问题,结合代码看下mScheduler做了什么?

  • mScheduler定义
    1)类关系图
    Scheduler继承了impl::MessageQueue类,类关系图:
    在这里插入图片描述

2)主要接口

  • createConnection()
    用于创建一个指定名字的连接,它用于连接生产者和消费者,相互通信,回调消息。
ConnectionHandle Scheduler::createConnection(const char* connectionName, frametimeline::TokenManager* tokenManager,std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,impl::EventThread::InterceptVSyncsCallback interceptCallback) {//创建DispSyncSource对象auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);auto throttleVsync = makeThrottleVsyncCallback();auto getVsyncPeriod = makeGetVsyncPeriodFunction();//创建EventThread的对象,将传入的参数传给EventThread构造函数中auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),  tokenManager,std::move(interceptCallback),std::move(throttleVsync),std::move(getVsyncPeriod));return createConnection(std::move(eventThread));
}
//创建EventThreadConnection并加入到mConnections中
ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++}; //获取连接handleauto connection = createConnectionInternal(eventThread.get());std::lock_guard<std::mutex> lock(mConnectionsLock);//将handle和EventThreadConnection对象一起保存到mConnections中mConnections.emplace(handle, Connection{connection, std::move(eventThread)});return handle; // 返回handle
}
//创建并返回EventThreadConnection对象
sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
}

EventThread的构造函数做了什么?
1)给mVSyncSource设置回调函数
2)创建主线程
3)提高主线程优先级

EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,android::frametimeline::TokenManager* tokenManager,InterceptVSyncsCallback interceptVSyncsCallback,ThrottleVsyncCallback throttleVsyncCallback,GetVsyncPeriodFunction getVsyncPeriodFunction): mVSyncSource(std::move(vsyncSource)),mTokenManager(tokenManager),mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),mThrottleVsyncCallback(std::move(throttleVsyncCallback)),mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),mThreadName(mVSyncSource->getName()) {mVSyncSource->setCallback(this); //EventThread继承了VSyncSource::Callback,用EventThread给mVSyncSource设置回调函数//回调函数接口是onVSyncEvent(nsecs_t when, VSyncData vsyncData)//创建主线程,同时阻塞等待mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {std::unique_lock<std::mutex> lock(mMutex);threadMain(lock);});//设置线程优先级为SP_FOREGROUNDpthread_setname_np(mThread.native_handle(), mThreadName);pid_t tid = pthread_gettid_np(mThread.native_handle());...set_sched_policy(tid, SP_FOREGROUND);
}

EventThread类关系图
在这里插入图片描述

createEventConnection()做了什么?新建一个EventThreadConnection对象,设置eventThread、resyncCallback和eventRegistration三个参数。

sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback,ISurfaceComposer::EventRegistrationFlags eventRegistration) const {return new EventThreadConnection(const_cast<EventThread*>(this),IPCThreadState::self()->getCallingUid(),std::move(resyncCallback), eventRegistration);
}
  • initVsync()
    绑定一个回调函数 MessageQueue::vsyncCallback到VsyncDispatch上,回调名字是"sf"。
    通过adb shell dumpsys SurfaceFlinger > sf.txt命令可以查看VsysncDispatch上绑定了那些callbacks?
VsyncDispatch:Timer:DebugState: WaitingmTimerSlack: 0.50ms mMinVsyncDistance: 3.00msmIntendedWakeupTime: 9223078412288.00ms from nowmLastTimerCallback: 2553.19ms ago mLastTimerSchedule: 2554.64ms agoCallbacks:sf:  workDuration: 27.60ms readyDuration: 0.00ms earliestVsync: -2655.90ms relative to nowmLastDispatchTime: 2525.89ms agoappSf:  workDuration: 10.00ms readyDuration: 27.60ms earliestVsync: -31929.41ms relative to nowmLastDispatchTime: 31929.41ms agoapp:  workDuration: 20.00ms readyDuration: 27.60ms earliestVsync: -2648.97ms relative to nowmLastDispatchTime: 2648.97ms ago
void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,frametimeline::TokenManager& tokenManager,std::chrono::nanoseconds workDuration) {setDuration(workDuration);//给mVsync赋值,其中registration 是VSyncCallbackRegistration,MessageQueue::vsyncCallback在MessageQueue中定义的回调函数,//	bind()是将MessageQueue的成员函数(vsyncCallback)与参数绑定,生成新的可调用对象mVsync.tokenManager = &tokenManager;mVsync.registration = std::make_unique<scheduler::VSyncCallbackRegistration>(dispatch,std::bind(&MessageQueue::vsyncCallback, this, //this表示MessageQueue对象std::placeholders::_1,std::placeholders::_2,std::placeholders::_3),"sf"); //这里将sf名字的回调函数绑定到VsyncDispatch上
}

3 小结

本文介绍了SF的职责,它是一个服务进程,负责显示系统界面和应用程序的图形渲染工作。

  • 负责管理应用程序窗口的创建、显示、更新和销毁等操作
  • 向上和WindowManager交互,向下和HwComposer交互。
    SF会接收所有应用更新的绘制数据,并根据Z-Order、透明度、大小、位置等参数计算出每个应用图层在最终合成图像中的位置;
    SF完成图层处理后会把所有的应用图层提供给HWC,由HWC来决定这些图层的合成策略并调用屏显驱动做合成
    同时讲述了SurfaceFlinger的进程启动入口和初始化流程,涉及到Vsync系统的初始化(mScheduler做的事情)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/393335.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络(网络层)

网络层概述 网络层是干什么的&#xff1f; 网络层的主要任务是实现不同异构网络互连&#xff0c;进而实现数据包在各网络之间的传输相比于数据链路层的以太网通信&#xff0c;网络层则是将一个个数据链路层连接的以太网通过路由器连接起来。从而实现不同数据链路层的互联。 这…

​【香菇带你学Mysql】Mysql超长执行sql定位和优化【建议收藏】

本文为MySQL数据库管理员和开发人员提供了一套全面的超时SQL定位和优化解决方案。通过合理运用这些方法和技巧&#xff0c;可以显著提升MySQL数据库的性能和稳定性&#xff0c;减少超时SQL语句的发生&#xff0c;确保数据库的高效运行。 0. 引言 最近某个Mysql数据库频繁告警…

登录页滑块验证图

效果图 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> <b…

【Kubernetes】k8s集群中pod的容器资源限制和三种探针

目录 一.关于pod容器的资源限制 1.资源限制的单位 1.1.CPU 资源单位 1.2.内存 资源单位 二.关于QOS服务质量&#xff08;pod的调度和驱逐有限制&#xff09; 1.QoS服务质量分类 2.驱逐顺序 三.关于pod容器的三种探针 1.探针的三种规则 2.Probe支持三种检查方法 3.探…

docker安装及使用

一、docker优点及作用 优点&#xff1a; 基础镜像MB级别创建简单隔离性强启动速度秒级移植与分享放便 作用&#xff1a;资源隔离 cpu、memory资源隔离与限制访问设备隔离与限制网络隔离与限制用户、用户组隔离限制 二、docker安装 2.1.配置yum源 yum install -y yum-uti…

Mysql开启SSL

等二测出未开启SSL,如下 have_openssl、have_ssl都是DISABLED也不知道当时为啥没开&#xff0c;看最近的都是开启的,整改必去得开了&#xff0c;开启步骤 1.生成秘钥 进入mysql的bin目录下&#xff0c;运行 ./mysql_ssl_rsa_setup运行后会生成证书 默认证书会在mysql的data…

主从备份(复制)

一、备份的三种类型 备份的三种主要类型包括热备份、逻辑备份和物理备份&#xff0c;每种备份类型都有其特定的应用场景和优缺点。 1. 热备份 定义&#xff1a; 热备份是在数据库或系统处于正常运行状态下进行的备份。这种备份方式允许在不停机的情况下对数据库或系统数据进…

【Python】Django Web 框架

一、常用的Web开发框架 1.Django Django是一个由Python写成的开放源代码的Web应用框架。这套框架的主要目标是使开发复杂、数据库驱动的网站变得简单。Django注重组件的重用性和“可拔插性”、敏捷开发和DRY(Dont Repeat Yourself)法则 2.Flask Flask是一个微型的Python开发…

反序列化靶机实战serial(保姆级教程)

一.信息收集 靶机地址下载&#xff1a;https://download.vulnhub.com/serial/serial.zip 打开靶机&#xff0c;在kali虚拟机中进行主机存活探测 可以知道靶机ip地址为192.168.133.171 然后扫描端口 可以发现有一个22端口跟80端口 然后接下来用kali扫描它的目录 可以发现有一…

Django-Oscar开发独立站/外贸商城教程与问题记录

​特别说明&#xff1a; 本博客为个人开发Django-Oscar时的经验总结&#xff0c;方便后期维护&#xff01;&#xff08;第一次这么认真的记录这种大型项目&#xff0c;打个广告吧&#xff1a;本人可接单算法程序开发&#xff0c;包含深度学习和图像相关……等相关&#xff09;…

Unity补完计划 之 音效

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 首先&#xff0c;音频这块组件较少&#xff0c;但是内容很重要&#xff0c;因为对于任何一款非特殊面向人群的游戏来说&a…

STM32入门三(开漏输出点亮外接的LED)

前面2章用的是推免输出&#xff0c; 推免输出: 输出端由两个晶体管构成&#xff1a;一个N沟道晶体管和一个P沟道晶体管。这两个晶体管一般不会同时导通&#xff0c;避免短路; 白话&#xff0c;就是输入高还是低&#xff0c;由你的GPIO 控制&#xff08;GPIO 输出高就高&#xf…

【LeetCode 1991 找到数组的中间位置 / LeetCode 724 寻找数组的中心下标】中间索引问题

1991 题目描述 暴力解法1&#xff1a; 思路&#xff1a; 遍历下标&#xff0c;求出左边和和右边和比较两边是否相等相等直接返回值没有符合的返回 -1 class Solution {public int findMiddleIndex(int[] nums) {int lennums.length;//初始化一个变量 midIndex 为 -1&#xff…

C# Unity 面向对象补全计划 七大原则 之 接口隔离原则 (ISP) 难度:☆ 总结:大接口分成小的,然后该干啥干啥

本文仅作学习笔记与交流&#xff0c;不作任何商业用途&#xff0c;作者能力有限&#xff0c;如有不足还请斧正 本系列作为七大原则和设计模式的进阶知识&#xff0c;看不懂没关系 请看专栏&#xff1a;http://t.csdnimg.cn/mIitr&#xff0c;查漏补缺 1.接口隔离原则 (ISP) 这…

MySQL--查询数据

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。其基本格式为&#xff1a; select {* | <字段列表>}[from <表1>,&l…

贝壳找房:基于OceanBase构建实时字典服务的实践 | OceanBase案例

贝壳找房作为领先的居住服务综合平台&#xff0c;一直在推进居住产业的数字化与智能化升级。该平台通过汇聚并赋能优质的服务者&#xff0c;旨在为中国广大家庭带来涵盖二手房买卖、新房交易、房屋租赁、家装、家居以及家庭服务等全方位、高质量且高效的居住服务体验。 在贝壳…

0803实操-数字取证

0803实操-数字取证 易失性数据收集 创建应急工具箱&#xff0c;并生成工具箱校验和&#xff0c;能在最低限度地改变系统状态的情况下收集易失性数据。 数据箱 使用md5sums.exe对工具目录中的所有文件进行计算 获取计算机本地日期和时间。输入命令date/t>timefront.txt和…

鸿蒙图形开发【3D引擎接口示例】

介绍 本实例主要介绍3D引擎提供的接口功能。提供了ohos.graphics.scene中接口的功能演示。 3D引擎渲染的画面会被显示在Component3D这一控件中。点击按钮触发不同的功能&#xff0c;用户可以观察渲染画面的改变。 效果预览 使用说明 在主界面&#xff0c;可以点击按钮进入不…

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——4Bin模型转化过程

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——4Bin模型转化过程 ​ 大家好&#xff0c;经过前几期的介绍&#xff0c;对于X3派上的Yolo模型部署&#xff0c;我们已经可以进行到最后一步了 ​ 今天给大家带来&#xff0c;转模型的关键步骤&#xff0…

学习进行到了第十七天(2024.8.5)

1.Mybatis的定义 数据持久化是将内存中的数据模型转换为存储模型&#xff0c;以及将存储模型转换为内存中数据模型的统称。例如&#xff0c;文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作。MyBatis 支持定制化 SQL、存储过程以及高级映射&#xff0c;可以…