【Android原生问题分析】夸克、抖音划动无响应问题【Android14】

在这里插入图片描述

1 问题描述

偶现问题,用户打开夸克、抖音后,在界面上划动无响应,但是没有ANR。回到Launcher后再次打开夸克/抖音,发现App的界面发生了变化,但是仍然是划不动的。

2 log初分析

复现问题附近的log为:

在这里插入图片描述

用户打开夸克的时间点为“00:27:25”左右,然后是持续的划动无响应,最后在“00:27:36”最后退出了夸克。

现在基本上已经没有冻屏的操作了,从log中也没有看到可能的痕迹,并且根据用户的描述,在夸克界面上划动无响应,切换到别的界面后再切回夸克,看到夸克的界面是有更新的,说明应该是在绘制或者合成的地方卡住了。

由于默认的log有限,无法定位到具体的问题,因此需要添加log。

3 第一次添加log

由于不确定是哪个阶段出了问题,因此先针对Input流程和measure、layout以及draw流程添加了log,后面测试再次复现问题后,查看log发现问题出在draw流程(其实从问题描述上也能看出来,从App切换到Launcher再切换回App,界面发生了变化,说明输入事件应该是被App正确接收到了)。

第一帧正常:

在这里插入图片描述

从第二帧开始异常:

在这里插入图片描述

异常的点在ViewRootImpl.performDraw:

在这里插入图片描述

这里就是问题的直接原因了,明明是亮屏状态,但是ViewRootImpl.mAttachInfo.mDisplayState保存的值却是Display.STATE_OFF,导致代码误判断此时处于灭屏状态,因此不会继续走绘制流程。

至于为什么第一帧有没问题,而从第二帧开始就出问题了,因为第一帧的时候,ViewRootImpl.mReportNextDraw为true,所以没有提前return,可以继续走draw流程。而从第二帧开始,ViewRootImpl.mReportNextDraw就变成了false,导致代码提前返回。

搜索代码,AttachInfo.mDisplayState赋值的地方只有四处:

1、View.dispatchMovedToDisplay

2、ViewRootImpl.onMovedToDisplay

3、ViewRootImpl.setView

4、ViewRootImpl.mDisplayListener.onDisplayChanged

前两处是跟移动到新的Display相关的,可以排除,第3处是窗口首次添加,也可以排除,那么就只有第4处是Display状态改变后,更新AttachInfo.mDisplayState的地方。

另外根据本地调试也的确如此,当发生亮灭屏时,触发的是ViewRootImpl.mDisplayListener.onDisplayChanged:

DisplayManagerGlobal.DisplayManagerCallback.onDisplayEvent

-> DisplayManagerGlobal.handleDisplayEvent

-> DisplayManagerGlobal.DisplayListenerDelegate.sendDisplayEvent

在这里插入图片描述

最终的调用起点在系统进程的DisplayManagerService。

由于之前对DisplayManagerService了解比较少,所以还不清楚问题出在哪儿,这次加的log不太够,需要继续在DisplayManagerService处加log,然后让测试帮忙去复现问题。

但是同时别的同事也合入了几个google的patch,导致后续问题复现不到了,这个问题一度没有了下文,当时觉得还挺可惜,花了挺长的时间去分析,结果后面复现不到了。

4 第二次添加log

虽然之前的问题不了了之了,但是后面别的项目又继续复现了该问题,希望又被点燃了…先在DisplayManagerService中添加一些log,然后继续让测试同事帮忙复现,最终成功复现到了问题。

以下是当时的log分析:

4.1 11-12 22:25:14 - 第一次启动抖音

在这里插入图片描述

这个是后续发生问题的抖音界面第一次启动的时间,此时是正常的。

中间又多次启动抖音以及亮灭屏,但是都没有问题,所以我们跳过这些阶段。

直接看发生问题前的那次启动抖音的情况。

4.2 11-13 10:33:39 - 再次启动抖音

在这里插入图片描述

ViewRootImpl保存的DisplayState已经是2了,即Display.STATE_ON,这次绘制没有问题。

4.3 11-13 10:53:42 - 灭屏

在这里插入图片描述

此次DisplayManagerService向SplashActivity对应进程“17885”进行了通知。

最终抖音对应进程“17885”也打印了相应的onDisplayChanged的log。

4.4 11-13 10:55:07 - 亮屏

在这里插入图片描述

DisplayManagerService没有通知抖音对应进程“17885”,有以下log打印:

