当前位置: 首页 > news >正文

【Android】Android Framework系列---CarPower深度睡眠STR

Android Framework系列—CarPower深度睡眠

之前博客说了CarPower的开机启动流程
这里分析一下,Android CarPower实现深度睡眠的流程。
首先,什么是深度睡眠(Deep Sleep)?

Android进入Deep Sleep后,关闭屏幕、关闭CPU的电源,保持RAM的电源(激活状态)。深度睡眠会进行Suspend-to-RAM挂起到内存(做车载的经常会听到的STR)。当然深度睡眠,还一种是挂起到硬盘(Suspend-to-Disk),不过目前主要用的是STR。所以STR是深度睡眠时的状态,但STR不等于深度睡眠。

DeepSleep或STR,需要内核配合实现。通过这种技术,车载系统可以进入低能耗模式,同时又可以再用户需要时实现快速(非常快,因为从内存中恢复状态)的启动。
感觉这种方式,跟snapshot快启动有点类似。不过是两个概念上的事情了。

下面摘自Android官网对这种状态的介绍。

也称为“挂起到 RAM”(S2R 或 STR)。将 SoC 置于 S3 电源模式,CPU 断电而 RAM 仍通电。

VMCU 应将 SoC 置于“深度睡眠”状态,并断开应用处理器的电源。然后,AAOS 将进入“挂起到 RAM”状态,但不执行任何代码。

整套深度睡眠的实现,涉及的主要模块(这里指单Android系统。当前还有Qnx之类的hypervisor)

  • MCU/VMCU:控制电源,发送电源状态给SOC(Android)
  • Vehicle HAL:一般由硬件供应商实现,与MCU通信(can、以太方式)
  • CarPowerManagerService(CPMS): 实现Android侧的电源控制,应用电源策略。
  • CarPowerManager(CPM): CPMS的客户端,通知电源状态给应用,等待应用回复状态等。

下面是摘自Android官网的深入睡眠流程描述;

只有 VMCU 可以启动深度睡眠。启动深度睡眠后,VMCU 会通过 VHAL 向 CPMS 发送通知。CPMS 通过使用由 CPM 提供的新状态 ID 调用 onStateChanged() 方法,将状态更改为“关闭准备”并向所有观察者(监控 CPMS 的应用和服务)广播此状态转换。

CPM 在应用/服务与 CPMS 之间进行协调。在 CPM 的 onStateChanged() 方法中,同步调用应用/服务的
onStateChanged() 方法。大多数应用和服务需要完成其准备后才能从该调用返回。特权服务可以在为
PRE_SHUTDOWN_PREPARE、SUSPEND_ENTER 和 POST_SUSPEND_ENTER
返回后,异步继续关闭准备。在此情况下,特权服务应在完成准备时对提供的 CompletablePowerStateChangeFuture
对象调用 complete()。请注意,SHUTDOWN_PREPARE 不允许异步准备。在向 VHAL 发送
DEEP_SLEEP_ENTRY 之前,CPMS 会定期向 VHAL 发送推迟关闭的请求。

当所有 CPM 对象完成关闭准备后,CPMS 会向 VHAL 发送 AP_POWER_STATE_REPORT,VHAL 随后通知
VMCU,告知它 AP 已准备好挂起。CPMS 还会调用其挂起方法以挂起内核。

下面的图,是Android官网对于深度睡眠的时序图。这里贴在这里。后面会根据源码,分析并画一个跟代码对应的时序图。
Android官网深入睡眠流程

CPMS深度睡眠代码实现

  • 到这里,我们开始分析CPMS的实现。代码基于Android11

首先,VMCU请求挂起。Vehicle HAL (Hal层的nativeservice)发送“AP_POWER_STATE_REQ(SHUTDOWN_PREPARE)”, 这样CarService中的 VehicleHal(Client端 )就收到了这个通知。一路通知给CPMS进行状态处理。

  • packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {// 省略for (HalServiceBase s : mServicesToDispatch) {//  通知给监听者s.onHalEvents(s.getDispatchList());s.getDispatchList().clear();}mServicesToDispatch.clear();
}
  • packages/services/Car/service/src/com/android/car/hal/PowerHalService.java
