【社区投稿】Rust登陆华为鸿蒙操作系统之Native模块开发

Rust登陆【华为鸿蒙】操作系统之Native模块开发

名词解释

  • 【鸿蒙操作系统】的英文全名是Open Harmony Operation System。正文将以其首字母缩写词ohos引用该词条。

  • 【鸿蒙软件开发工具包】的英文全名是Open Harmony Software Development Kit。正文也将以它的首字母缩写词ohsdk引用该词条。

  • DevEco Studio IDE是【华为】为鸿蒙应用程序开发免费提供的集成开发环境。它的最新稳定版内置了ohsdk 3.1.0 (API v9)

  • Native模块】是指由遵循了ArkTs NAPI接口规范的C/Cpp/Rust程序经交叉编译输出的链接库.so文件。

前言

到写文章时止,虽然华为技术团队既未将rustup工具链无缝集成入DevEco Studio IDE也未提供ArkTs + Rust的“一站式”混合编程体验,但Rust登陆ohos依旧势不可挡,因为相较于Rust带来的生产效率收益(参照c / cpp),搭建交叉编译环境的人工成本真的微不足道。甚至,求助于【操作系统镜像】或Docker技术,@Rustacean 还能避免这类重复性劳动的再次发生。

为了填补DevEco Studio IDErustup工具链之间的“窄沟”,仅有两步操作需被执行:

  1. 搭建面向ohos的交叉编译环境。

  • 限于作者dev boxWindows 11,所以本篇文章仅分享从Windowsohos的交叉编译环境搭建心得。

将交叉编译输出的.so文件注入DevEco Studio工作流。

搭建Windows  ➞ ohos交叉编译环境

鉴于华为硬件产品的三款主流CPU架构,@Rustacean 需同时准备三套交叉编译方案,分别是:

  • 面向64ARM CPUaarch64-unknown-linux-ohos方案。

  • 面向32ARM CPUarmv7-unknown-linux-ohos方案。

  • 面向64AMD / Intel CPUx86_64-unknown-linux-ohos方案。

前两套方案是为【真机】设备提供动态链接库/Native模块;而后一套方案则是服务于手机模拟器(虚拟机)的。

0e6db510fdef2bb15deb442d05810136.png

上表中Triple的信息描述格式统一是:

<CPU架构><CPU子架构>-<厂商>-<操作系统>-<应用程序二进制接口格式>

于是,armv7-unknown-linux-ohos应被读作

bdc3e936c93fc3a315e084a9537925f9.png

【厂商】栏的unkownMozilla公司的“锅”,而不是我定的。就我本意,这一栏馁馁的是汉语拼音HuaWei

下面上干货了...

第一步,给ohsdk补装native组件

DevEco Studio IDE的内置ohsdk位于%LocalAppData%\Huawei\Sdk\openharmony\<API 版本号>目录下,但其初始安装却缺失了native组件(— 可能是因为这个模块太大了,超过2GB)。所以,@Rustacean 需要

  1. 补装native组件

  2. 记住ohsdk对应的【API版本号】,因为后续配置得用。

具体步骤
  1. 打开DevEco Studio IDE

  2. 若出现的是【欢迎界面】,就从菜单ConfigureSettings,打开Settings对话框

  3. 若出现的是【工程界面】,就从菜单FileSettings,打开Settings对话框

  4. 从对话框左侧选择SDK;从右侧查看Platform选项卡下面的内容

  5. 寻找并记忆被勾选的【SDK版本号 (API版本号)】。比如,下图中的3.1.0 (API 9)

    94bdfe399eefa3f8cec3b77d263c9c1e.png

  6. 勾选native复选框

  7. 点击OK按钮

  8. 等待native组件安装完成 — 耐心点儿,等待时间可不短

待上述操作都正常完成之后,便可见如下所示的新目录结构

d929755384809ad5957fd59298fafcb7.png

第二步,重新编译Rust标准库

之所以把事情搞这么大是因为Mozilla厂方并没有为ohos提供预编译的【标准库】二进制文件。于是,尽管ohos已被纳入了rustc交叉编译支持清单(请见下图)

1b35c8121bdcfb577bb7527628f96d28.png