11-13 10:55:07.252351  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.252761  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.253925  1450  1575 D DisplayManagerService: Ignore redundant display event 0/2 to 10224/16998
11-13 10:55:07.253967  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.253978  1450  1575 D DisplayManagerService: Ignore redundant display event 0/2 to 10224/16998

4.5 11-13 10:55:16 - 灭屏

在这里插入图片描述

DisplayManagerService没有通知抖音对应进程“17885”。

4.6 11-13 13:00:00 - 出现问题前的最后一次亮屏

在这里插入图片描述

DisplayManagerService还是没有通知抖音对应进程“17885”。

4.7 11-13 13:24:18 - 再次启动抖音,有问题

在这里插入图片描述

原因则是“11-13 10:53:42”那次灭屏DisplayManagerService通知到了抖音对应进程“17885”,随后不管是亮屏还是灭屏都没有再通知抖音进程,导致此次启动抖音SplashActivity后,其ViewRootImpl处保存的DisplayState还是1,即Display.STATE_OFF,灭屏状态,所以不会走绘制流程。

接下来需要看下具体的代码,为什么没有通知到抖音进程,并且打印了这些log:

11-13 10:55:07.252351  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.252761  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.253925  1450  1575 D DisplayManagerService: Ignore redundant display event 0/2 to 10224/16998
11-13 10:55:07.253967  1450  1575 I DisplayManagerService: DisplayManagerService.deliverDisplayEvent: isUidCached=10224
11-13 10:55:07.253978  1450  1575 D DisplayManagerService: Ignore redundant display event 0/2 to 10224/16998

5 代码分析

5.1 App侧注册display状态的监听

ViewRootImpl有一个DisplayListener的成员变量mDisplayListener,该成员变量用来接收DisplayManagerService关于Display状态改变的通知:

在这里插入图片描述

当onDisplayChanged回调触发,会更新mAttachInfo.mDisplayState的值。

该成员变量通过DisplayManagerGlobal.registerDisplayListener进行注册,最终是调用了IDisplayManager.registerCallbackWithEventMask,跨进程在DisplayManagerService进行了注册,如下图:

在这里插入图片描述

DisplayManagerService内部类BinderService是IDisplayManager的本地实现,然后BinderService.registerCallbackWithEventMask又调用了DisplayManagerService.registerCallbackInternal:

在这里插入图片描述

最终,DisplayManagerService为每一个通过IDisplayManager.registerCallbackWithEventMask监听Display状态的客户端,都创建了一个CallbackRecord对象,用来代表一个客户端记录,该CallbackRecord被保存在DisplayManagerService的成员变量mCallbacks中。

至于CallbackRecord,其内部保存了客户端的pid和uid,因此可以唯一对应一个客户端。

5.2 DisplayManagerService通知客户端Display状态改变

直接放结论,在CallbackRecord.notifyDisplayEventAsync中:

在这里插入图片描述

CallbackRecord中的IDisplayManagerCallback类型的成员变量mCallback保存的是客户端进程传过来的IDisplayManagerCallback代理,因此调用IDisplayManagerCallback.onDisplayEvent最终可以调用到客户端进程的DisplayManagerGlobal:

在这里插入图片描述

最终通知到ViewRootImpl。

CallbackRecord.notifyDisplayEventAsync方法调用的地方有两处,先看第一处,在DisplayManagerService.deliverDisplayEvent:

在这里插入图片描述

这里会将DisplayManagerService.mCallbacks的数据拷贝到DisplayManagerService.mTempCallbacks,然后调用DisplayManagerService.isUidCached方法来判断需要通知的进程是否处于cached mode:

在这里插入图片描述

如果不是,那么说明此时该客户端进程的优先级比较高,需要直接调用CallbackRecord.notifyDisplayEventAsync来通知客户端Display状态改变的消息。

如果客户端进程处于cached mode,那么说明该进程处于一个优先级较低的状态,因此不会立即调用CallbackRecord.notifyDisplayEventAsync,而是创建一个PendingCallback对象,然后保存到DisplayManagerService的mPendingCallbackSelfLocked成员变量中,注意这里mPendingCallbackSelfLocked是根据uid去区分的,并非是pid。

等到客户端进程的进程优先级提高后,再去从DisplayManagerService.mPendingCallbackSelfLocked中取出相应的CallbackRecord,然后调用CallbackRecord.notifyDisplayEventAsync方法通知客户端进程,这也就是第二处调用CallbackRecord.notifyDisplayEventAsync的地方,在PendingCallback.sendPendingDisplayEvent:

