鸿蒙HarmonyOS实战:IPC与RPC设备内进程通信

基本

IPC(Inter-Process Communication)与RPC(Remote Procedure Call)用于实现跨进程通信,不同的是前者使用Binder驱动,用于设备内的跨进程通信,后者使用软总线驱动,用于跨设备跨进程通信。需要跨进程通信的原因是因为每个进程都有自己独立的资源和内存空间,其他进程不能随意访问不同进程的内存和资源,IPC/RPC便是为了突破这一点。

说明

Stage模型不能直接使用本文介绍的IPC和RPC,需要通过以下能力实现相关业务场景:

  • IPC典型使用场景在后台服务,应用的后台服务通过IPC机制提供跨进程的服务调用能力。
  • RPC典型使用场景在多端协同,多端协同通过RPC机制提供远端接口调用与数据传递能力。

实现原理

IPC和RPC通常采用客户端-服务器(Client-Server)模型,在使用时,请求服务的(Client)一端进程可获取提供服务(Server)一端所在进程的代理(Proxy),并通过此代理读写数据来实现进程间的数据通信,更具体的讲,首先请求服务的(Client)一端会建立一个服务提供端(Server)的代理对象,这个代理对象具备和服务提供端(Server)一样的功能,若想访问服务提供端(Server)中的某一个方法,只需访问代理对象中对应的方法即可,代理对象会将请求发送给服务提供端(Server);然后服务提供端(Server)处理接受到的请求,处理完之后通过驱动返回处理结果给代理对象;最后代理对象将请求结果进一步返回给请求服务端(Client)。通常,Server会先注册系统能力(System Ability)到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。下文直接使用Proxy表示服务请求方,Stub表示服务提供方。

约束与限制

  • 单个设备上跨进程通信时,传输的数据量最大约为1MB,过大的数据量请使用匿名共享内存匿名共享内存匿名共享内存。

  • 不支持在RPC中订阅匿名Stub对象(没有向SAMgr注册Stub对象)的死亡通知。

  • 不支持把跨设备的Proxy对象传递回该Proxy对象所指向的Stub对象所在的设备,即指向远端设备Stub的Proxy对象不能在本设备内进行二次跨进程传递。

场景介绍

IPC/RPC的主要工作是让运行在不同进程的Proxy和Stub互相通信,包括Proxy和Stub运行在不同设备的情况。

开发步骤