@Override
public void onHalEvents(List<VehiclePropValue> values) {// 省略dispatchEvents(values, listener);
}private void dispatchEvents(List<VehiclePropValue> values, PowerEventListener listener) {for (VehiclePropValue v : values) {switch (v.prop) {case AP_POWER_STATE_REPORT:// Ignore this property event. It was generated inside of CarService.break;case AP_POWER_STATE_REQ:int state = v.value.int32Values.get(VehicleApPowerStateReqIndex.STATE);int param = v.value.int32Values.get(VehicleApPowerStateReqIndex.ADDITIONAL);Log.i(CarLog.TAG_POWER, "Received AP_POWER_STATE_REQ="+ powerStateReqName(state) + " param=" + param);// 通知状态给CPMS对象// AP_POWER_STATE_REQ(SHUTDOWN_PREPARE, CAN_SLEEP)listener.onApPowerStateChange(new PowerState(state, param));break;case DISPLAY_BRIGHTNESS:{// 省略}break;}}
}
  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
@Override
public void onApPowerStateChange(PowerState state) {synchronized (mLock) {mPendingPowerStates.addFirst(new CpmsState(state));mLock.notify();}mHandler.handlePowerStateChange();
}private void handlePowerStateChange() {Message msg = obtainMessage(MSG_POWER_STATE_CHANGE);sendMessage(msg);
}@Override
public void handleMessage(Message msg) {CarPowerManagementService service = mService.get();if (service == null) {Slog.i(TAG, "handleMessage null service");return;}switch (msg.what) {case MSG_POWER_STATE_CHANGE:// 开始处理//  service是doHandlePowerStateChange对象service.doHandlePowerStateChange();break;// 省略}
}private void doHandlePowerStateChange() {// 省略了mCurrentState = state;switch (state.mState) {case CpmsState.WAIT_FOR_VHAL:handleWaitForVhal(state);break;case CpmsState.ON:handleOn();break;case CpmsState.SHUTDOWN_PREPARE:// 处理SHUTDOWN_PREPAREhandleShutdownPrepare(state);break;// 省略default:// Illegal state// TODO:  Throw exception?break;}
}

CarPowerManagementService开始处理SHUTDOWN_PREPARE。主要是关闭VR交互、关闭屏幕。通知状态给CPMS的客户端,启动超时等待客户端处理完成(使用****CarPowerStateListenerWithCompletion的客户端)

  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
private void handleShutdownPrepare(CpmsState newState) {setVoiceInteractionDisabled(true);mSystemInterface.setDisplayState(false);// Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.// 通知状态给客户端sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE);mHal.sendShutdownPrepare();doHandlePreprocessing();
}private void doHandlePreprocessing() {int intervalMs;int pollingCount;synchronized (mLock) {intervalMs = mShutdownPollingIntervalMs;pollingCount = (mShutdownPrepareTimeMs / mShutdownPollingIntervalMs) + 1;}if (Build.IS_USERDEBUG || Build.IS_ENG) {// 这里省略了,主要是可以通过属性,修改超时时间。}Slog.i(TAG, "processing before shutdown expected for: "+ mShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);// 启动超时任务,定期检查。那些使用 CarPowerStateListenerWithCompletion//(带有future,告知CPMS自己是否处理完了)的应用端是否处理完毕。// 如果超时还没完成,强制走DeepSleep流程。synchronized (mLock) {mProcessingStartTime = SystemClock.elapsedRealtime();releaseTimerLocked();mTimer = new Timer();mTimerActive = true;mTimer.scheduleAtFixedRate(new ShutdownProcessingTimerTask(pollingCount),0 /*delay*/,intervalMs);}if (mSwitchGuestUserBeforeSleep) {switchToNewGuestIfNecessary();}
}// 超时处理Task
private class ShutdownProcessingTimerTask extends TimerTask {private final int mExpirationCount;private int mCurrentCount;private ShutdownProcessingTimerTask(int expirationCount) {mExpirationCount = expirationCount;mCurrentCount = 0;}@Overridepublic void run() {synchronized (mLock) {if (!mTimerActive) {// Ignore timer expiration since we got cancelledreturn;}mCurrentCount++;if (mCurrentCount > mExpirationCount) {// 超时处理。给Vehicle HAL通知DEEP_SLEEP_ENTRY,告知可以开始进入深入睡眠状态。PowerHandler handler;releaseTimerLocked();handler = mHandler;handler.handleProcessingComplete();} else {// 定期通过PowerServiceHAL向Vehicle HAL发送延时通知。mHal.sendShutdownPostpone(SHUTDOWN_EXTEND_MAX_MS);}}}
}

这里假设走正常的处理了,即所有客户端(CarPowerStateListenerWithCompletion)期限内完成了处理。那么CPMS,就会通过PowerserviceHAL告知 VehicleHAL “DEEP_SLEEP_ENTRY”状态。下面是CarPowerStateListenerWithCompletion的一个例子。

mCarPowerManager.setListenerWithCompletion(mCarPowerStateListener);
private final CarPowerStateListenerWithCompletion mCarPowerStateListener =new CarPowerStateListenerWithCompletion() {@Overridepublic void onStateChanged(int state, CompletableFuture<Void> future) {if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) {// 例子,应用端处理完成后,调用future.complete 告知CPMS处理完成if (future != null) {future.complete(null);}}}
};
  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
// future.complete 会触发CPMS这个函数。
@Override
public void finished(ICarPowerStateListener listener) {ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);ICarImpl.assertCallingFromSystemProcessOrSelf();finishedImpl(listener.asBinder());
}private void finishedImpl(IBinder binder) {boolean allAreComplete;synchronized (mLock) {mListenersWeAreWaitingFor.remove(binder);allAreComplete = mListenersWeAreWaitingFor.isEmpty();}// 所有Listener处理完成了。if (allAreComplete) {signalComplete();}
}private void signalComplete() {if (mCurrentState.mState == CpmsState.SHUTDOWN_PREPARE|| mCurrentState.mState == CpmsState.SIMULATE_SLEEP) {PowerHandler powerHandler;// All apps are ready to shutdown/suspend.synchronized (mLock) {if (!mShutdownOnFinish) {if (mLastSleepEntryTime > mProcessingStartTime&& mLastSleepEntryTime < SystemClock.elapsedRealtime()) {Slog.i(TAG, "signalComplete: Already slept!");return;}}powerHandler = mHandler;}Slog.i(TAG, "Apps are finished, call handleProcessingComplete()");powerHandler.handleProcessingComplete();}
}private void handleProcessingComplete() {removeMessages(MSG_PROCESSING_COMPLETE);Message msg = obtainMessage(MSG_PROCESSING_COMPLETE);sendMessage(msg);
}@Override
public void handleMessage(Message msg) {CarPowerManagementService service = mService.get();if (service == null) {Slog.i(TAG, "handleMessage null service");return;}switch (msg.what) {case MSG_POWER_STATE_CHANGE:service.doHandlePowerStateChange();break;case MSG_DISPLAY_BRIGHTNESS_CHANGE:service.doHandleDisplayBrightnessChange(msg.arg1);break;case MSG_MAIN_DISPLAY_STATE_CHANGE:service.doHandleMainDisplayStateChange((Boolean) msg.obj);break;case MSG_PROCESSING_COMPLETE:// 走这里service.doHandleProcessingComplete();break;}
}private void doHandleProcessingComplete() {int listenerState;synchronized (mLock) {releaseTimerLocked();if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {// entered sleep after processing start. So this could be duplicate request.Slog.w(TAG, "Duplicate sleep entry request, ignore");return;}// 可以设置关机,即不进入挂起。// 默认状态,走SUSPEND_ENTERlistenerState = mShutdownOnFinish? CarPowerStateListener.SHUTDOWN_ENTER : CarPowerStateListener.SUSPEND_ENTER;}// 处理状态变化(这个函数是两参的,并且是private的)onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, listenerState);
}private void onApPowerStateChange(int apState, int carPowerStateListenerState) {CpmsState newState = new CpmsState(apState, carPowerStateListenerState);synchronized (mLock) {mPendingPowerStates.addFirst(newState);mLock.notify();}// 用 handler线程处理。下面省略了中间流程,最终调用的是doHandlePowerStateChangemHandler.handlePowerStateChange();
}private void doHandlePowerStateChange() {mCurrentState = state;switch (state.mState) {// 省略case CpmsState.WAIT_FOR_FINISH:// 走这里handleWaitForFinish(state);break;// 省略default:// Illegal state// TODO:  Throw exception?break;}
}private void handleWaitForFinish(CpmsState state) {//  通知给CPMS的监听者当前状态sendPowerManagerEvent(state.mCarPowerStateListenerState);int wakeupSec;synchronized (mLock) {// If we're shutting down immediately, don't schedule// a wakeup time.//  可以设置,进入休眠状态后的唤醒时间(默认是不设置的),0表示一直Sleep(除非VMCU主动唤起)wakeupSec = mGarageModeShouldExitImmediately ? 0 : mNextWakeupSec;}switch (state.mCarPowerStateListenerState) {case CarPowerStateListener.SUSPEND_ENTER:// 	// 给Vehicle  HAL发送状态// 发送的是DEEP_SLEEP_ENTRY,告知可以进入休眠状态了。mHal.sendSleepEntry(wakeupSec);break;case CarPowerStateListener.SHUTDOWN_ENTER:mHal.sendShutdownStart(wakeupSec);break;}
}

到这里,就是告诉了VMCU,Android 挂起Ready了,也就是一切准备完成。然后MCU就会发送AP_POWER_STATE_REQ(FINISHED),告知Android侧调用Linux内核的实现,把自己挂起来(Supend)。消息通知流程,跟上面的onPropertyEvent一致,这部分下面就省略了。

  • packages/services/Car/service/src/com/android/car/CarPowerManagementService.java
private void doHandlePowerStateChange() {mCurrentState = state;switch (state.mState) {// 省略case CpmsState.SUSPEND:// Received FINISH from VHAL// 走这里handleFinish();break;default:// Illegal state// TODO:  Throw exception?break;}
}private void handleFinish() {// 省略// 关闭VoicesetVoiceInteractionDisabled(true);// To make Kernel implementation simpler when going into sleep.disableWifi();if (mustShutDown) {// shutdown HUmSystemInterface.shutdown();} else {// 处理深入睡眠doHandleDeepSleep(simulatedMode);}mShutdownOnNextSuspend = false;
}private void doHandleDeepSleep(boolean simulatedMode) {// keep holding partial wakelock to prevent entering sleep before enterDeepSleep call// enterDeepSleep should force sleep entry even if wake lock is kept.mSystemInterface.switchToPartialWakeLock();mHandler.cancelProcessingComplete();synchronized (mLock) {mLastSleepEntryTime = SystemClock.elapsedRealtime();}int nextListenerState;if (simulatedMode) {// 省略} else {// 最终会调用Kernel接口,Supend To RAM(感兴趣的小伙伴可以顺着这个接口看下去。// 挂成功的情况下面,下面的代码就不会走了(CPU都没电了)。// 之后的代码,就是CPU上电后的恢复流程了。// 可以看出恢复前,要刷新一下屏幕亮度。然后走WAIT_FOR_VHAL状态。boolean sleepSucceeded = suspendWithRetries();if (!sleepSucceeded) {// Suspend failed and we shut down instead.// We either won't get here at all or we will power off very soon.return;}// We suspended and have now resumednextListenerState = CarPowerStateListener.SUSPEND_EXIT;}synchronized (mLock) {mIsResuming = true;// Any wakeup time from before is no longer valid.mNextWakeupSec = 0;}Slog.i(TAG, "Resuming after suspending");mSystemInterface.refreshDisplayBrightness();onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState);
}

综上是Android CarPower的深入睡眠睡眠主要流程,实际开发过程中。根据业务进行扩展,一般会添加一个跟CPMS平行,或者基于它的新的电源模块。下面是上面代码的时序图,供参考。
在这里插入图片描述

相关文章:

【Android】Android Framework系列---CarPower深度睡眠STR

Android Framework系列—CarPower深度睡眠 之前博客说了CarPower的开机启动流程 这里分析一下&#xff0c;Android CarPower实现深度睡眠的流程。 首先&#xff0c;什么是深度睡眠(Deep Sleep)? Android进入Deep Sleep后&#xff0c;关闭屏幕、关闭CPU的电源&#xff0c;保持…...

【漏洞复现】Fastjson_1.2.47_rce

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞检测3、漏洞验证 1.5、深度利用1、反弹Shell 说明内容漏洞编号漏洞名称Fastjson_1.2.47_远程执行漏…...

玩转AIGC:如何选择最佳的Prompt提示词?

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

ELK搭建以及使用教程(多pipiline)

1、环境准备 服务器&#xff1a;Centos7 Jdk版本&#xff1a;1.8 Es版本&#xff1a;7.12.1 kibana版本&#xff1a;7.12.1 logstash版本:7.12.1 IP地址安装软件192.168.50.211Es&#xff0c;Kibana&#xff0c;logstash 2、安装docker 安装步骤参考&#xff1a;https:…...

小程序如何设置用户同意服务协议并上传头像和昵称

为了保护用户权益和提供更好的用户体验&#xff0c;设置一些必填项和必读协议是非常必要的。首先&#xff0c;用户必须阅读服务协议。服务协议是明确规定用户和商家之间权益和义务的文件。通过要求用户在下单前必须同意协议&#xff0c;可以确保用户在使用服务之前了解并同意相…...

6.4 例程:使用互斥量

这个例程为使用多线程配合互斥量进行点乘计算&#xff0c;相关的数据通过全局变量的形式存在&#xff0c;因此可以被各个线程访问&#xff1b;每个线程会在相关数据的不同区域上进行处理&#xff0c;主线程等待子线程完成操作后&#xff0c;将最后的结果打印出来。 代码如下 #…...

[算法日志]图论: 深度优先搜索(DFS)

[算法日志]图论&#xff1a; 深度优先搜索(DFS) 深度优先概论 ​ 深度优先搜索算法是一种遍历图这种数据结构的算法策略&#xff0c;其中心思想是朝图节点的一个方向不断跳转&#xff0c;当该节点无下一个节点或所有方向都遍历完时&#xff0c;便回溯朝上一个节点的另一个方向…...

这道经典SQL面试问题你会吗?

大家经常自嘲后端开发就是crud boy嘛&#xff0c;今天给大家看一道SQL题&#xff0c;我相信很多人写不出来。我们来看一下这个题目。 create table course (id int primary key,name varchar(32) not null ); create table student (id int primary key,name varchar(32) not …...

网络服务退出一个问题的解析

一、问题 在实际开发中遇到一个问题&#xff0c;解决的过程虽然不长&#xff0c;但确实是想得比较多&#xff0c;总结一下&#xff0c;以供参考。这是一个网络通信的服务端而且使用的是别人封装好的库&#xff0c;通信等都没有问题&#xff0c;但在退出时会报一个错误&#xf…...

第四次pta认证P测试

第一题 试题编号&#xff1a; 试题名称&#xff1a;整数排序 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 128.0MB 【问题描述】 老师给定 10 个整数的序列&#xff0c;要求对其重新排序。排序要求: 1.奇数在前&#xff0c;偶数在后&#xff1b; 2.奇数按从大到小排序&am…...

mysql:B+树/事务

B树 : 为了数据库量身定做的数据结构 我们当前这里的讨论都是围绕 mysql 的 innodb 这个存储引擎来讨论的 其他存储引擎可能会用到hash 作为索引,此时就只能应对这种精准匹配的情况了 要了解 B树 我们先了解 B树, B树 是 B树 的改进 B树 有时候会写作 B-树 (这里的" -…...

python-在系统托盘显示CPU使用率和内存使用率

一、添加轮子 1.添加托盘区图标库 infi.systray from infi.systray import SysTrayIcon 2.添加图像处理库 Pillow from PIL import Image, ImageDraw, ImageFont 3.添加 psutil 来获取CPU、内存信息 import psutil 二、完整代码 from infi.systray import SysTrayIcon …...

构建mono-repo风格的脚手架库

前段时间阅读了 https://juejin.cn/post/7260144602471776311#heading-25 这篇文章&#xff1b;本文做一个梳理和笔记&#xff1b; 主要聚焦的知识点如下&#xff1a; 如何搭建脚手架工程如何开发调试如何处理命令行参数如何实现用户交互如何拷贝文件夹或文件如何动态生成文件…...

云安全—etcd攻击面

0x00 前言 本篇还是一样&#xff0c;先来说一说etcd是什么&#xff0c;干啥的&#xff0c;然后再来看看etcd的攻击面到底有哪些&#xff0c;做一个抛砖引玉的作用&#xff0c;如有不妥之处还请斧正 0x01 etcd 依旧还是按照问问题的方式来进行阐述&#xff0c;因为学到的东西…...

类锁和实例对象锁你分清了吗?

系列文章目录 文章目录 系列文章目录前言一、什么是锁竞争&#xff1f;二、什么是类锁&#xff1f;什么是实例对象锁&#xff1f;三、给类对象加锁不是锁住了整个类四、总结 前言 java选手们应该都对锁不陌生&#xff0c;加锁了就是为保证操作语句的原子性&#xff0c;如果你是…...

如何在麒麟上安装 ONLYOFFICE 桌面编辑器

我们很高兴地告诉大家&#xff0c;ONLYOFFICE 桌面编辑器现已上架麒麟软件商店。请阅读下文了解详情。 关于麒麟 麒麟是一款国产操作系统&#xff0c;主要是为了满足中国市场的需求和偏好而设计的。 它能够与各种硬件平台和软件应用程序的广泛兼容&#xff0c;因而受到认可。…...

记录:如何编写linux驱动,用module的方式

记录:如何编写Linux驱动,用module的方式 记录:如何编写Linux驱动,用module的方式参考记录:如何编写Linux驱动,用module的方式 编写一个 Linux 的驱动,用 module 方式开发,一般来说,编写一个 Linux 的驱动,需要遵循以下步骤: 确定设备的类型和功能,以及它在系统中的…...

3款免费又好用的 Docker 可视化管理工具

前言 Docker提供了命令行工具&#xff08;Docker CLI&#xff09;来管理Docker容器、镜像、网络和数据卷等Docker组件。我们也可以使用可视化管理工具来更方便地查看和管理Docker容器、镜像、网络和数据卷等Docker组件。今天我们来介绍3款免费且好用的 Docker 可视化管理工具。…...

C语言--判断一个年份是否是闰年(详解)

一.闰年的定义 闰年是指在公历&#xff08;格里高利历&#xff09;中&#xff0c;年份可以被4整除但不能被100整除的年份&#xff0c;或者可以被400整除的年份。简单来说&#xff0c;闰年是一个比平年多出一天的年份&#xff0c;即2月有29天。闰年的目的是校准公历与地球公转周…...

Python---排序算法

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 Python中的排序算法用于对数据进行排序。排序算法可以使数据按照一定的规则进行排列&#xff0c;以便于数据的查找、统计、比较等操作。在数据分析、机器学习、图形计算等领域&#xff0c…...

gitlab Blocking and unblocking users

原文&#xff1a;Redirecting... Blocking a userUnblocking a user Blocking and unblocking users GitLab 管理员阻止和取消阻止用户. Blocking a user 为了完全阻止用户访问 GitLab 实例&#xff0c;管理员可以选择阻止该用户. 可以通过滥用报告或直接从管理区域来阻止…...

Swift 和 Python 两种语言中带关联信息错误(异常)类型的比较

0. 概览 如果我们分别在平静如水、和谐感人的 Swift 和 Python 社区抛出诸如“Python 是天下最好的语言…” 和 “Swift 是宇宙第一语言…”之类的言论会有怎样的“下场”&#xff1f; 我们并不想对可能发生的“炸裂”景象做出什么预测&#xff0c;也无意比较 Swift 与 Pytho…...

北京联通iptv组播配置

多年前折腾过iptv&#xff0c;近期搬家换了个大电视&#xff0c;打算把iptv配置好了&#xff0c;尽管不怎么看&#xff0c;但聊胜于无。 其实很简单&#xff0c;用到了一些工具&#xff0c;记录如下 1. openwrt配置 因为有软路由&#xff0c;所以就借助openwrt了&#xff0c;一…...

C++ STL 迭代器失效

一、学习资料 STL迭代器的使用 二、vector容器获取值是下标法和at()的区别 vector<int> vA; int array[]{0,1,2,3,4}; vA.assign(array,array5); cout<<vA[6]<<endl; cout<<va.at(6)<<endl;如上述代码&#xff0c;当使用vA[6]的方式出现访问越…...

麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包

原文链接&#xff1a;麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包 hello&#xff0c;大家好啊&#xff0c;今天给大家带来麒麟桌面操作系统软件仓库搭建的文章02-软件仓库添加新的软件包&#xff0c;本篇文章主要给大家介绍了如何在麒麟桌面操作系统2203-x86版本上&…...

专业媒体播放软件Movist Pro中文

Movist Pro是一款专为Mac用户设计的专业媒体播放器。它支持广泛的视频和音频格式&#xff0c;包括MP4、AVI、MKV等&#xff0c;并提供了高级播放控件和定制的视频设置。其直观易用的用户界面&#xff0c;使得播放高清视频更为流畅&#xff0c;且不会卡顿或滞后。同时&#xff0…...

数据结构-邻接表广度优先搜索(C语言版)

对于一个有向图无向图&#xff0c;我们下面介绍第二种遍历方式。 广度优先搜索&#xff0c;即优先对同一层的顶点进行遍历。 如下图所示&#xff1a; 该例子&#xff0c;我们有六个顶点&#xff0c; 十条边。 对于广度优先搜索&#xff0c;我们先搜索a&#xff0c;再搜索abc…...

Py之auto-gptq:auto-gptq的简介、安装、使用方法之详细攻略

Py之auto-gptq&#xff1a;auto-gptq的简介、安装、使用方法之详细攻略 目录 auto-gptq的简介 1、版本更新历史 2、性能对比 推理速度 困惑度&#xff08;PPL&#xff09; 3、支持的模型 3、支持的评估任务 auto-gptq的安装 auto-gptq的使用方法 1、基础用法 (1)、量…...

【Linux】Linux+Nginx部署项目(负载均衡动静分离)

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Linux的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Nginx负载均衡 1.什么是负载均衡 2.实…...

C++笔记之vector的成员函数swap()和data()

C笔记之vector的成员函数swap()和data() 标准C中的std::vector类确实有swap()和data()这两个成员函数。下面是它们的简要描述&#xff1a; swap(): std::vector的swap()成员函数用于交换两个向量的内容&#xff0c;实现了高效的交换操作&#xff0c;不需要复制向量的元素。这…...