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

Android 中 app freezer 原理详解(二):S 版本

基于版本:Android S

0. 前言

在之前的两篇博文《Android 中app内存回收优化(一)》《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理,为什么叫这个名字,而不叫 AppCompact 等?在之前的两篇博文中也提到了,因为该类中还管理了一个重要功能:freezer,一个针对应用进程长期处于 Cached 状态的优化。

在之前博文《app freezer 原理 R 版本》中简单的剖析了app freeze / unfreeze 的流程,但从代码逻辑上来看,笔者觉得 R 版本上有些逻辑是存在问题的,好在S 版本中都修复了。

本文将以 R 版本的原理为基础,对 S 版本进行对比分析。

1. freezer 触发

R 版本中,S 版本的触发也是在 applyOomAdjLSP() 函数中调用 updateAppFreezeStateLSP() 来确认是否冻结进程:

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate void updateAppFreezeStateLSP(ProcessRecord app) {// 确认该功能是否使能,如果没有使能则返回if (!mCachedAppOptimizer.useFreezer()) {return;}// S 版本新加的,确认应用是否可以豁免if (app.mOptRecord.isFreezeExempt()) {return;}// S 版本中cached进程优化相关的属性,都放到了mOptRecord中管理final ProcessCachedOptimizerRecord opt = app.mOptRecord;// 如果进程处于frozen状态,但shouldNotFreeze变成true,需要解冻if (opt.isFrozen() && opt.shouldNotFreeze()) {mCachedAppOptimizer.unfreezeAppLSP(app);return;}// 确定adj,是进入freeze 还是 unfreeze 流程final ProcessStateRecord state = app.mState;if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()&& !opt.shouldNotFreeze()) {mCachedAppOptimizer.freezeAppAsyncLSP(app);} else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {mCachedAppOptimizer.unfreezeAppLSP(app);}}

该函数与 R 版本基本逻辑差不多,稍微有些差异:

  • 进程有了是否freeze 豁免的功能,当应用设置了 INSTALL_PACKAGES 的权限之后,该应用处于豁免状态,详细看 ProcessList.startProcessLocked() 函数;
  • S 版本中cached进程的一些状态,统一由ProcessRecord 中的 mOptRecord 维护;
  • 最后再unfreeze 调用时,不再判断app 是否处于 frozen,因为这部分逻辑判断会在 unfreeze 函数中判定,这让代码更简介,R 版本中有些多余;

2. CachedAppOptimizer.init()

对于CachedAppOptimizer 的构造调用,以及 init() 函数的触发流程,可以参考《Android 中app内存回收优化(二)》 一文第 1 节 和 第 2 节。

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javapublic void init() {...DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,ActivityThread.currentApplication().getMainExecutor(),mOnNativeBootFlagsChangedListener);mAm.mContext.getContentResolver().registerContentObserver(CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);synchronized (mPhenotypeFlagLock) {...updateUseFreezer();...}}

相比较与 R 版本,这里多了两个功能:

  • 新加一个 freeze_debounce_timeout 属性发生变化的 listener,当该属性变化时,会调用 updateFreezerDebounceTimeout() 进行更新;
  • 新加了 cached_apps_freezer 属性值发生变化的 observer;

R 版本中,freeze timeout 是10min,使用的是常量。而在 S 版本中,将该值设计为可变的,用户可以通过 DeviceConfig 进行修改。当发生变化时,会调用 updateFreezerDebounceTimeout()进行更新。

另外,在 S 版本中,对 cached_apps_freezer 的值做了一个observer,及时控制 freezer 的使能。