ArkTS侧开发步骤

  • 此文档中的示例代码描述的是系统应用跨进程通信。

  • 当前不支持三方应用实现ServiceExtensionAbility,三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。

  • 当前使用场景: 仅限客户端是三方应用,服务端是系统应用。

  1. 添加依赖

     // FA模型需要从@kit.AbilityKit导入featureAbility// import { featureAbility } from '@kit.AbilityKit';import { rpc } from '@kit.IPCKit';
  2. 绑定Ability

    首先,构造变量want,指定要绑定的Ability所在应用的包名、组件名,如果是跨设备的场景,还需要绑定目标设备NetworkId(组网场景下对应设备的标识符,可以使用distributedDeviceManager获取目标设备的NetworkId);然后,构造变量connect,指定绑定成功、绑定失败、断开连接时的回调函数;最后,FA模型使用featureAbility提供的接口绑定Ability,Stage模型通过context获取服务后用提供的接口绑定Ability。

     // FA模型需要从@kit.AbilityKit导入featureAbility// import { featureAbility } from "@kit.AbilityKit";import { Want, common } from '@kit.AbilityKit';import { rpc } from '@kit.IPCKit';import { hilog } from '@kit.PerformanceAnalysisKit';import { distributedDeviceManager } from '@kit.DistributedServiceKit';import { BusinessError } from '@kit.BasicServicesKit';let dmInstance: distributedDeviceManager.DeviceManager | undefined;let proxy: rpc.IRemoteObject | undefined;let connectId: number;// 单个设备绑定Abilitylet want: Want = {// 包名和组件名写实际的值bundleName: "ohos.rpc.test.server",abilityName: "ohos.rpc.test.server.ServiceAbility",};let connect: common.ConnectOptions = {onConnect: (elementName, remoteProxy) => {hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called');proxy = remoteProxy;},onDisconnect: (elementName) => {hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect');},onFailed: () => {hilog.info(0x0000, 'testTag', 'RpcClient: onFailed');}};// FA模型使用此方法连接服务// connectId = featureAbility.connectAbility(want, connect);let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入connectId = context.connectServiceExtensionAbility(want,connect);// 跨设备绑定try{dmInstance = distributedDeviceManager.createDeviceManager("ohos.rpc.test");} catch(error) {let err: BusinessError = error as BusinessError;hilog.error(0x0000, 'testTag', 'createDeviceManager errCode:' + err.code + ', errMessage:' + err.message);}// 使用distributedDeviceManager获取目标设备NetworkIdif (dmInstance != undefined) {let deviceList = dmInstance.getAvailableDeviceListSync();let networkId = deviceList[0].networkId;let want: Want = {bundleName: "ohos.rpc.test.server",abilityName: "ohos.rpc.test.service.ServiceAbility",deviceId: networkId,flags: 256};// 建立连接后返回的Id需要保存下来,在断开连接时需要作为参数传入// FA模型使用此方法连接服务// connectId = featureAbility.connectAbility(want, connect);// 第一个参数是本应用的包名,第二个参数是接收distributedDeviceManager的回调函数connectId = context.connectServiceExtensionAbility(want,connect);}
  3. 服务端处理客户端请求

    服务端被绑定的Ability在onConnect方法里返回继承自rpc.RemoteObject的对象,该对象需要实现onRemoteMessageRequest方法,处理客户端的请求。

     import { rpc } from '@kit.IPCKit';import { Want } from '@kit.AbilityKit';class Stub extends rpc.RemoteObject {constructor(descriptor: string) {super(descriptor);}onRemoteMessageRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, option: rpc.MessageOption): boolean | Promise<boolean> {// 根据code处理客户端的请求return true;}onConnect(want: Want) {const robj: rpc.RemoteObject = new Stub("rpcTestAbility");return robj;}}
  4. 客户端处理服务端响应

    客户端在onConnect回调里接收到代理对象,调用sendMessageRequestsendMessageRequestsendMessageRequest方法发起请求,在期约(用于表示一个异步操作的最终完成或失败及其结果值)或者回调函数里接收结果。

     import { rpc } from '@kit.IPCKit';import { hilog } from '@kit.PerformanceAnalysisKit';// 使用期约let option = new rpc.MessageOption();let data = rpc.MessageSequence.create();let reply = rpc.MessageSequence.create();// 往data里写入参数let proxy: rpc.IRemoteObject | undefined;if (proxy != undefined) {proxy.sendMessageRequest(1, data, reply, option).then((result: rpc.RequestResult) => {if (result.errCode != 0) {hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);return;}// 从result.reply里读取结果}).catch((e: Error) => {hilog.error(0x0000, 'testTag', 'sendMessageRequest got exception: ' + e);}).finally(() => {data.reclaim();reply.reclaim();})}// 使用回调函数function sendRequestCallback(err: Error, result: rpc.RequestResult) {try {if (result.errCode != 0) {hilog.error(0x0000, 'testTag', 'sendMessageRequest failed, errCode: ' + result.errCode);return;}// 从result.reply里读取结果} finally {result.data.reclaim();result.reply.reclaim();}}let options = new rpc.MessageOption();let datas = rpc.MessageSequence.create();let replys = rpc.MessageSequence.create();// 往data里写入参数if (proxy != undefined) {proxy.sendMessageRequest(1, datas, replys, options, sendRequestCallback);}
  5. 断开连接

    IPC通信结束后,FA模型使用featureAbility的接口断开连接,Stage模型在获取context后用提供的接口断开连接。

     // FA模型需要从@kit.AbilityKit导入featureAbility// import { featureAbility } from "@kit.AbilityKit";import { Want, common } from '@kit.AbilityKit';import { rpc } from '@kit.IPCKit';import { hilog } from '@kit.PerformanceAnalysisKit';function disconnectCallback() {hilog.info(0x0000, 'testTag', 'disconnect ability done');}// FA模型使用此方法断开连接// featureAbility.disconnectAbility(connectId, disconnectCallback);let proxy: rpc.IRemoteObject | undefined;let connectId: number;// 单个设备绑定Abilitylet want: Want = {// 包名和组件名写实际的值bundleName: "ohos.rpc.test.server",abilityName: "ohos.rpc.test.server.ServiceAbility",};let connect: common.ConnectOptions = {onConnect: (elementName, remote) => {proxy = remote;},onDisconnect: (elementName) => {},onFailed: () => {proxy;}};// FA模型使用此方法连接服务// connectId = featureAbility.connectAbility(want, connect);connectId = this.context.connectServiceExtensionAbility(want,connect);this.context.disconnectServiceExtensionAbility(connectId);

远端状态订阅开发实例

IPC/RPC提供对远端Stub对象状态的订阅机制,在远端Stub对象消亡时,可触发消亡通知告诉本地Proxy对象。这种状态通知订阅需要调用特定接口完成,当不再需要订阅时也需要调用特定接口取消。使用这种订阅机制的用户,需要实现消亡通知接口DeathRecipient并实现onRemoteDied方法清理资源。该方法会在远端Stub对象所在进程消亡或所在设备离开组网时被回调。值得注意的是,调用这些接口有一定的顺序。首先,需要Proxy订阅Stub消亡通知,若在订阅期间Stub状态正常,则在不再需要时取消订阅;若在订阅期间Stub所在进程退出或者所在设备退出组网,则会自动触发Proxy自定义的后续操作。

使用场景

这种订阅机制适用于本地Proxy对象需要感知远端Stub对象所在进程消亡,或所在设备离开组网的场景。当Proxy感知到Stub端消亡后,可适当清理本地资源。此外,RPC目前不提供匿名Stub对象的消亡通知,即只有向SAMgr注册过的服务才能被订阅消亡通知,IPC则支持匿名对象的消亡通知。

ArkTS侧接口

  • 此文档中的示例代码描述的是系统应用跨进程通信。

  • 当前不支持三方应用实现ServiceExtensionAbility,三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。

  • 当前使用场景: 仅限客户端是三方应用,服务端是系统应用。

接口名返回值类型功能描述
registerDeathRecipientvoid注册用于接收远程对象消亡通知的回调,增加 proxy 对象上的消亡通知。
unregisterDeathRecipientvoid注销用于接收远程对象消亡通知的回调。
onRemoteDiedvoid在成功添加死亡通知订阅后,当远端对象死亡时,将自动调用本方法。

参考代码

// FA模型需要从@kit.AbilityKit导入featureAbility
// import { featureAbility } from '@kit.AbilityKit';
import { Want, common } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';let proxy: rpc.IRemoteObject | undefined;
let connect: common.ConnectOptions = {onConnect: (elementName, remoteProxy) => {hilog.info(0x0000, 'testTag', 'RpcClient: js onConnect called.');proxy = remoteProxy;},onDisconnect: (elementName) => {hilog.info(0x0000, 'testTag', 'RpcClient: onDisconnect');},onFailed: () => {hilog.info(0x0000, 'testTag', 'RpcClient: onFailed');}
};
let want: Want = {bundleName: "com.ohos.server",abilityName: "com.ohos.server.EntryAbility",
};
// FA模型通过此方法连接服务
// FA.connectAbility(want, connect);// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let connectionId = context.connectServiceExtensionAbility(want, connect);

上述onConnect回调函数中的proxy对象需要等ability异步连接成功后才会被赋值,然后才可调用proxy对象的unregisterDeathRecipient接口方法注销死亡回调

import { rpc } from '@kit.IPCKit';
import { hilog } from '@kit.PerformanceAnalysisKit';class MyDeathRecipient implements rpc.DeathRecipient{onRemoteDied() {hilog.info(0x0000, 'testTag', 'server died');}
}
let deathRecipient = new MyDeathRecipient();
if (proxy != undefined) {proxy.registerDeathRecipient(deathRecipient, 0);proxy.unregisterDeathRecipient(deathRecipient, 0);
}

Stub感知Proxy消亡(匿名Stub的使用)

正向的消亡通知是Proxy感知Stub的状态,若想达到反向的死消亡通知,即Stub感知Proxy的状态,可以巧妙的利用正向消亡通知。如两个进程A(原Stub所在进程)和B(原Proxy所在进程),进程B在获取到进程A的Proxy对象后,在B进程新建一个匿名Stub对象(匿名指未向SAMgr注册),可称之为回调Stub,再通过SendRequest接口将回调Stub传给进程A的原Stub。这样一来,进程A便获取到了进程B的回调Proxy。当进程B消亡或B所在设备离开组网时,回调Stub会消亡,回调Proxy会感知,进而通知给原Stub,便实现了反向消亡通知。

注意:

反向死亡通知仅限设备内跨进程通信使用,不可用于跨设备。

当匿名Stub对象没有被任何一个Proxy指向的时候,内核会自动回收。

参考代码

// Proxy
int TestAbilityProxy::TestAnonymousStub()
{MessageOption option;MessageParcel dataParcel, replyParcel;dataParcel.UpdateDataVersion(Remote());dataParcel.WriteRemoteObject(new TestAbilityStub());int error = Remote()->SendRequest(TRANS_ID_REVERSED_MONITOR,dataParcel, replyParcel, option);int result = (error == ERR_NONE) ? replyParcel.ReadInt32() : -1;return result;
}// Stubint TestAbilityStub::OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{switch (code) {case TRANS_ID_REVERSED_MONITOR: {sptr<IRemoteObject> obj = data.ReadRemoteObject();if (obj == nullptr) {reply.WriteInt32(ERR_NULL_OBJECT);return ERR_NULL_OBJECT;}bool result = obj->AddDeathRecipient(new TestDeathRecipient());result ? reply.WriteInt32(ERR_NONE) : reply.WriteInt32(-1);break;}default:break;}return ERR_NONE;
}

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习资料+学习PDF文档

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习资料(面试、文档、全套视频等)              

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

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

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