,但直接执行交叉编译指令

cargo build --release --target=aarch64-unknown-linux-ohos

还是会遭遇失败和看到E0463号错误

5baf787da9d787d5c95225585a9eaed4.png

技术方案选型

编译【标准库】源码有两条技术路径

  1. 重新编译整条rustup工具链,捎带着也就编译出【标准库】了 — 难!我没搞定

  2. 将【标准库】作为普通依赖crateCargo (Lib) Package工程的业务代码一起编译(— 注:这个解释并不精确,因为细究起来crate依赖crates是搅和在一起的各自独立编译,而不是绝对意义上的“一锅烩”)。下图中被红框圈定的crates就都出自于【标准库】


    d2a8f2372856f2d7d2845726517e77e8.png

我选择了第二条技术路线。虽然后一条技术路线拖长了程序编译的总用时,但它仅会影响首次编译操作。从那以后,借助sccache编译缓存技术,由【标准库】引入的额外延时几乎可以忽略不计。更重要的是,该技术路线不会阻塞 @Rustacean 对rustup工具链的后续升级。咱们随时都可以rustup update

采用【方案二】的准备工作与先决条件
  1. rustup工具链,补装【标准库】源码(即,rust-src组件)。

    从命令行,立即执行且仅执行一次:

    rustup component add rust-src
  2. 启用nigtly工具链,因为工具链的stable版本还尚不支持“裹挟【标准库】共同编译”的新功能。

    从命令行,立即执行且仅执行一次:

    rustup default nightly
  3. 采用ohsdk内置的llvm - clang作为rustc链接器(下一节将详细介绍)

  4. 向交叉编译指令添加新命令行参数-Zbuild-std

    1. cargo会透传该参数给rustc并指示编译器不是寻找现成的【标准库】链接文件而是现场编译【标准库】源码。

    2. 编译指令也将变为

      cargo +nightly build -Zbuild-std --release --target=aarch64-unknown-linux-ohos
如何把ohsdk内置的llvm - clang作为rustc链接器

第一步,回忆之前记下的【鸿蒙API版本号】数字和新建环境变量OHOS_API_V。【推荐】从Cargo全局配置文件%UserProfile%\.cargo\config.toml新建OHOS_API_V环境变量,因为

  • 一方面,这可最小化对系统环境的“污染” — 该变量仅对Rust交叉编译有用,没有必要系统级全局可见。

  • 另一方面,它随时可被【会话级】同名环境变量短暂复写,方便以后临时变更做试验。

打开%UserProfile%\.cargo\config.toml配置文件和添加配置表

[env]
OHOS_API_V = "9"

【注意】伴随今后ohsdk自动升级,该环境变量的值须被同步地手动更新,以避免编译失败。

第二步,将ohsdk目录下的LLVM前端编译器llvm\bin\clang.exe包装为rustc的【鸿蒙链接器】。敲黑板,重点来了!@Rustacean 需分别构建三个链接器,以服务三套交叉编译方案,和向华为的三类硬件设备提供.so文件。于是,有

  • 【链接器1】面向64ARM CPU真机aarch64-unknown-linux-ohos交叉编译方案。在%UserProfile%目录下,新建cmd文件aarch64-unknown-linux-ohos-clang.cmd,并添加如下代码

    %LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\llvm\bin\clang.exe ^
    -target aarch64-linux-ohos ^
    --sysroot=%LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\sysroot ^
    -D__MUSL__ %*
  • 【链接器2】面向32ARM CPU真机armv7-unknown-linux-ohos交叉编译方案。在%UserProfile%目录下,新建cmd文件armv7-unknown-linux-ohos-clang.cmd,并添加如下代码

    %LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\llvm\bin\clang.exe ^
    -target arm-linux-ohos ^
    --sysroot=%LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\sysroot ^
    -D__MUSL__ ^
    -march=armv7-a ^
    -mfloat-abi=softfp ^
    -mtune=generic-armv7-a ^
    -mthumb %*
  • 【链接器3】面向64AMD / Intel CPU模拟器x86_64-unknown-linux-ohos交叉编译方案。在%UserProfile%目录下,新建cmd文件x86_64-unknown-linux-ohos-clang.cmd,并添加如下代码

    %LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\llvm\bin\clang.exe ^
    -target x86_64-linux-ohos ^
    --sysroot=%LocalAppData%\Huawei\Sdk\openharmony\%OHOS_API_V%\native\sysroot ^
    -D__MUSL__ %*