2.1 updateUseFreezer()

    private void updateUseFreezer() {// 获取 settings中属性 cached_apps_freezer的值,同R 版本final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),Settings.Global.CACHED_APPS_FREEZER_ENABLED);if ("disabled".equals(configOverride)) {mUseFreezer = false;} else if ("enabled".equals(configOverride)|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {mUseFreezer = isFreezerSupported();updateFreezerDebounceTimeout();} else {mUseFreezer = false;}final boolean useFreezer = mUseFreezer;// enableFreezer() would need the global ActivityManagerService lock, post it.mAm.mHandler.post(() -> {if (useFreezer) {Slog.d(TAG_AM, "Freezer enabled");enableFreezer(true);if (!mCachedAppOptimizerThread.isAlive()) {mCachedAppOptimizerThread.start();}if (mFreezeHandler == null) {mFreezeHandler = new FreezeHandler();}Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),mCompactionPriority);} else {Slog.d(TAG_AM, "Freezer disabled");enableFreezer(false);}});}

针对 R 版本也做了个优化,将 enableFreezer() 的处理进行异步处理,因为在 S 版本中做了一个很大的调整。详细查看下文第 4 节。

freezer 功能是否使能,用流程图说明比较清晰:

需要注意的是,在获取freezer 节点的时候,与R 版本有所不同:

  • R 版本是,直接指定节点 /sys/fs/cgroup/freezer/cgroup.freeze;
  • S 版本是,通过函数 getFreezerCheckPath() 向libprocessgroup 中查找 pid 所对应的节点;

3. cgroups 简介

这里不再补充,详细可以查看 R 版本《Android 中 cgroup抽象层详解》

这里需要注意的是,R 版本中的 freezer 中,通过 /sys/fs/cgroup/freezer/cgroup.freeze 来确定是否使能 freezer,通过 /sys/fs/cgroup/freezer/cgroup.procs 来进行 frozen / unfrozen 操作。

而在 S 版本中,frozen/unfrozen 的操作通过 /sys/fs/cgroup/uid_xxx/pid_xxx/cgroup.freeze 节点。

在 S 版本中 cgroups.json 有所不同:

  "Cgroups2": {"Path": "/sys/fs/cgroup","Mode": "0755","UID": "system","GID": "system","Controllers": [{"Controller": "freezer","Path": ".","Mode": "0755","UID": "system","GID": "system"}]}

不再出现 freezer 目录,而是直接在 /sys/fs/cgroup 目录下创建 uid_xxx/pid_xxx 目录。

详细看下面第 5.2.1 节。

4. enableFreezer()

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javapublic synchronized boolean enableFreezer(boolean enable) {if (!mUseFreezer) {return false;}if (enable) {mFreezerDisableCount--;if (mFreezerDisableCount > 0) {return true;} else if (mFreezerDisableCount < 0) {Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring");mFreezerDisableCount = 0;return false;}} else {mFreezerDisableCount++;if (mFreezerDisableCount > 1) {return true;}}// Override is applied immediately, restore is delayedsynchronized (mAm) {synchronized (mProcLock) {mFreezerOverride = !enable;Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);mAm.mProcessList.forEachLruProcessesLOSP(true, process -> {if (process == null) {return;}final ProcessCachedOptimizerRecord opt = process.mOptRecord;if (enable && opt.hasFreezerOverride()) {freezeAppAsyncLSP(process);opt.setFreezerOverride(false);}if (!enable && opt.isFrozen()) {unfreezeAppLSP(process);// Set freezerOverride *after* calling unfreezeAppLSP (it resets the flag)opt.setFreezerOverride(true);}});}}return true;}

代码与 R 版本 不同,这里通过 mAm.mProcessList.forEachLruProcessesLOSP() 对每个LRU 中的进程进行确认。

进程中引入了 mOptRecord.mFreezerOverride 属性,用以标记对某个进程是否进行 disable freezer 操作。若该值为 true,则表示进程被 disable freezer 过。此时如果再次 enable,需对进程进行 freeze 请求。

当然,CachedAppOptimizer 类中也有这样的成员变量 mFreezerOverride,这个用以控制从外部调用的 freezeAppAsyncLSP()。如果该值为 true,则表示disable freezer了,外部如果有调用 freezeAppAsyncLSP(),则不需要去处理。

5. freezeAppAsyncLSP()

    void freezeAppAsyncLSP(ProcessRecord app) {final ProcessCachedOptimizerRecord opt = app.mOptRecord;if (opt.isPendingFreeze()) {// Skip redundant DO_FREEZE messagereturn;}mFreezeHandler.sendMessageDelayed(mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),mFreezerDebounceTimeout);opt.setPendingFreeze(true);}

相比较 R 版本,这里做了一个保护,防止反复进行 freeze 请求。

另外,timeout 从 R 版本中的常量 FREEZE_TIMEOUT_MS 改成可变的 debounce timeout。

注意:冻结时异步操作,使用 CachedAppOptimizer类中定义的 ServiceThread 进行,而解冻是东部操作,没有通过 ServiceThread。

5.1 freeze 消息处理

        public void handleMessage(Message msg) {switch (msg.what) {case SET_FROZEN_PROCESS_MSG:synchronized (mAm) {freezeProcess((ProcessRecord) msg.obj);}break;case REPORT_UNFREEZE_MSG:int pid = msg.arg1;int frozenDuration = msg.arg2;String processName = (String) msg.obj;reportUnfreeze(pid, frozenDuration, processName);break;default:return;}}

对于 freezer 一共有两个消息,REPORT_UNFREEZE_MSG 这个消息是在 unfreeze 之后进行记录的。

本文重点来看下 freeze 的消息处理,这里看到最终调用的是 freezeProcess() 函数,详细查看下一小节

5.2 freezeProcess()

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javaprivate void freezeProcess(final ProcessRecord proc) {int pid = proc.getPid(); // Unlocked intentionallyfinal String name = proc.processName;final long unfrozenDuration;final boolean frozen;final ProcessCachedOptimizerRecord opt = proc.mOptRecord;opt.setPendingFreeze(false);try {// 确认进程是否存在任意的文件锁,避免不必要的 free/unfreeze操作//   这是为了防止冻结进程持有文件锁,从而引起死锁//   冻结成功之后,还会再次确认文件锁,如果有锁,则立即解冻if (mProcLocksReader.hasFileLocks(pid)) {if (DEBUG_FREEZER) {Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");}return;}} catch (Exception e) {Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid+ "): " + e);return;}synchronized (mProcLock) {pid = proc.getPid();// 如果进程没有变成Cached或者不能freeze,则退出此次freeze操作if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ|| opt.shouldNotFreeze()) {return;}// 如果freezer 处于disable状态,返回,并告知该进程if (mFreezerOverride) {opt.setFreezerOverride(true);return;}// 已经处于frozen,或者不是一个应用进程,则退出此次 freeze操作//   pid为0,有可能进程还没有launch完成,或者进程被kill了if (pid == 0 || opt.isFrozen()) {// Already frozen or not a real process, either one being// launched or one being killedreturn;}Slog.d(TAG_AM, "freezing " + pid + " " + name);// 在S版本中,将这部分功能提前,也是正确的行为// 冻结 binder//    1.如果freezer是使能,将同步发送所有的pending 交互给指定的pid;//    2.该函数调用后,所有的binder 请求,都会被block,并返回error给发送请求的进程;try {if (freezeBinder(pid, true) != 0) {rescheduleFreeze(proc, "outstanding txns");return;}} catch (RuntimeException e) { //如果调用失败,则直接kill该进程Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);mFreezeHandler.post(() -> {synchronized (mAm) {proc.killLocked("Unable to freeze binder interface",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);}});}long unfreezeTime = opt.getFreezeUnfreezeTime();//核心函数 setProcessFrozen(),同步冻结进程try {Process.setProcessFrozen(pid, proc.uid, true);opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());opt.setFrozen(true);} catch (Exception e) {Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);}unfrozenDuration = opt.getFreezeUnfreezeTime() - unfreezeTime;frozen = opt.isFrozen();}if (!frozen) {return;}Slog.d(TAG_AM, "froze " + pid + " " + name);// 将此次 freeze 记录到 event log 中EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);// See above for why we're not taking mPhenotypeFlagLock hereif (mRandom.nextFloat() < mFreezerStatsdSampleRate) {FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,pid,name,unfrozenDuration);}// 确认在冻结的时候,是否收到了 TXNS_PENDING_WHILE_FROZEN的binder请求,//    如果有有收到请求,则重新freeze 该进程(unfreeze + freeze)try {// post-check to prevent racesint freezeInfo = getBinderFreezeInfo(pid);if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {synchronized (mProcLock) {rescheduleFreeze(proc, "new pending txns");}return;}} catch (RuntimeException e) {Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);mFreezeHandler.post(() -> {synchronized (mAm) {proc.killLocked("Unable to freeze binder interface",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);}});}try {// 再次check文件锁,如果该冻结进程持有文件锁,立即unfreezeif (mProcLocksReader.hasFileLocks(pid)) {if (DEBUG_FREEZER) {Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");}unfreezeAppLSP(proc);}} catch (Exception e) {Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);unfreezeAppLSP(proc);}}

逻辑与R 版本有很大的不同:

  • 在冻结进程之前,调用 freezeBinder(),用以冻结binder 通信;
  • 在冻结进程之后,调用 getBinderFreezeInfo(),确认是否在冻结的时候有 TXNS_PENDING_WHILE_FROZEN 的binder 请求,如果有该请求,则重新freeze 该进程(unfreeze + freeze);

弄个流程图理解冻结过程:

 

 5.2.1 setProcessFrozen()

frameworks/base/core/java/android/os/Process.javapublic static final native void setProcessFrozen(int pid, int uid, boolean frozen);
frameworks/base/core/jni/android_util_Process.cppvoid android_os_Process_setProcessFrozen(JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze)
{bool success = true;if (freeze) {success = SetProcessProfiles(uid, pid, {"Frozen"});} else {success = SetProcessProfiles(uid, pid, {"Unfrozen"});}if (!success) {signalExceptionForGroupError(env, EINVAL, pid);}
}

此处的调用在博文《cgroup抽象层》中已经分析过,通过接口 SetProcessProfiles() 精细是 SetAttributeAction 类型的profile,最终调用 ExecuteForProcess():

system/core/libprocesscgroup/task_profiles.cppbool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {return ExecuteForTask(pid);
}bool SetAttributeAction::ExecuteForTask(int tid) const {std::string path;if (!attribute_->GetPathForTask(tid, &path)) {LOG(ERROR) << "Failed to find cgroup for tid " << tid;return false;}if (!WriteStringToFile(value_, path)) {PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;return false;}return true;
}

通过代码需要确定 attribute_->GetPathForTask():

system/core/libprocessgroup/task_profiles.cppbool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {std::string subgroup;if (!controller()->GetTaskGroup(tid, &subgroup)) {return false;}if (path == nullptr) {return true;}if (subgroup.empty()) {*path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());} else {*path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),file_name_.c_str());}return true;
}

有两部分,一个是通过 controller 获取subgroup,二是进行最终path 的拼接。

下面来看下 congtroller 指向的 GetTaskGroup()

system/core/libprocessgroup/cgroup_map.cppbool CgroupController::GetTaskGroup(int tid, std::string* group) const {std::string file_name = StringPrintf("/proc/%d/cgroup", tid);std::string content;if (!android::base::ReadFileToString(file_name, &content)) {PLOG(ERROR) << "Failed to read " << file_name;return false;}// if group is null and tid exists return early because// user is not interested in cgroup membershipif (group == nullptr) {return true;}std::string cg_tag;if (version() == 2) {cg_tag = "0::";} else {cg_tag = StringPrintf(":%s:", name());}size_t start_pos = content.find(cg_tag);if (start_pos == std::string::npos) {return false;}start_pos += cg_tag.length() + 1;  // skip '/'size_t end_pos = content.find('\n', start_pos);if (end_pos == std::string::npos) {*group = content.substr(start_pos, std::string::npos);} else {*group = content.substr(start_pos, end_pos - start_pos);}return true;
}

读取 /proc/PID/cgroup 中信息,对于 cgroup2,读取是 0:: 所在信息,在 S 版本中指定的是uid和pid 拼接,例如:

130|shift:/sys/fs/cgroup # cat /proc/2119/cgroup
4:memory:/
3:cpuset:/restricted
2:cpu:/foreground
1:blkio:/foreground
0::/uid_10083/pid_2119

最终写的是 /sys/fs/cgroup/uid_xxx/pid_xxx/cgroup.freeze 文件。

某个进程被冻结的旅程图:

6. unfreezeAppLSP()

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.javavoid unfreezeAppLSP(ProcessRecord app) {final int pid = app.getPid();final ProcessCachedOptimizerRecord opt = app.mOptRecord;// 进程已经处于peding freeze中,移除冻结消息if (opt.isPendingFreeze()) {// Remove pending DO_FREEZE messagemFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);opt.setPendingFreeze(false);}opt.setFreezerOverride(false);// 如果进程还没有冻结,则无需做解冻处理if (!opt.isFrozen()) {return;}// 冻住的进程可以接收异步binder请求,但是不会处理,只是放入binder buffer, 过多的请求会导致buffer耗尽;// 这里需要确认下该进程在解冻之前,进程是否在冰冻期间收到同步的binder 请求,有则kill该进程boolean processKilled = false;try {int freezeInfo = getBinderFreezeInfo(pid);if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {Slog.d(TAG_AM, "pid " + pid + " " + app.processName+ " received sync transactions while frozen, killing");app.killLocked("Sync transaction while in frozen state",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true);processKilled = true;}if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0 && DEBUG_FREEZER) {Slog.d(TAG_AM, "pid " + pid + " " + app.processName+ " received async transactions while frozen");}} catch (Exception e) {Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "+ app.processName + ". Killing it. Exception: " + e);app.killLocked("Unable to query binder frozen stats",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);processKilled = true;}//进程被kill 了,无需 unfreezeif (processKilled) {return;}// app.freezeUnfreezeTime记录的是上次free、unfreeze的时间long freezeTime = opt.getFreezeUnfreezeTime();try {freezeBinder(pid, false);} catch (RuntimeException e) {Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName+ ". Killing it");app.killLocked("Unable to unfreeze",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);return;}try {Process.setProcessFrozen(pid, app.uid, false);opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());opt.setFrozen(false);} catch (Exception e) {Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName+ ". This might cause inconsistency or UI hangs.");}if (!opt.isFrozen()) {Slog.d(TAG_AM, "sync unfroze " + pid + " " + app.processName);mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,pid,(int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),app.processName));}}

逻辑比较清晰,核心处理函数是 setProcessFrozen(),详细的流程在上面第 5.2.1 节中已经分析过。

7. kernel 处理

kernel/cgroup/cgroup.cstatic struct cftype cgroup_base_files[] = {...{.name = "cgroup.freeze",.flags = CFTYPE_NOT_ON_ROOT,.seq_show = cgroup_freeze_show,.write = cgroup_freeze_write,},...
};

/sys/fs/cgroup/uid_xxx/pid_xxx/cgroup.freeze 文件的写入触发回调函数 cgroup_freeze_write() 函数,通过当前的 kernfs 找到对应的 cgroup,将这个 cgroup 下所有进程以及子进程都 freeze。

详细的 cgroup_freeze_write() 函数,后续将补充剖析。

 

 

 

 
相关博文:

https://justinwei.blog.csdn.net/article/details/131845360

https://justinwei.blog.csdn.net/article/details/131854291 

https://justinwei.blog.csdn.net/article/details/131769953

https://justinwei.blog.csdn.net/article/details/131591931

https://justinwei.blog.csdn.net/article/details/131717028

https://justinwei.blog.csdn.net/article/details/131685304

https://justinwei.blog.csdn.net/article/details/131595511

相关文章:

Android 中 app freezer 原理详解(二):S 版本

基于版本&#xff1a;Android S 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理&#xff0c;为什么叫这个名字&#xff0c;而不…...

Vue3_04_ref 函数和 reactive 函数

ref 函数 声明变量时&#xff0c;赋值的值要写在 ref() 函数中修改变量时&#xff0c;变量名.value xxx在模板中使用时可以省略掉 .value&#xff0c;直接使用变量名即可 <template><h1>一个人的信息</h1><h2>姓名&#xff1a;{{name}}</h2><…...

05 Ubuntu下安装.deb安装包方式安装vscode,snap安装Jetbrains产品等常用软件

使用deb包安装类型 deb包指的其实就是debian系统&#xff0c;ubuntu系统是基于debian系统的发行版。 一般我们会到需要的软件官网下载deb安装包&#xff0c;然后你既可以采用使用“软件安装”打开的方法来进行安装&#xff0c;也可以使用命令行进行安装。我推荐后者&#xff…...

性能测试jmeter连接数据库jdbc(sql server举例)

一、下载第三方工具包驱动数据库 1. 因为JMeter本身没有提供链接数据库的功能&#xff0c;所以我们需要借助第三方的工具包来实现。 &#xff08;有这个jar包之后&#xff0c;jmeter可以发起jdbc请求&#xff0c;没有这个jar包&#xff0c;也有jdbc取样器&#xff0c;但不能发起…...

8.3 C高级 Shell脚本

写一个脚本&#xff0c;包含以下内容&#xff1a; 显示/etc/group文件中第五行的内容创建目录/home/ubuntu/copy切换工作路径到此目录赋值/etc/shadow到此目录&#xff0c;并重命名为test将当前目录中test的所属用户改为root将test中其他用户的权限改为没有任何权限 #!/bin/b…...

2023年华数杯A题

A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前&#xff0c;由单根隔热材料 A 纤维编织成的织物&#xff0c;其热导率可以直接测出&#xff1b;但是 单根隔热材料 A 纤维…...

【零基础学Rust | 基础系列 | 函数,语句和表达式】函数的定义,使用和特性

文章标题 简介一&#xff0c;函数1&#xff0c;函数的定义2&#xff0c;函数的调用3&#xff0c;函数的参数4&#xff0c;函数的返回值 二&#xff0c;语句和表达式1&#xff0c;语句2&#xff0c;表达式 总结&#xff1a; 简介 在Rust编程中&#xff0c;函数&#xff0c;语句…...

加解密算法+压缩工具

sha256 工具类 package com.fanghui.vota.packages.util;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger…...

FeignClient接口的几种方式总结

FeignClient这个注解&#xff0c;已经封装了远程调用协议。在springboot的开发&#xff0c;或者微服务的开发过程中&#xff0c;我们需要跨服务调用&#xff0c;或者调用外部的接口&#xff0c;我们都可以使用FeignClient。 一、FeignClient介绍 FeignClient 注解是 Spring Cl…...

springBoot多数据源使用tdengine(3.0.7.1)+MySQL+mybatisPlus+druid连接池

一、安装部署 1、我这里使用的 3.0.7.1版本&#xff0c;因为我看3.x版本已经发布了一年了&#xff0c;增加了很多新的功能&#xff0c;而且3.x官方推荐&#xff0c;对于2.x的版本&#xff0c;官网都已经推荐进行升级到3.x&#xff0c;所以考虑到项目以后的发展&#xff0c;决定…...

剑指Offer 05.替换空格

剑指Offer 05.替换空格 目录 剑指Offer 05.替换空格05.替换空格题目代码&#xff08;容易想到的&#xff09;利用库函数的方法题解&#xff08;时间复杂度更低&#xff09;面试&#xff1a;为什么java中String类型是不可变的 05.替换空格 题目 官网题目地址 代码&#xff08;…...

ChatGPT的功能与特点

随着人工智能技术的不断发展&#xff0c;ChatGPT作为OpenAI公司开发的基于GPT-3.5架构的大型语言模型&#xff0c;正引领着智能交互的新纪元。ChatGPT的功能与特点使其能够在多个领域展现出惊人的能力&#xff0c;本文将深入探讨ChatGPT的功能与特点&#xff0c;以及它在人工智…...

Vue2.0基础

1、概述 Vue(读音/vju/&#xff0c;类似于view)是一套用于构建用户界面的渐进式框架&#xff0c;发布于2014年2月。与其它大型框架不同的是&#xff0c;Vue被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff08;也就是可以理解为HTMLCSSJS&#xff09;&#xff…...

rust 如何定义[u8]数组?

在Rust中&#xff0c;有多种方式可以定义 [u8] 数组。以下是一些常见的方式&#xff1a; 使用数组字面量初始化数组&#xff1a; let array: [u8; 5] [1, 2, 3, 4, 5];使用 vec! 宏创建可变长度的数组&#xff1a; let mut vec: Vec<u8> vec![1, 2, 3, 4, 5];使用 v…...

关于Hive的使用技巧

前言 Hive是一个基于Hadoop的数据仓库基础架构&#xff0c;它提供了一种类SQL的查询语言&#xff0c;称为HiveQL&#xff0c;用于分析和处理大规模的结构化数据。 Hive的主要特点包括&#xff1a; 可扩展性&#xff1a;Hive可以处理大规模的数据&#xff0c;支持高性能的并行…...

【C++】BSTree 模拟笔记

文章目录 概念插入和删除非递归实现中的问题递归中的引用简化相关OJ复习直达 概念 由下面二叉搜索树的性质可以知道&#xff0c;中序遍历它便可以得到一个升序序列&#xff0c;查找效率高&#xff0c;小于往左找&#xff0c;大于往右走。最多查找高度次&#xff0c;走到到空&am…...

5分钟快手入门laravel邮件通知

第一步&#xff1a; 生成一个邮件发送对象 php artisan make:mail TestMail 第二步&#xff1a; 编辑.env 添加/修改&#xff08;没有的key则添加&#xff09; MAIL_DRIVERsmtp MAIL_HOSTsmtp.163.com &#xff08;这里用163邮箱&#xff09; MAIL_PORT25 &#xff08;163邮箱…...

iOS——Block two

Block 的实质究竟是什么呢&#xff1f;类型&#xff1f;变量&#xff1f;还是什么黑科技&#xff1f; Blocks 是 带有局部变量的匿名函数 Blocks 由 OC 转 C 源码方法 在项目中添加 blocks.m 文件&#xff0c;并写好 block 的相关代码。打开「终端」&#xff0c;执行 cd XX…...

Ubuntu出现内部错误解决办法

使用的Ubuntu版本是18.04&#xff0c;使用的时候弹出对话框说出现了内部错误&#xff0c;好奇是哪里出现了错误&#xff0c;查找了一下解决的办法&#xff0c;记录一下。 参考解决方案&#xff1a;ubantu出现了内部错误 一旦程序崩溃过一次&#xff0c;就会生成一个.crash文件…...

2023年中职组“网络安全”赛项吉安市竞赛任务书

2023年中职组“网络安全”赛项 吉安市竞赛任务书 一、竞赛时间 总计&#xff1a;360分钟 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略配置 A-3 流量完整性保护 A-4 事件监控 A-5 服务加固…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...