相关文章

Latex 插入图片或表格导致页面空白过多

如图所示&#xff1a; Latex 插入图片或表格导致页面空白过多 我们可以采用这个方式来减少空白。 \documentclass{article} \usepackage{graphicx} % 包含图形支持 \usepackage{caption} % 提供更多对caption的控制% 设置标题上方和下方的间距 \setlength{\abovecaptionskip}{…

学习记录:js算法(十二):柱状图中最大的矩形

文章目录 柱状图中最大的矩形我的思路网上思路 总结 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1:上图 输入&#xff1a;…

mac 安装Arthas

mac安装有两种方式 1.第一步安装Arthas 第一种&#xff1a; curl -L https://arthas.aliyun.com/install.sh | sh 第二种jar包形式 curl -O https://arthas.aliyun.com/arthas-boot.jar个人比较推荐第一种因为运行测试成功了 第一种安装后可能会出现一些命令不符合 需…

网安新声 | 从微软“狂躁许可”漏洞事件看安全新挑战与应对策略

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 近期&#xff0c;微软披露了一个最…

CyberScraper-2077+simple-one-api:使用大模型爬虫

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 swift与Internvl下的多模态大模型分布式微调指南&#xff08;附代码和数据&#xff…

C语言典型例题50

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 例题4.2 使用do……while语句循环求1234……100&#xff1b; 代码&#xff1a; //《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 //例题4.2 使用do……while语句循环求1234……100&#xff1b;#incl…