第三步,全局且有条件地向rustc装配【鸿蒙链接器】。其中,

  • 【全局】意味着修改Cargo全局配置文件%UserProfile%\.cargo\config.toml和作用于所有Cargo Package工程。

  • 【有条件】意味着采用条件编译语法target.<triple>.linker限定该【链接器】仅生效于面向ohos的交叉编译操作。

具体作法,打开%UserProfile%\.cargo\config.toml配置文件和添加配置表

[target.aarch64-unknown-linux-ohos]
linker = "./aarch64-unknown-linux-ohos-clang.cmd"
[target.armv7-unknown-linux-ohos]
linker = "./armv7-unknown-linux-ohos-clang.cmd"
[target.x86_64-unknown-linux-ohos]
linker = "./x86_64-unknown-linux-ohos-clang.cmd"
[profile.dev.package.compiler_builtins]
opt-level = 2

再对前面配置片段补充两点解释:

  1. 配置项linker相对路径引用链接器文件的背后逻辑是cargo总是以config.toml父文件夹(.cargo)所处目录为起点开始解析相对路径(,而不是以config.toml的同级目录为起点)。所以,本例中的./路径前缀对应的就是登录账号的根目录%UserProfile%

  2. 配置项opt-level,借助【Profile重写(i.e. Override)】配置表头[profile.dev.package.compiler_builtins],仅将【开发编译】模式下【标准库】内compiler_builtins crate的代码优化级别强制锚定于2。否则,cargo build -Zbuild-std --target=aarch64-unknown-linux-ohos指令(注意:没有--release参数)会概率性地失败于exit code: 0xc0000005, STATUS_ACCESS_VIOLATION错误。

第四步,给冗长的交叉编译指令约定(短)别名。

还是打开%UserProfile%\.cargo\config.toml配置文件和增补如下配置表

[alias]
ohos-build = ["build", "-Zbuild-std", "--target=aarch64-unknown-linux-ohos", "--target=armv7-unknown-linux-ohos", "--target=x86_64-unknown-linux-ohos"]

于是,只要执行一条cargo ohos-build指令就相当于连续执行下面三条编译指令:

  1. cargo build -Zbuild-std --target=aarch64-unknown-linux-ohos

  2. cargo build -Zbuild-std --target=armv7-unknown-linux-ohos

  3. cargo build -Zbuild-std --target=x86_64-unknown-linux-ohos

总结交叉编译环境的搭建成果

以后每次在Cargo (Lib) Package工程根目录下执行

cargo ohos-build --release

,编译器都会立即

  1. 唤起ohsdk内置的LLVM前端编译器llvm - clang作为rustc链接器

  2. 将【标准库】源码作为普通依赖cratecrate业务程序一起编译

  3. 并行启动三个JOB进程对同一套Rust源码同时执行三组交叉编译操作

  4. 交叉编译输出三个文件名相同ABI格式不同的动态链接库.so文件

新建Cargo (Library) Package工程,验证交叉编译环境

首先,克隆stuartZhang/socket2至本地,并将代码分支切至v0.4.x

git clone git@github.com:stuartZhang/socket2.git
cd socket2
git checkout -q v0.4.x

关于这一步操作的必要性,我已经详细地阐述于ohos-node-bindgen还不能被直接使用章节了。简单地讲,这是为了绕过socket2 crate对华为鸿蒙操作系统的不兼容缺陷。

然后,从命令行,新建Cargo (Library) Package工程

cd ..
cargo new --lib calculator
code calculator

其次,在VSCode内,打开Cargo.toml文件,和追加如下内容

[lib]
crate-type = ["dylib"][dependencies]
ohos-node-bindgen = "6.0.3"
socket2 = "0.4.10"[patch.crates-io]
socket2 = { path = "../socket2" }