在这里插入图片描述

而PendingCallback.sendPendingDisplayEvent方法则由UidImportanceListener.onUidImportance方法调用。

这里的逻辑很好理解,当进程的优先级改变的时候,会回调UidImportanceListener.onUidImportance,然后UidImportanceListener.onUidImportance中继续判断该进程的优先级,如果优先级高于IMPORTANCE_CACHED,那么会继续根据该进程的uid从DisplayManagerService.mPendingCallbackSelfLocked中找其对应的PendingCallback,如果能找到,那么调用PendingCallback.sendPendingDisplayEvent,最终在PendingCallback.sendPendingDisplayEvent中调用CallbackRecord.notifyDisplayEventAsync来通知客户端Display状态的改变。

5.3 省流版

概括一点说,就是并不是每次Display状态发生变化的时候,DisplayMangerService就会去通知抖音进程,而是会调用DisplayManagerService.isUidCached方法来看这个App进程的优先级是否足够高:

1)、如果优先级高于IMPORTANCE_CACHED,说明此时优先级还是挺高的,那么DisplayManagerService会立即通知抖音进程。

2)、如果优先级等于或者低于IMPORTANCE_CACHED,说明优先级不够高,那么DisplayManagerService不会立即通知抖音进程,而是为其创建一个记录,等到抖音进程的优先级改变并且高于IMPORTANCE_CACHED的时候,根据这个记录找到抖音进程,然后通知它。

6 原因分析

1)、首先是这次:”11-13 10:33:39 - 再次启动抖音“

这是离问题发生时间点最近的那次启动抖音。

2)、然后接着:”11-13 10:53:42 - 灭屏“

看到这次灭屏的时候,可能是由于距离启动抖音的时间不长,因此这时抖音进程的优先级还比较高,所以这次灭屏的时候,通知到了抖音进程17885。

3)、接着是”11-13 10:55:07 - 亮屏“

这里我们之前的分析有误,看了代码后才了解,不是说DisplayManagerService没有通知抖音进程17885,而是抖音进程的优先级比较低,所以没有立即通知抖音进程,而是为其创建了PendingCallback,等待抖音进程优先级提高后,再去通知抖音进程,如这里的log:

在这里插入图片描述

但是这里的逻辑有问题。

如log中显示的,这里抖音App的uid为10224,有三个进程,16998,17885,18213,我们关注的抖音的”SplashActivity”所在进程是17885.

然后再看代码:

在这里插入图片描述

首先对于抖音进程16998,由于这是遍历到的抖音的第一个进程,因此DisplayManagerService.mPendingCallbackSelfLocked中是没有抖音uid的信息的,所以这里会基于进程16998对应的CallbackRecord创建一个PendingCallback对象,然后保存到DisplayManagerService.mPendingCallbackSelfLocked中。

然后遍历到抖音进程17885的时候,由于它和抖音进程16998的uid是一样的,都是10224,那么这里就不会为进程17885再去创建一个PendingCallback对象, 而是直接复用上一步创建的那个PendingCallback,如这里的log打印:

”Ignore redundant display event…“

那么这里就是问题的根因所在了,抖音有三个进程,这里只为抖音进程16998对应的CallbackRecord创建了一个PendingCallback,到后续抖音进程优先级提高的时候,那么也只会调用该PendingCallback中的CallbackRecord的notifyDisplayEventAsync方法,那么这意味着只有进程16998会得到Display状态改变的通知,进程17885和进程18213都无法得到通知,再看后面抖音进程优先级提升的情况:

在这里插入图片描述

看到果然只有进程16998得到通知了,我们关注的进程17885没有被通知到,那么进程17885的ViewRootImpl保存的mAttachInfo.mDisplayState将一直是灭屏的状态。

不太省流的省流版

目前的代码,在一个App对应一个uid,以及一个pid的情况下,是够用的,但是实际上一个App对应了一个uid,但是可能会有多个进程,即一个uid会对应多个pid。

再回看我们的代码总结:如果App优先级等于或者低于IMPORTANCE_CACHED,说明优先级不够高,那么DisplayManagerService不会立即通知抖音进程,而是为其创建一个记录,等到抖音进程的优先级改变并且高于IMPORTANCE_CACHED的时候,根据这个记录找到抖音进程,然后通知它。