web后端(javaEE)开发——servlet

目录 一、web后端开发概述 二、web后端开发环境搭建 1.安装服务器软件 2.安装JDK 三、创建web后端项目 1.创建项目 2.修改设置 3.*在IDEA中集成Tomcat* 四、Servlet创建和应用 1.概述 2.Servlet程序创建与配置 3.分析Servlet程序结构 一、web后端开发概述 web开发&a…

Flutter ListView 实现不同样式 item

我们在实际开发中会创建显示不同类型内容的列表。以下是使用 Flutter 创建此类结构的方法&#xff1a; 1. 创建包含不同类型项目的数据源。 2. 将数据源转换为小部件列表。 创建包含不同类型项目的数据源 项目类型 要表示列表中不同类型的项目&#xff0c;请为每种类型的项目…

基于STM32开发的智能水箱液位控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化液位监测与控制水泵控制与状态显示Wi-Fi通信与远程监控应用场景 家庭用水系统的液位控制工业水箱的液位管理常见问题及解决方案 常见问题解决方案结论 1. 引言 智能水箱液位控制系…

初学Python如何快速入门(内附详细攻略),一文讲清

目前python可以说是一门非常火爆的编程语言&#xff0c;应用范围也非常的广泛&#xff0c;工资也挺高&#xff0c;未来发展也极好。 Python究竟应该怎么学呢&#xff0c;我自己最初也是从零基础开始学习Python的&#xff0c;给大家分享Python的学习思路和方法。一味的买书看书…