前面配置片段内的【依赖图重写】配置表[patch.crates-io]指示Cargo包管理器使用本地的stuartZhang/socket2 crate山寨货替换crates.io上的正品,因为正品不兼容华为操作系统。

接着,从VSCode打开src/lib.rs文件,和增补如下Demo代码。这是一段简单的整数加运算程序。请把注意力聚焦在【派生宏】的使用上

use ::ohos_node_bindgen::derive::ohos_node_bindgen;
#[ohos_node_bindgen]
fn add(first: i32, second: i32) -> i32 {first + second
}

再次,执行交叉编译

cargo ohos-build --release

最后,从【资源管理器】查看编译输出结果

Cargo (Library) Package 工程根目录
├── Cargo.toml
├── src — Rust 源码目录
├── target
│  ├── aarch64-unknown-linux-ohos
│  │  └── release
│  │     └── libcalculator.so
│  ├── armv7-unknown-linux-ohos
│  │  └── release
│  │     └── libcalculator.so
│  ├── x86_64-unknown-linux-ohos
│  │  └── release
│  │     └── libcalculator.so

值得注意的是,编译输出的链接库文件名是lib前缀的。所以,Native模块的文件名是lib<包名>.so,而不是<包名>.so

Native模块注入普通的DevEco Studio工程

Native模块就是由前面交叉编译输出的ArkTs N-API链接库.so文件。

首先,从DevEco Studio IDE新建/打开普通Empty Ability工程。

然后,修改模块级build-profile.json5文件(比如,entry/build-profile.json5),和添加如下配置项至buildOption节点

"externalNativeOptions": {"abiFilters": ["arm64-v8a","armeabi-v7a","x86_64"]
}

其次,在模块根目录下,创建下面三个子文件夹

  • libs/arm64-v8a

  • libs/armeabi-v7a

  • libs/x86_64

接着,依次向它们复制入编译好的链接库文件。例如,

2d48a5375d279dcc94bcc3f16de8cd49.png

最后,在ArkTs业务代码内(比如,entry/src/main/ets/pages/Index.ets),以ES Module语法,导入Native模块,和调用其成员方法

import calculator from 'libcalculator.so';
const result = calculator.add(2, 3);

总的来讲,调用端的ets代码就这么简单!但还是有三处优化可做以改善开发体验:

优化DevEco Studio工程目录结构

Cargo (Lib) PackageDevEco Studio Project合并为一个工程更有利于提高Rust + ArkTs的混合编程生产力。所以,如下DevEco Studio工程目录结构是被强力推荐的:

DevEco Studio 工程根目录
├── entry — 模块根目录
│   ├── libs — 交叉编译输出的 .so 文件都被复制到下面的子文件夹内
│   │   ├── arm64-v8a
│   │   ├── armeabi-v7a
│   │   └── x86_64
│   ├── src
│   │   ├── main
│   │   │  ├── resources
│   │   │  ├── cpp  — *旧有*的 Cpp(ArkTs N-API) 工程目录
│   │   │  ├── ets  — *旧有*的 ArkTs 源码目录
│   │   │  ├── rust — *新建*的 Rust(ArkTs N-API) 工程目录
│   │   │  │   ├── Cargo.toml
│   │   │  │   ├── src — Rust 源码目录
│   │   │  │   ├── target
│   │   │  │   │  ├── aarch64-unknown-linux-ohos
│   │   │  │   │  │  └── release
│   │   │  │   │  ├── armv7-unknown-linux-ohos
│   │   │  │   │  │  └── release
│   │   │  │   │  ├── x86_64-unknown-linux-ohos
│   │   │  │   │  │  └── release

Cargo (Lib) Package降级为DevEco Studio Project内某个特定模块下的子工程有两个好处:

  1. 同一个DevEco Studio工程内可同时包含多个Native子工程。

  2. 每个Native子工程既可独占一个模块以达成与主模块业务代码有限隔离的目的,也能与ets程序“混住”耦合于相同模块内。

友情提示

在移动Cargo (Lib) Package工程位置后,千万别忘了同步修改Cargo.toml配置文件中【依赖图重写】配置表[patch.crates-io]对本地stuartZhang/socket2 crate的引用路径。否则,会编译失败!

自动化链接库.so文件的复制操作

