【安卓源码】安卓Watchdog 机制
在Android系统中,也设计了一个软件层面Watchdog,用于保护一些重要的系统服务,比如:AMS、WMS、PMS等,由于以上核心服务运行在system_server进程里面,所以当以上服务出现异常时,通常会将system_server进程kill掉,即让Android系统重启。
WatchDog功能主要是分析系统核心服务的重要线程、和锁 是否处于Blocked状态,即以下两个功能:
- 监控 system_server 中几个关键的锁,原理是在 android_fg 线程中尝试加锁
- 监控几个常用线程的执行时间,原理是在这几个线程中执行任务
WatchDog 的启动是在系统进程初始化后
/frameworks/base/services/java/com/android/server/SystemServer.java
991 private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
992 t.traceBegin("startBootstrapServices");
993
994 // Start the watchdog as early as possible so we can crash the system server
995 // if we deadlock during early boot
996 t.traceBegin("StartWatchdog");// 调用 Watchdog 的构造方法初始化
997 final Watchdog watchdog = Watchdog.getInstance();// 调用start 方法,启动线程
998 watchdog.start();
999 t.traceEnd();
1000
1001 Slog.i(TAG, "Reading configuration...");
1002 final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
1. WatchDog 的初始化
调用 Watchdog 的构造方法初始化
/frameworks/base/services/core/java/com/android/server/Watchdog.java
public static Watchdog getInstance() {if (sWatchdog == null) {
// 单例模式,创建 Watchdog 对象sWatchdog = new Watchdog();}return sWatchdog;}
Watchdog 构造方法
private Watchdog() {
// 创建线程,名字为 watchdog,线程调用run方法 mThread = new Thread(this::run, "watchdog");// 监听锁机制和 前台线程 FgThread 的handler处理 ,其也是单例模式,
// 提供给其他对象使用,如 PermissionManagerServicemMonitorChecker = new HandlerChecker(FgThread.getHandler(),"foreground thread", DEFAULT_TIMEOUT);// 将 mMonitorChecker 也保存到 mHandlerCheckers 中,去监听handler 是否超时,默认超时时间为 DEFAULT_TIMEOUT 30秒mHandlerCheckers.add(mMonitorChecker);// 监听系统进程的主线程mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),"main thread", DEFAULT_TIMEOUT));// 监听ui线程 UI thread.mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),"ui thread", DEFAULT_TIMEOUT));
// 监听Io线程 mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),"i/o thread", DEFAULT_TIMEOUT));// 监听DisplayThread线程 mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),"display thread", DEFAULT_TIMEOUT));// 监听动画 AnimationThread线程 mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),"animation thread", DEFAULT_TIMEOUT));// 监听surface animation thread.也是关于动画的mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),"surface animation thread", DEFAULT_TIMEOUT));// 监听是否有可用的binder 线程addMonitor(new BinderThreadMonitor());// 将系统进程增加到感兴趣的进程队列中mInterestingJavaPids.add(Process.myPid());// See the notes on DEFAULT_TIMEOUT.assert DB ||DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;mTraceErrorLogger = new TraceErrorLogger();}
在 SystemServer 启动过程中初始化 Watchdog。Watchdog 除了在初始化时创建一个线程mThread ,还会构建很多 HandlerChecker,大致可以分为两类:
- Monitor Checker,用于检查 Monitor 对象可能发生的死锁,AMS,IMS,WMS PMS 等核心的系统服务都是 Monitor 对象
- Looper Checker,用于检查线程的消息队列是否长时间处于工作状态。Watchdog 自身的消息队列,ui,io, Display 这些全局的消息队列都是被检查的对象。此外,一些重要的线程的消息队列,也会加入到 Looper Checker中,譬如 AMS,WMS 这些是在对应的对象初始化时加入的
new HandlerChecker 的构造函数初始化
// HandlerChecker 实现了 Runnable接口,会回调run 方法public final class HandlerChecker implements Runnable {private final Handler mHandler;private final String mName;private final long mWaitMax;private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();private boolean mCompleted;private Monitor mCurrentMonitor;private long mStartTime;private int mPauseCount;HandlerChecker(Handler handler, String name, long waitMaxMillis) {mHandler = handler;mName = name;
// 等待的最长时间设置给 mWaitMaxmWaitMax = waitMaxMillis;
// 初始化 mCompleted 为 truemCompleted = true;}// 增加监听锁的方法,调用的接口是 addMonitorvoid addMonitorLocked(Monitor monitor) {// We don't want to update mMonitors when the Handler is in the middle of checking// all monitors. We will update mMonitors on the next schedule if it is safemMonitorQueue.add(monitor);}public void addMonitor(Monitor monitor) {synchronized (mLock) {mMonitorChecker.addMonitorLocked(monitor);}}
系统进程的service 给watchdog 监听
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
由前面分析,监听是否持锁时间长,在 前台线程去监听
// ams 实现了 Watchdog.Monitor 接口
431 public class ActivityManagerService extends IActivityManager.Stub
432 implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {2226 public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
2227 LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
2228 mInjector = new Injector(systemContext);
2229 mContext = systemContext;。。。。
// 监听是否持锁时间长
2328 Watchdog.getInstance().addMonitor(this);
// 监听ams 的handler 是否超时
2329 Watchdog.getInstance().addThread(mHandler);// ams 实现了 Watchdog.Monitor 接口,会回调 monitor 方法
15024 public void monitor() {
15025 synchronized (this) { }
15026 }
同样wms 也监听了锁是否超时
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
330 public class WindowManagerService extends IWindowManager.Stub
331 implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {1417 public void onInitReady() {
1418 initPolicy();
1419
1420 // Add ourself to the Watchdog monitors.
1421 Watchdog.getInstance().addMonitor(this);=========
6658 @Override
6659 public void monitor() {// 监听mGlobalLock 锁
6660 synchronized (mGlobalLock) { }
6661 }
2. 调用start 方法,启动WatchDog 线程监听
public void start() {
// 即调用this::run 方法mThread.start();}
调用this::run 方法
/frameworks/base/services/core/java/com/android/server/Watchdog.java
private void run() {boolean waitedHalf = false;while (true) {List<HandlerChecker> blockedCheckers = Collections.emptyList();String subject = "";boolean allowRestart = true;int debuggerWasConnected = 0;boolean doWaitedHalfDump = false;final ArrayList<Integer> pids;synchronized (mLock) {// timeout 为 30 秒long timeout = CHECK_INTERVAL;// Make sure we (re)spin the checkers that have become idle within// this wait-and-check interval// 2-1)遍历所有的 HandlerCheckers,去post消息看是否超时for (int i=0; i<mHandlerCheckers.size(); i++) {HandlerChecker hc = mHandlerCheckers.get(i);hc.scheduleCheckLocked();}if (debuggerWasConnected > 0) {debuggerWasConnected--;}// 记录开始的时间long start = SystemClock.uptimeMillis();while (timeout > 0) {if (Debug.isDebuggerConnected()) {debuggerWasConnected = 2;}try {
// 等待 30 秒mLock.wait(timeout);// Note: mHandlerCheckers and mMonitorChecker may have changed after waiting} catch (InterruptedException e) {Log.wtf(TAG, e);}if (Debug.isDebuggerConnected()) {debuggerWasConnected = 2;}
// 保证执行等待30秒,然后跳出循环timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);}// 2-2)去计算等待的状态 evaluateCheckerCompletionLockedfinal int waitState = evaluateCheckerCompletionLocked();if (waitState == COMPLETED) {waitedHalf = false;continue;} else if (waitState == WAITING) {continue;
// 2-3)处理时间大于 30秒但是小于60秒流程} else if (waitState == WAITED_HALF) {if (!waitedHalf) {Slog.i(TAG, "WAITED_HALF");
// 则设置 waitedHalf = truewaitedHalf = true;// We've waited half, but we'd need to do the stack trace dump w/o the lock.pids = new ArrayList<>(mInterestingJavaPids);
// 设置 doWaitedHalfDump = truedoWaitedHalfDump = true;} else {continue;}} else {
// 2-4)执行超时的流程blockedCheckers = getBlockedCheckersLocked();subject = describeCheckersLocked(blockedCheckers);allowRestart = mAllowRestart;pids = new ArrayList<>(mInterestingJavaPids);}} // END synchronized (mLock)if (doWaitedHalfDump) {
// 超时 30秒后,会ams先dump 消息ActivityManagerService.dumpStackTraces(pids, null, null,getInterestingNativePids(), null, subject);continue;}// 超时会打印下列logEventLog.writeEvent(EventLogTags.WATCHDOG, subject);
2-1)遍历所有的 HandlerCheckers,去post消息看是否超时
public void scheduleCheckLocked() {if (mCompleted) {// Safe to update monitors in queue, Handler is not in the middle of workmMonitors.addAll(mMonitorQueue);mMonitorQueue.clear();}// 当不是前台线程 FgThread,并且在轮询状态;或者调用了 pauseLocked 则直接return 出去if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())|| (mPauseCount > 0)) {// Don't schedule until after resume OR// If the target looper has recently been polling, then// there is no reason to enqueue our checker on it since that// is as good as it not being deadlocked. This avoid having// to do a context switch to check the thread. Note that we// only do this if we have no monitors since those would need to// be executed at this point.mCompleted = true;return;}
// mCompleted 为false 则表示在查询if (!mCompleted) {// we already have a check in flight, so no needreturn;}mCompleted = false;mCurrentMonitor = null;
// 设置开始调用的时间mStartTime = SystemClock.uptimeMillis();
// 将this 是runable,插入到消息队列的对头mHandler.postAtFrontOfQueue(this);}
如果消息处理的话,则会执行run 方法:
@Overridepublic void run() {final int size = mMonitors.size();
// 如果是 FgThread 则有监听持锁是否久,则会回调 monitor 方法for (int i = 0 ; i < size ; i++) {synchronized (mLock) {mCurrentMonitor = mMonitors.get(i);}
// 可能会卡住,则不会设置 mCompleted 为 truemCurrentMonitor.monitor();}synchronized (mLock) {
// 执行完则会mCompleted设置为 truemCompleted = true;mCurrentMonitor = null;}}
2-2)去计算等待的状态 evaluateCheckerCompletionLocked
- COMPLETED = 0:等待完成;
- WAITING = 1:等待时间小于DEFAULT_TIMEOUT的一半,即30s;
- WAITED_HALF = 2:等待时间处于30s~60s之间;
- OVERDUE = 3:等待时间大于或等于60s。
// 有下列 4 种状态private static final int COMPLETED = 0;private static final int WAITING = 1;private static final int WAITED_HALF = 2;private static final int OVERDUE = 3;private int evaluateCheckerCompletionLocked() {int state = COMPLETED;for (int i=0; i<mHandlerCheckers.size(); i++) {HandlerChecker hc = mHandlerCheckers.get(i);
// 同样也是遍历所有的 HandlerCheckerstate = Math.max(state, hc.getCompletionStateLocked());}return state;}========
// 调用 getCompletionStateLocked 方法public int getCompletionStateLocked() {// 如果1.对应handler 有处理队头消息;2.Fgthread 处理了队头消息并且监听的锁没有超时;则 mCompleted 为 trueif (mCompleted) {return COMPLETED;} else {long latency = SystemClock.uptimeMillis() - mStartTime;
// 如果是处理的时间小于 30 秒,则设置状态为等待 WAITINGif (latency < mWaitMax/2) {return WAITING;
// 如果处理的时间大于 30 秒,但是小于 60s,则设置状态为 WAITED_HALF} else if (latency < mWaitMax) {return WAITED_HALF;}}return OVERDUE;}
2-3)处理时间大于 30秒但是小于60秒流程 WAITED_HALF
// 2-3)处理时间大于 30秒但是小于60秒流程} else if (waitState == WAITED_HALF) {if (!waitedHalf) {Slog.i(TAG, "WAITED_HALF");
// 则设置 waitedHalf = truewaitedHalf = true;// We've waited half, but we'd need to do the stack trace dump w/o the lock.pids = new ArrayList<>(mInterestingJavaPids);
// 设置 doWaitedHalfDump = truedoWaitedHalfDump = true;} else {continue;}} else {
// 执行超时的流程blockedCheckers = getBlockedCheckersLocked();subject = describeCheckersLocked(blockedCheckers);allowRestart = mAllowRestart;pids = new ArrayList<>(mInterestingJavaPids);}} // END synchronized (mLock)if (doWaitedHalfDump) {
// 超时 30秒后,会ams先dump 消息,然后 continue 不往下执行
// dump 出 NATIVE_STACKS_OF_INTEREST 数组中的进程信息ActivityManagerService.dumpStackTraces(pids, null, null,getInterestingNativePids(), null, subject);continue;}
2-4)执行超时的流程
} else if (waitState == WAITED_HALF) {if (!waitedHalf) {Slog.i(TAG, "WAITED_HALF");} else {
// 执行超时的流程
// 首先getBlockedCheckersLocked获取到执行超时的 HandlerChecker 对应的线程blockedCheckers = getBlockedCheckersLocked();// 获取超时的信息 describeCheckersLockedsubject = describeCheckersLocked(blockedCheckers);
// 默认是允许重启的allowRestart = mAllowRestart;pids = new ArrayList<>(mInterestingJavaPids);}} // END synchronized (mLock)。。。。
// 会打印下列logEventLog.writeEvent(EventLogTags.WATCHDOG, subject);final UUID errorId;if (mTraceErrorLogger.isAddErrorIdEnabled()) {errorId = mTraceErrorLogger.generateErrorId();mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);} else {errorId = null;}FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);long anrTime = SystemClock.uptimeMillis();StringBuilder report = new StringBuilder();report.append(MemoryPressureUtil.currentPsiState());ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);StringWriter tracesFileException = new StringWriter();final File stack = ActivityManagerService.dumpStackTraces(pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),tracesFileException, subject);// Give some extra time to make sure the stack traces get written.// The system's been hanging for a minute, another second or two won't hurt much.SystemClock.sleep(5000);processCpuTracker.update();report.append(processCpuTracker.printCurrentState(anrTime));report.append(tracesFileException.getBuffer());// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel logdoSysRq('w');doSysRq('l');// Try to add the error to the dropbox, but assuming that the ActivityManager// itself may be deadlocked. (which has happened, causing this statement to// deadlock and the watchdog as a whole to be ineffective)Thread dropboxThread = new Thread("watchdogWriteToDropbox") {public void run() {// If a watched thread hangs before init() is called, we don't have a// valid mActivity. So we can't log the error to dropbox.if (mActivity != null) {mActivity.addErrorToDropBox("watchdog", null, "system_server", null, null, null,null, report.toString(), stack, null, null, null,errorId);}}};dropboxThread.start();try {dropboxThread.join(2000); // wait up to 2 seconds for it to return.} catch (InterruptedException ignored) {}IActivityController controller;synchronized (mLock) {controller = mController;}if (controller != null) {Slog.i(TAG, "Reporting stuck state to activity controller");try {Binder.setDumpDisabled("Service dumps disabled due to hung system process.");// 1 = keep waiting, -1 = kill systemint res = controller.systemNotResponding(subject);if (res >= 0) {Slog.i(TAG, "Activity controller requested to coninue to wait");waitedHalf = false;continue;}} catch (RemoteException e) {}}// Only kill the process if the debugger is not attached.if (Debug.isDebuggerConnected()) {debuggerWasConnected = 2;}if (debuggerWasConnected >= 2) {Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");} else if (debuggerWasConnected > 0) {Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");} else if (!allowRestart) {Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");} else {Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);Slog.w(TAG, "*** GOODBYE!");if (!Build.IS_USER && isCrashLoopFound()&& !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {breakCrashLoop();}// 杀掉系统进程Process.killProcess(Process.myPid());System.exit(10);}waitedHalf = false;}}
// 首先getBlockedCheckersLocked获取到执行超时的 HandlerChecker 对应的线程
private ArrayList<HandlerChecker> getBlockedCheckersLocked() {ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
// 同样也是遍历所有的 mHandlerCheckersfor (int i=0; i<mHandlerCheckers.size(); i++) {HandlerChecker hc = mHandlerCheckers.get(i);// 通过下列方法isOverdueLocked看是否是超时的if (hc.isOverdueLocked()) {checkers.add(hc);}}return checkers;}==========
// 通过下列方法看是否是超时的boolean isOverdueLocked() {return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);}
// 获取超时的信息 describeCheckersLocked
private String describeCheckersLocked(List<HandlerChecker> checkers) {StringBuilder builder = new StringBuilder(128);for (int i=0; i<checkers.size(); i++) {if (builder.length() > 0) {builder.append(", ");}builder.append(checkers.get(i).describeBlockedStateLocked());}return builder.toString();}=======String describeBlockedStateLocked() {
// 如果 mCurrentMonitor 为空,则表示不是锁超时出现问题,而是handler,则打印handler 信息if (mCurrentMonitor == null) {return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";} else {// 不为空,则是锁出现问题,只能在 FgThread 中return "Blocked in monitor " + mCurrentMonitor.getClass().getName()+ " on " + mName + " (" + getThread().getName() + ")";}}
Watchdog 检测到异常的信息收集
- AMS.dumpStackTraces:输出Java和Native进程的栈信息
- doSysRq
- dropBox
收集完信息后便会杀死 system_server 进程。此处 allowRestart 默认值为 true,当执行 am hang 操作则设置不允许重启 (allowRestart =false), 则不会杀死 system_server 进程。
相关文章:
【安卓源码】安卓Watchdog 机制
在Android系统中,也设计了一个软件层面Watchdog,用于保护一些重要的系统服务,比如:AMS、WMS、PMS等,由于以上核心服务运行在system_server进程里面,所以当以上服务出现异常时,通常会将system_se…...

inscode连接不上gpu,持续8小时,为了数据不丢失续费了6小时,我只想知道什么时候可以连接
并且给我相应的补偿...
QT位置相关函数
Qt(Qt Framework)是一个流行的C应用程序开发框架,提供了丰富的位置相关函数和类,用于处理窗口、窗口小部件和图形的位置和几何操作。以下是一些常用的Qt位置相关函数和类: QPoint:QPoint类表示一个二维点的…...

vulnhub靶场 Kioptrix-level-1
简介: vulnhub是一个提供靶场环境的平台。而Kioptrix-level-1就是一个对新手比较友好的靶场。初学渗透的同学可以做做试试看,项目地址如下。 项目地址:Kioptrix: Level 1 (#1) ~ VulnHub 信息收集 查看本机IP,靶机跟kali都是使用…...

全网最细,真实企业性能测试落地实施,一文带你快速打通...
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、什么是性能测试…...

三十一、【进阶】B+树的演变过程
1、B树简单介绍 (1)介绍:B树也属于B树,是B树的变种 (2)特点:所有的数据都位于叶子节点上,叶子节点上的所有元素形成了一个单项链表 (3)图示: 2…...

算法通过村第十三关-术数|白银笔记|术数高频问题
文章目录 前言数组实现加法专题数组实现整数加法字符串加法二进制加法 幂运算专题求2的次幂求3的次幂求4的次幂 总结 前言 提示:人心本易趋死寂,苦难之后,焕然重建,激荡一阵,又趋麻木。 --苏枕书《有鹿来》 我们继续看…...

Java 线程的生命周期
🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎 📚系列专栏:Java全栈,…...
Vue页面监听键盘按键的多种方法
在Vue页面中,可以使用多种方法来监听键盘按键。以下是至少五种常用的方法: 使用keydown或keyup指令来绑定键盘按键事件。 <template><div><input type"text" keydown.enter"handleEnterKey" /></div> <…...

解析硬件连通性测试的重要性及测试方法
在现代科技世界中,硬件设备的复杂性和多样性已经达到了前所未有的水平。无论是计算机、智能手机、物联网设备还是嵌入式系统,各种硬件组件的协同工作对于设备的正常运行至关重要。硬件连通性测试是确保这些组件相互配合无误的重要步骤。 一、硬件连通性测…...

Hive窗口函数回顾
1.语法 1.1 基于行的窗口函数 Hive的窗口函数分为两种类型,一种是基于行的窗口函数,即将某个字段的多行限定为一个范围,对范围内的字段值进行计算,最后将形成的字段拼接在该表上。 注意:在进行窗口函数计算之前&#…...

flink自定义窗口分配器
背景 我们知道处理常用的滑动窗口分配器,滚动窗口分配器,全局窗口分配器,会话窗口分配器外,我们可以实现自己的自定义窗口分配器,以实现我们的自己的窗口逻辑 自定义窗口分配器的实现 package wikiedits.assigner;i…...
iOS CGRect CGPoint NSRange等结构体的NSLog打印输出
iOS的UIKit里提供了UIGeometry.h内有各结构体转换成NSString的方法,可用于打印输出; UIKIT_EXTERN NSString *NSStringFromCGPoint(CGPoint point); UIKIT_EXTERN NSString *NSStringFromCGVector(CGVector vector); UIKIT_EXTERN NSString *NSStringFr…...
Viper FTP Mac/ftp管理工具
Viper FTP 是一个用于文件传输和管理的 Mac 应用程序。它允许用户上传、下载和管理远程服务器上的文件,以及在不同本地文件夹之间传输文件。 Viper FTP 支持广泛的文件传输协议,包括 FTP、SFTP、WebDav、Amazon S3、Google Drive 等。它还包括文件同步、…...

web漏洞-xml外部实体注入(XXE)
web漏洞-xml外部实体注入(XXE) 目录 web漏洞-xml外部实体注入(XXE)概念危害检测方法利用方法漏洞利用xxe-lab有回显情况无回显情况 pikachu靶场有回显内容无回显 修复方案 概念 xml可拓展标记语言: xml是一种可拓展的标…...
Impeller-Flutter的新渲染引擎
Impeller是什么?它本质上是怎样运行的? Impeller是Flutter的新的渲染引擎,直到现在Flutter正在用一个叫做Skia的渲染引擎。 问题是Skia不是为了Flutter量身定做的。它有为范围广阔的设备构建的一大堆的渲染特性,这意味着它并不总…...
python 面试算法题
1.第一题 题目描述:给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。 s 的 旋转操作 就是将 s 最左边的字符移动到最右边。 例如, 若 s abcde,在旋转一次之后结果就是bcdea 。 示例一: 输入: s &quo…...
Python中的yield关键字
基本概念 yield 是 Python 中的一个关键字,主要在定义生成器函数时使用。使用 yield 的函数在调用时返回一个特殊的迭代器,称为生成器。不同于常规的函数返回一个单一的值(如数字、字符串或其他对象),带有 yield 的函…...

怎么压缩pdf文件?分享缩小pdf文件的简单方法
在我们的日常生活和工作中,往往需要处理大量的PDF文件,而很多时候这些文件的大小会成为传输和存储的难题。为了解决这个问题,下面我们将介绍三种方法来压缩PDF文件,一起来看看吧~ 一、嗨格式压缩大师 首先,最简单也是…...

51单片机可调幅度频率波形信号发生器( proteus仿真+程序+原理图+报告+讲解视频)
51单片机可调幅度频率信号发生器( proteus仿真程序原理图报告讲解视频) 讲解视频1.主要功能:2.仿真3. 程序代码4. 原理图4. 设计报告5. 设计资料内容清单&&下载链接***[资料下载链接](https://docs.qq.com/doc/DS1daV1BKRXZMeE9u)*** 51单片机可…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...