C语言基础(十二)

指针的用法&#xff1a; 测试代码1&#xff1a; #include "date.h" #include <stdio.h> // 函数声明&#xff0c;用于交换两个整数的值&#xff0c;通过指针传递 void swap(int *a, int *b); int main() { int time getTime();int nums[3] {3, 1, 4};…

【Python】列表和元组

文章目录 概念创建列表访问下标通过下标来修改列表元素获取列表长度下标可以写成负数 切片操作省略后边界省略前边界省略前后边界带有步长的切片 遍历列表元素使用 for 循环使用 for 循环访问下标的方式使用 while 循环 新增元素在末尾新增在任意位置新增 查找元素判定元素是否…

ESP RainMaker OTA 自动签名功能的安全启动

【如果您之前有关注乐鑫的博客和新闻&#xff0c;那么应该对 ESP RainMaker 及其各项功能有所了解。如果不曾关注&#xff0c;建议先查看相关信息&#xff0c;知晓本文背景。】 在物联网系统的建构中&#xff0c;安全性是一项核心要素。乐鑫科技对系统安全给予了极高的重视。ES…

【C# 】使用List<实体类>

1. 使用List<实体类> 要在C#中使用List<EntityTemp>并实现查找数据输出&#xff0c;首先需要定义EntityTemp类&#xff0c;并创建一个List<EntityTemp>类型的列表。然后&#xff0c;你可以使用LINQ或其他方法来查找和输出数据。 假设EntityTemp类具有一个…

Transformer总结(三):组件介绍(位置编码,多头注意,残差连接,层归一化,基于位置的前馈网络)

文章目录 一、位置编码1.1 介绍1.2 简单探讨 二、多头注意力2.1 一般的多头注意力机制2.2 解码器中的掩码多头注意力机制&#xff08;Look-ahead Mask&#xff09; 三、残差连接四、层归一化4.1 对比不同的Normalization4.2 Batch Normalization的实现4.3 Layer Normalization的…

Variomes:支持基因组变异筛选的高召回率搜索引擎

《Bioinformatics》2022 Variomes&#xff1a; https://candy.hesge.ch/Variomes Source code&#xff1a; https://github.com/variomes/sibtm-variomes SynVar&#xff1a; https://goldorak.hesge.ch/synvar 文章摘要&#xff08;Abstract&#xff09; 动机&#xff08;Mot…

电子电气架构--- 智能汽车电子架构的核心诉求

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

8.23工作感悟 关于div样式

一、关于div 样式嵌套在el-dialog框里面,如何让div框里面的东西&#xff0c;例如图片的大小不超出dialog框 首先对div框进行一个自定义class ‌div的class属性‌用于为div元素指定一个或多个类名&#xff0c;类名之间用空格分隔。类名可以在CSS样式表中使用&#xff0c;以定…

统计Statistics | AnyLogic 帮助

统计Statistics | AnyLogic 帮助 统计对象计算一系列数据样本的统计信息&#xff08;平均值、最小值、最大值等&#xff09;&#xff0c;数据类型为double。 根据数据被视为离散还是连续&#xff0c;对象的工作原理不同。 演示模型&#xff1a; 观测次数的统计函数打开AnyL…

【经验】linux下cuda的更换

linux下cuda的更换 查看当前cuda和cudnn的版本 nvcc -Vcudnn版本 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2下载对应版本的cuda 查看驱动版本535.54.03 下载对应的cuda版本 版本查看https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.htm…