在每次执行cargo ohos-build --release指令之后都徒手复制三个.so文件至不同的文件夹是非常低效的,所以 @Rustacean 有必要给Cargo编写build.rspost_build.rs构建程序,以扩展包管理器在编译前编译后的处理行为,并自动完成文件复制操作。其中,

  1. build.rs作为【前置处理】程序

    1. 从环境变量,收集.so文件的位置信息

    2. 生成[CMD] COPY /Y[Shell] cp -f文件复制指令

    3. 将【文件复制】指令尾追加至同一个.cmd / .sh脚本文件

  2. post_build.rs作为【后置处理】程序

    1. 执行被写入【文件复制】指令的程序文件,并

    2. 删除该程序文件

【打广告】build.rspost_build.rs皆未对上下文做任何的假设。所以,它们可被零成本地复用于其它同类工程中。

还是看图吧,一图抵千词

da24575a62ade6af19dc35d340ec9e7a.png

设计很完美但现实很骨感,因为Mozilla厂方的rustup工具链尚支持【后置处理】。所以,@Rustacean 需

  1. 额外安装功能增补包cargo-post

    cargo install cargo-post
  2. 修改Cargo全局配置文件%UserProfile%\.cargo\config.toml中的ohos-build别名设置,以使cargo-post生效

    [alias]
    ohos-build = ["post", "build", "-Zbuild-std", "--target=aarch64-unknown-linux-ohos", "--target=armv7-unknown-linux-ohos", "--target=x86_64-unknown-linux-ohos"]

    【注意】在"build"左侧添加了"post"数组项

Native模块导出接口,添加.d.ts类型提示

DevEco Studio IDE并没有集成类似于DLL Export Viewer的【动态链接库外部接口反射工具】。所以需要

  1. @Rustacean 在输出.so文件的同时也提供一份接口类型说明的.d.ts文件(— 其功能几乎等效于C头文件),并

  2. 将该类型说明文件注入DevEco Studio工作流

接下来,我沿着前面Rust + ArkTs混合编程的目录结构,描述操作步骤:

  1. 在模块entry的根目录下,创建src/main/rust/types/libcalculator子目录。注意:路径末端的文件夹名libcalculator是链接库文件的basename

  2. 在新建文件夹内,再新建文件index.d.ts和添入Native模块导出函数的函数签名

    export const add: (frist: number, second: number) => number;
  3. 接着新建文件oh-package.json5和添入Native模块的摘要信息。

    {"name": "libcalculator.so","types": "./index.d.ts","version": "0.1.0","description": "ArkTs NAPI 原生模块示例"
    }

    其中,

    1. name字段就是链接库的文件名(含扩展名)。

    2. types字段是指向类型说明文件的相对路径。

    3. version字段是Native模块版本号。【推荐】该字段值与Cargo (Lib) Package子工程中Cargo.toml配置文件内[package]配置表下version配置项的值保持一致 — 这又是一处纯人工同步点。

    4. description字段是Native模块描述信息。

  4. 打开entry模块的oh-package.json5文件,并添加对Native模块的依赖项条目。

    "dependencies": {"libcalculator.so": "file:src/main/rust/types/libcalculator"
    }

    在依赖项条目中,左侧是链接库的文件名;而右侧是指向了类型说明文件所处文件夹的相对目录。


    3e7443b3f0321139f7ddb0afdf4e5896.png

  5. 最后,从DevEco Studio IDE依次点击菜单项BuildRebuild Project重新构建整个工程和使配置项修改生效。

于是,鸿蒙应用软件开发程序员就能在etsts代码编辑器内获得针对Native模块API的丰富类型提示了。

线上例程

我已将上述全部文字描述内容都例程化到github工程Arkts-NAPI-Rust-Demo内了。线下运行该工程可加强对文章繁杂内容的理解。

运行例程工程的环境要求

  1. rustc 1.75.0-nightly

  2. VSCode 1.86

  3. ohsdk 3.1.0(API v9)

  4. DevEco Studio 3.1.1 Release