这里创建的记录,是根据uid去创建的,那么即使抖音有多个进程,也只会创建一个记录,根据遍历顺序来看则是pid最小的那个。

再看问题场景:

1)、启动完抖音后灭屏,此时抖音的优先级比较高,因此DisplayMangerService会立即通知抖音进程,这里抖音的三个进程都能通知到,因此进程17885的ViewRootImpl处保存的Display状态就是灭屏状态。

2)、过了一阵子再次亮屏,由于抖音的优先级降低了,所以DisplayManagerService没有立即通知抖音进程,而是创建为抖音创建了一个记录,等到抖音优先级提高的时候,再去通知抖音进程。此时抖音有三个进程,16998,17885,18213,这里只为pid最小的16998创建了一项纪录。

3)、后续抖音优先级提高的时候,DisplayMangerService只通知了进程16998,由于没有为剩下的两个进程创建记录,因此这两个进程就通知不到了,所以我们关注的进程17885出了问题。

从以上分析可以知道,我们需要为每一个pid都创建一个记录,而非每一个uid。

7 问题解决

最终看到已经有google patch解决这个问题了:

Diff - f2daa46634f6a1e5e329041f07a27dbc894d71b2^! - platform/frameworks/base - Git at Google

Correct the deferred sending of display events.When multiple processes sharing the same uid become non-cached, the
current deferred transmission logic does not consider all of them,
leading to only the first process in the callback list to receive the
pending display events. Moreover, this first process may inadvertently
receive unintended display events.Bug: 325692442
Test: manual, see bug descriptionChange-Id: Ife98a8d843a3000294fbc9929bb78a755534b5dc
Signed-off-by: Hyeongseop Shim <hyeongseop.shim@samsung.corp-partner.google.com>diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ba21a32..434985e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java@@ -464,10 +464,11 @@// May be used outside of the lock but only on the handler thread.private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();-    // Pending callback records indexed by calling process uid.
+    // Pending callback records indexed by calling process uid and pid.// Must be used outside of the lock mSyncRoot and should be selflocked.@GuardedBy("mPendingCallbackSelfLocked")
-    public final SparseArray<PendingCallback> mPendingCallbackSelfLocked = new SparseArray<>();
+    public final SparseArray<SparseArray<PendingCallback>> mPendingCallbackSelfLocked =
+            new SparseArray<>();// Temporary viewports, used when sending new viewport information to the// input system.  May be used outside of the lock but only on the handler thread.
@@ -1011,8 +1012,8 @@}// Do we care about this uid?
-                PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
-                if (pendingCallback == null) {
+                SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(uid);
+                if (pendingCallbacks == null) {return;}@@ -1020,7 +1021,12 @@if (DEBUG) {Slog.d(TAG, "Uid " + uid + " becomes " + importance);}
-                pendingCallback.sendPendingDisplayEvent();
+                for (int i = 0; i < pendingCallbacks.size(); i++) {
+                    PendingCallback pendingCallback = pendingCallbacks.valueAt(i);
+                    if (pendingCallback != null) {
+                        pendingCallback.sendPendingDisplayEvent();
+                    }
+                }mPendingCallbackSelfLocked.delete(uid);}}
@@ -3193,16 +3199,23 @@for (int i = 0; i < mTempCallbacks.size(); i++) {CallbackRecord callbackRecord = mTempCallbacks.get(i);final int uid = callbackRecord.mUid;
+            final int pid = callbackRecord.mPid;if (isUidCached(uid)) {// For cached apps, save the pending event until it becomes non-cachedsynchronized (mPendingCallbackSelfLocked) {
-                    PendingCallback pendingCallback = mPendingCallbackSelfLocked.get(uid);
+                    SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(
+                            uid);if (extraLogging(callbackRecord.mPackageName)) {
-                        Slog.i(TAG,
-                                "Uid is cached: " + uid + ", pendingCallback: " + pendingCallback);
+                        Slog.i(TAG, "Uid is cached: " + uid
+                                + ", pendingCallbacks: " + pendingCallbacks);}
+                    if (pendingCallbacks == null) {
+                        pendingCallbacks = new SparseArray<>();
+                        mPendingCallbackSelfLocked.put(uid, pendingCallbacks);
+                    }
+                    PendingCallback pendingCallback = pendingCallbacks.get(pid);if (pendingCallback == null) {
-                        mPendingCallbackSelfLocked.put(uid,
+                        pendingCallbacks.put(pid,new PendingCallback(callbackRecord, displayId, event));} else {pendingCallback.addDisplayEvent(displayId, event);

看注释,说的就是多个进程共享同一个uid的情况。

再看修改,将原来的:

public final SparseArray<PendingCallback> mPendingCallbackSelfLocked = new SparseArray<>();

改为了:

    public final SparseArray<SparseArray<PendingCallback>> mPendingCallbackSelfLocked =new SparseArray<>();

很明显,之前是一个uid对应一个PendingCallback对象,现在是一个uid对应一组PendingCallback对象,即一个uid对应多个进程的情况,这样同一个uid下多个进程都能得到通知了,代码比较简单,不再赘述。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2024年11月16日 星期六 重新整理Go技术

今日格言 坚持每天进步一点点~ 一个人也可以是一个团队~ 学习全栈开发, 做自己喜欢的产品~~ 简介 大家好, 我是张大鹏, 今天是2024年11月16日星期六, 很高兴在这里给大家分享技术. 今天又是休息的一天, 做了很多的思考, 整理了自己掌握的技术, 比如Java, Python, Golang,…

go-zero(一) 介绍和使用

go-zero 介绍和使用 一、什么是 go-zero&#xff1f; go-zero 是一个基于 Go 语言的微服务框架&#xff0c;提供了高效、简单并易于扩展的 API 设计和开发模式。它主要目的是为开发者提供一种简单的方式来构建和管理云原生应用。 1.go-zero 的核心特性 高性能&#xff1a; g…

VUE+SPRINGBOOT实现邮箱注册、重置密码、登录功能

随着互联网的发展&#xff0c;网站用户的管理、触达、消息通知成为一个网站设计是否合理的重要标志。目前主流互联网公司都支持手机验证码注册、登录。但是手机短信作为服务端网站是需要付出运营商通信成本的&#xff0c;而邮箱的注册、登录、重置密码&#xff0c;无疑成为了这…

多目标优化算法:多目标蛇鹫优化算法(MOSBOA)求解ZDT1、ZDT2、ZDT3、ZDT4、ZDT6,提供完整MATLAB代码

一、蛇鹫优化算法 蛇鹫优化算法&#xff08;Secretary Bird Optimization Algorithm&#xff0c;简称SBOA&#xff09;由Youfa Fu等人于2024年4月发表在《Artificial Intelligence Review》期刊上的一种新型的元启发式算法。该算法旨在解决复杂工程优化问题&#xff0c;特别是…

2024-11-15 Java开发工程师 内推

Java开发工程师 坐标&#xff1a;大连 岗位要求&#xff1a; 1、本科以上学历&#xff0c;计算机相关专业 2、22/23/24届毕业生 小结&#xff1a;有意向的私信发简历

Python绘制雪花

文章目录 系列目录写在前面技术需求完整代码代码分析1. 代码初始化部分分析2. 雪花绘制核心逻辑分析3. 窗口保持部分分析4. 美学与几何特点总结 写在后面 系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4…

当 docker-compose.yaml 文件部署时,Dify 线上版本升级过程

如果线上 Dify 是通过 docker-compose.yaml 文件部署的&#xff0c;那么当 Dify 版本升级时该如何操作呢&#xff1f;官方已经给出了 Docker compose 和 Source Code 两种方式。相对而言&#xff0c;前者更简单些&#xff0c;至少不需要安装依赖包和迁移数据库文件。为了更加具…

RHCSA学习超详细知识点2命令篇

输入命令行的语法 终端中执行命令需要遵照一定的语法&#xff0c;输入命令的格式如下&#xff1a; 命令 参数命令 -选项 参数 输入命令时可以包含多个选项&#xff0c;假如一个命令有-a,-b,-c,-d四个选项&#xff0c;可以写作 命令 -a -b -c -d 参数 这里的多个选项可以“提…

小米路由器用外网域名访问管理界面

本文在Redmi AX3000 (RA81)设置&#xff0c;其他型号路由器的管理界面端口可能各不相同。 开始之前需要保证路由器SSH功能正常&#xff0c;如果没有SSH可以参考这里。 1. 给WAN口开放80端口 可以通过下载mixbox的firewall插件或者其他防火墙插件开放端口。 2. 把域名解析到路…

✅DAY27贪心算法 | 455.分发饼干 | 376. 摆动序列 | 53. 最大子序和

一、贪心算法 核心理念是每一步都做出局部最优选择&#xff0c;以期最终得到全局最优解。它通常用于求解一些最优化问题&#xff0c;例如最小生成树、最短路径、背包问题等。 二、贪心算法的步骤 1. 定义选择标准&#xff1a;确定每一步如何选择当前最优解。 2. 验证贪心策…

无人机+无人车+机器狗:城市巷战突破技术详解

在城市巷战中&#xff0c;无人机、无人车和机器狗的组合可以形成一种全新的战场突破技术&#xff0c;这种组合能够大幅提升作战效率&#xff0c;减少人员伤亡。以下是对这一技术的详细解析&#xff1a; 一、无人机的作用 1.空中侦察&#xff1a;无人机能够提供高空视角&#x…

智慧环保之环境网格化监测解决方案

1. 引言 智慧环保网格化监测解决方案&#xff0c;旨在通过先进的信息技术手段&#xff0c;实现环境质量的全面、精准、实时监测&#xff0c;为环境治理提供科学依据和决策支持。 2. 背景与挑战 当前&#xff0c;环境污染问题日益严峻&#xff0c;治理难度不断加大。传统监测手…

vue2+3 —— Day5/6

自定义指令 自定义指令 需求&#xff1a;当页面加载时&#xff0c;让元素获取焦点&#xff08;一进页面&#xff0c;输入框就获取焦点&#xff09; 常规操作&#xff1a;操作dom “dom元素.focus()” 获取dom元素还要用ref 和 $refs <input ref"inp" type&quo…

2024 RISC-V 中国峰会 演讲幻灯片和视频回放 均已公开

目录 一、幻灯片地址&#xff1a; 二、演讲视频&#xff1a; 一、幻灯片地址&#xff1a; RVSC2024/slides at main cnrv/RVSC2024 GitHub 二、演讲视频&#xff1a; RISC-V国际基金会的个人空间-RISC-V国际基金会个人主页-哔哩哔哩视频 参考&#xff1a; 1、RISC-V 中…

sudu问题 --> 添加用户到系统配置文件里面

我们重新创建一个用户lisi 密码&#xff1a;lisixxxxxxxx 重新连接&#xff0c;输入用户&#xff0c;输入密码 是不行的 我们输入exit退出 打开文件 再切换身份&#xff0c;切换为超级管理员root 输入密码 再打开 我们打开后设置行号在50行左右可以看到我们的目标 我们再进行…

C++: string(二)

✨✨ 欢迎大家来到我的文章✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 分类专栏&#xff1a;c 我的主页&#xff1a;tyler s blog 文章目录 一 string的成员函数1 insert2 resize3assign4erase5replace6 find(1) find(2)rfind…

【无人机设计与控制】基于MATLAB的四旋翼无人机PID双闭环控制研究

摘要 本文基于MATLAB/Simulink环境&#xff0c;对四旋翼无人机进行了PID双闭环控制设计与仿真研究。通过分析四旋翼无人机的动力学模型与运动学模型&#xff0c;建立了姿态和位置双闭环控制系统&#xff0c;以实现无人机的稳定飞行与精确轨迹跟踪。仿真实验验证了该控制策略的…

DimensionX 部署笔记

目录 生成视频用CogVideoX-5b-I2V 推理代码&#xff1a; DimensionX 生成视频用CogVideoX-5b-I2V 推理代码&#xff1a; 可以生成&#xff0c;从左向右旋转的&#xff0c;也可以生成从上往下旋转的&#xff1a; import torch from diffusers import CogVideoXImageToVideo…

如何利用SAP低代码平台快速构建企业级应用?

SAP作为全球领先的企业管理软件解决方案提供商&#xff0c;一直致力于为企业提供全面且高效的业务管理工具。随着技术的快速发展&#xff0c;传统的开发方式已经无法满足企业在快速变化的市场环境下的需求。低代码开发平台应运而生&#xff0c;它通过简化应用程序的创建过程&am…

拉取docker镜像应急方法

发现许多docker hub镜像网址速度也慢得发指啦&#xff0c;如果想速度快点&#xff0c;可以考虑买个按量计费的公有云服务器&#xff0c;用他们的内网镜像&#xff0c;然后再导出&#xff0c;然后传到本地。 开通服务器 可以考虑个开通最低配的&#xff0c;这里我用的是腾讯的…