运行例程工程的具体步骤

  1. 克隆git@github.com:stuartZhang/Arkts-NAPI-Rust-Demo.git

  2. VSCode内,

    1. 打开entry/src/main/rust目录

    2. 敲击Alt + T + R键。

    3. 依次点击菜单项buildohos-build--release

    4. 观察控制台输出日志,等待交叉编译结束。

  3. DevEco Studio IDE内,

    image

    11d028fd12af619fdbdade9f817fdd52.png

    1. 打开工程根目录

    2. 启动手机模拟器

    3. 敲击Shift + F10键,运行移动端程序

结束语与扩展阅读

搞定【交叉编译】难关仅只是鸿蒙Rust原生开发万里征程的第一步。加深对ArkTs - NAPI接口定义的理解才是【形成生产力】的核心任务。好消息是

  1. ArkTs - NAPInodejs N-API高度相似。至少截至目前,它们的相似度还>= 95%。所以,已熟悉nodejs原生模块编程的“老司机”们上手鸿蒙ArkTs - NAPI应该不难。

  2. 另外,我在春节假期期间贡献的ohos-node-bindgen crate更可大幅降低ArkTs - NAPI原生开发的复杂度。请对比下图左右侧的代码量

    c26e2e6056747fac7512c34d5143125d.png

    所以,ohos-node-bindgen crate值得大家点star呀!也请大家给Arkts-NAPI-Rust-Demo点star

4785843e65026fab848e3d7d5554eee8.png


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

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

相关文章

C++-带你初步走进继承(1)

1.继承的概念及定义 1.1继承的概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段&#xff0c;它允许程序员在 保 持原有类特性的基础上进行扩展 &#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承 呈现了面向对象 …

如何在IDEA中使用固定公网地址SSH远程连接服务器开发环境

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

Compose 1.6 发布:性能大升级、拖放新功能、文本新变化...

翻译自&#xff1a; https://android-developers.googleblog.com/2024/01/whats-new-in-jetpack-compose-january-24-release.html 基于 1 月 24 号的 Compose 发行计划&#xff0c;我们正式推出了 Jetpack Compose 1.6 版本。 作为 Android 平台备受推崇的原生 UI 工具包&…

P4447 [AHOI2018初中组] 分组题解

题目 小可可的学校信息组总共有n个队员&#xff0c;每个人都有一个实力值。现在&#xff0c;一年一度的编程大赛就要到了&#xff0c;小可可的学校获得了若干个参赛名额&#xff0c;教练决定把学校信息组的n个队员分成若干个小组去参加这场比赛。 但是每个队员都不会愿意与实…

安全测试工具安装指南:在统信UOS上部署Burp Suite

原文链接&#xff1a;安全测试工具安装指南&#xff1a;在统信UOS上部署Burp Suite 大家好&#xff01;在网络安全领域&#xff0c;Burp Suite是一款不可或缺的工具&#xff0c;它提供了从初级映射和分析应用程序攻击面到查找和利用安全漏洞的一系列功能。今天&#xff0c;我将…

Shiro-05-shiro 基础知识补充密码学+哈希散列

密码学 密码术是隐藏或混淆数据的过程&#xff0c;因此窥探眼睛无法理解它。 Shiro的加密目标是简化JDK的加密支持并使之可用。 需要特别注意的是&#xff0c;密码通常不是特定于主题的&#xff0c;因此Shiro API的其中一个领域不是特定于主题的。 即使未使用“主题”&…

【elk查日志 elastic(kibana)】

文章目录 概要具体的使用方式一&#xff1a;查找接口调用历史二&#xff1a;查找自己的打印日志三&#xff1a;查找错误日志 概要 每次查日志&#xff0c;我都需要别人帮我&#xff0c;时间长了总觉得不好意思&#xff0c;所以这次下定决心好好的梳理一下&#xff0c;怎么查日…

spellman电源维修X3635系列CCM5P4X3635

Spellman高压发生器维修Perkin Elmer分析仪电源维修CCM5P4X3635 Perkin Elmer W102266 X射线高压发生器spellman电源维修X4297系列CT机高压电源维修CT42&#xff1b;CT70系列。SPELLMAN高压发生器应用于东芝CT机XVISION/EX、AUKLET系列、ASTEION系列、以及多排系列&#xff0c…

一文概括|CSC访问学者/博士后/联培申请及派出流程详解

为帮助申请者了解国家留学基金委&#xff08;CSC&#xff09;的政策&#xff0c;以及申报及派出的全过程&#xff0c;知识人网小编利用本文简略介绍并提出规划建议。 公派留学包括国家、地方&#xff08;含省市、行业、学校医院等单位&#xff09;资助派出。而国家公派则由留学…

HarmonyOS开发篇—数据管理(分布式数据服务)

分布式数据服务概述 分布式数据服务&#xff08;Distributed Data Service&#xff0c;DDS&#xff09; 为应用程序提供不同设备间数据库数据分布式的能力。通过调用分布式数据接口&#xff0c;应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组&#xf…

软件测试实训系统建设方案2024

软件测试实训室解决方案 一 、方案概述 软件测试实训解决方案是一个复杂且至关重要的过程&#xff0c;它确保了软件在开发过程中的各个模块能够正确地集成和交互。通过这一系列的测试步骤&#xff0c;开发团队能够及时发现并修复潜在的问题&#xff0c;从而提高软件的整体质量…

【漏洞复现-通达OA】通达OA share存在前台SQL注入漏洞

一、漏洞简介 通达OA&#xff08;Office Anywhere网络智能办公系统&#xff09;是由北京通达信科科技有限公司自主研发的协同办公自动化软件&#xff0c;是与中国企业管理实践相结合形成的综合管理办公平台。通达OA为各行业不同规模的众多用户提供信息化管理能力&#xff0c;包…

【Java EE初阶十五】网络编程TCP/IP协议(二)

1. 关于TCP 1.1 TCP 的socket api tcp的socket api和U大片的socket api差异很大&#xff0c;但是和前面所讲的文件操作很密切的联系 下面主要讲解两个关键的类&#xff1a; 1、ServerSocket&#xff1a;给服务器使用的类&#xff0c;使用这个类来绑定端口号 2、Socket&#xf…

全网最容易理解的KMP算法讲解

引言 其实网上有很多讲解KMP算法的文章&#xff0c;详略不一&#xff0c;我认为有两点没有解释清楚&#xff1a; 第一点&#xff1a;匹配失败以后&#xff0c;模式串的位移 第二点&#xff1a;next数组的生成算法 希望本篇文章能将KMP算法清晰易懂的拆解开来。 暴力匹配 …

网络同步—帧同步和状态同步解析

概述 同步就是要多个客户端表现效果是一致的&#xff0c;而且对于大多数的游戏&#xff0c;不仅仅要表现一致&#xff0c;还要客户端和服务器的数据也是一致的。所以同步是个网络游戏概念&#xff0c;只有网络游戏才需要同步&#xff0c;而单机游戏是不需要同步的。 帧同步和…

算法-3-基本的数据结构

单双链表 1.单链表双链表如何反转 import java.util.ArrayList; import java.util.List;public class Code01_ReverseList {public static class Node {public int value;public Node next;public Node(int data) {value data;}}public static class DoubleNode {public int…

掘根宝典之C++深复制与浅复制(复制构造函数,默认复制构造函数)

到目前为止我们已经学了构造函数&#xff0c;默认构造函数&#xff0c;析构函数&#xff1a;http://t.csdnimg.cn/EOQxx 转换函数&#xff0c;转换构造函数&#xff1a;http://t.csdnimg.cn/kiHo6 友元函数&#xff1a;http://t.csdnimg.cn/To8Tj 接下来我们来学习一个新函数…

python毕设选题 - 大数据全国疫情数据分析与3D可视化 - python 大数据

文章目录 0 前言1 课题背景2 实现效果3 设计原理4 部分代码5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的…

2024阿里云云服务器ECS价格表出炉

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

【Funny Game】 人生重开模拟器

目录 【Funny Game】 人生重开模拟器&#xff01; 人生重开模拟器&#xff01; 文章所属专区 Funny Game 人生重开模拟器&#xff01; 人生重开模拟器&#xff0c;让你体验从零开始的奇妙人生。在这个充满惊喜和挑战的游戏中&#xff0c;你可以自由选择性别、出生地、家庭背景…