Android Rust JNI系列教程(二) 创建第一个Rust JNI项目

news2024/11/23 11:28:49

前言

提到JNI,大家都会想到C,C++.不过如今rust又给我们增加了一个选项,借助rustjni库(https://github.com/jni-rs/jni-rs),我们可以很方便的使Androidrust交互.从本章起,我们将逐步地了解使用rust实现一些经典的jni方法.

创建Rust项目

  1. 创建工程

在命令行输入命令:

cargo new --lib rust_jni_demo

命令执行结果:

Created library `rust_jni_demo` package

这样我们就成功的创建了一个名为rust_jni_demorust项目.

  1. 查看项目目录
txs:rust_jni_demo/ (master*) $ tree .                                                
.
├── Cargo.toml
└── src
    └── lib.rs
1 directory, 2 files

可以看到目录里有一个src/lib.rs,这是我们接下来要遍写代码的地方.而Cargo.toml文件则是我们将要配置项目及依赖的地方.

  1. 打开项目
    使用vs code配合rust插件,或者使用CLion配合rust插件都可以,甚至使用vim都是可以的.如图:打开项目
  2. 导入依赖

Cargo.toml文件的[dependencies]标签下,导入jni依赖

jni = "0.20.0"
  1. 声明项目是一个动态库

Cargo.toml中添加如下内容

[lib]
#C规范动态库
crate_type = ["cdylib"]

至此,Cargo.toml文件的内容为:

[package]
name = "rust_jni_demo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
jni = "0.20.0"

[lib]
#C规范动态库
crate_type = ["cdylib"]

开始编写代码

  1. 声明一个JNI方法,返回值为String
pub extern "system" fn Java_com_jni_rust_RustNative_getStringFromRust(env: JNIEnv, _: JClass) -> jstring

可以看到,方法名Java_com_jni_rust_RustNative_getStringFromRust,命名格式和标准的C++声明的方法名格式相同.另外请注意,extern 的使用无需 unsafe,我经常看到一些文章里面使用unsafe的方式声明JNI方法,方法名如pub unsafe extern "system" fn....再次注意,unsafe目前是非必要的.

  1. 增加注解#[no_mangle]

在方法上方添加#[no_mangle]注解,来告诉 Rust 编译器不要 mangle 此函数的名称.

Mangling 发生于当编译器将我们指定的函数名修改为不同的名称时,这会增加用于其他编译过程的额外信息,不过会使其名称更难以阅读。每一个编程语言的编译器都会以稍微不同的方式 mangle 函数名,所以为了使 Rust 函数能在其他语言中指定,必须禁用 Rust 编译器的 name mangling。

  1. 定义Android函数
    jstring对应java里面的String,所以我们在Android端定义一个方法
public static native String getStringFromRust();

方法位于package com.jni.rust文件夹下,RustNative类.

  1. rust端返回一个jstring
 let output = env.new_string("hi bro from rust").unwrap();
 output.into_raw()

通过文档可看到env.new_string方法的具体描述

rust string 创建一个java string 对象

其返回值为JString,即一个jstring 的生命周期表示.
所以我们调用output.into_raw()JString转为JNI所需要的jstring.

  1. C++再实现一遍相同功能,代码如下:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_jni_rust_CNative_getStringFromCpp(JNIEnv *env, jclass clazz) {
    std::string bro = "hi bro from cpp";
    return env->NewStringUTF(bro.c_str());
}

打包.so

  1. 编译命令
cargo build --target aarch64-linux-android --release 
  1. 结果
 $ cargo build --target aarch64-linux-android --release                                                                                                 
   Compiling proc-macro2 v1.0.47
   Compiling quote v1.0.21
   Compiling unicode-ident v1.0.5
   Compiling syn v1.0.105
   Compiling memchr v2.5.0
   Compiling same-file v1.0.6
   Compiling thiserror v1.0.37
   Compiling log v0.4.17
   Compiling cfg-if v1.0.0
   Compiling bytes v1.3.0
   Compiling cesu8 v1.1.0
   Compiling jni-sys v0.3.0
   Compiling walkdir v2.3.2
   Compiling jni v0.20.0
   Compiling combine v4.6.6
   Compiling thiserror-impl v1.0.37
   Compiling rust_jni_demo v0.1.0 (/home/txs/Center/project/rustProject/rust_jni_demo)
    Finished release [optimized] target(s) in 6.60s

生成的.so/target/aarch64-linux-android/release文件夹下,本项目.so文件名为librust_jni_demo.so.

  1. so文件大小

/target/aarch64-linux-android/release文件夹下,查看.so文件大小:

$ ls -alh                                                                                                                                                     
总用量 4.2M
drwxrwxr-x  7 txs txs 4.0K 12月  6 15:30 .
drwxrwxr-x  3 txs txs 4.0K 12月  6 15:30 ..
drwxrwxr-x  6 txs txs 4.0K 12月  6 15:30 build
-rw-rw-r--  1 txs txs    0 12月  6 15:30 .cargo-lock
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:30 deps
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:30 examples
drwxrwxr-x 16 txs txs 4.0K 12月  6 15:30 .fingerprint
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:30 incremental
-rw-rw-r--  1 txs txs  171 12月  6 15:30 librust_jni_demo.d
-rwxrwxr-x  2 txs txs 4.2M 12月  6 15:30 librust_jni_demo.so

可以看到,librust_jni_demo.so文件大小居然有4.2M,远远超出了我们的预期.其实把这个so放到Android项目里面再打包之后会被优化到200k左右的,不过我们也可以在rust项目里先对我们的so文件大小进行优化.

  1. 优化so文件大小

编辑Cargo.toml文件,添加如下内容:

[profile.release]
lto = true
opt-level = 'z'
strip = true
codegen-units = 1
#panic = 'abort'

具体配置详情请参考官方文档. 如果配置上panic = 'abort'是可以进一步优化文件大小的.但是会导致panic后看不到调用栈信息.所以我还是习惯于不进行panic = 'abort'这一项优化.

另外其他优化手段,如重新编译libstd、声明#![no_std]不使用标准库,都可以优化so文件大小.后续文章也会为大家分享一些优化std的小知识.

  1. 再次查看so文件大小
 $ ls -alh                                                                                                                                                     
总用量 272K
drwxrwxr-x  7 txs txs 4.0K 12月  6 15:49 .
drwxrwxr-x  3 txs txs 4.0K 12月  6 15:30 ..
drwxrwxr-x 10 txs txs 4.0K 12月  6 15:48 build
-rw-rw-r--  1 txs txs    0 12月  6 15:30 .cargo-lock
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:49 deps
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:30 examples
drwxrwxr-x 29 txs txs 4.0K 12月  6 15:48 .fingerprint
drwxrwxr-x  2 txs txs 4.0K 12月  6 15:30 incremental
-rw-rw-r--  1 txs txs  171 12月  6 15:30 librust_jni_demo.d
-rwxrwxr-x  2 txs txs 237K 12月  6 15:49 librust_jni_demo.so

可以看到, librust_jni_demo.so 优化到了237k

开发Android项目

  1. 将我们rust项目打包生成的so文件,放到Android项目中.
  2. 创建类RustNative,路径为com.jni.rust,用以对应Java_com_jni_rust_RustNative_getStringFromRust函数路径.同时定义一个native方法
public static native String getStringFromRust();
  1. 完整的RustNative类代码为:
package com.jni.rust;

public class RustNative {
    static {
        System.loadLibrary("rust_jni_demo");
    }
    public static native String getStringFromRust();
}
  1. android项目中,我又用C++实现了一个返回string类型数据的函数,用来对比生成的so文件大小.详细代码将放在github.
  2. release包.

对比rust生成的soC++生成的so文件

  1. 解压apk,查看so文件
$ ls -lh                                         
总用量 460K
-rw-r--r-- 1 txs txs 217K  1月  1  1981 libcppnative.so
-rw-r--r-- 1 txs txs 237K  1月  1  1981 librust_jni_demo.so

其中libcppnative.so就是C++代码打出的so文件.可以看到,rust打包生成的so文件较C++打包生成的so文件大了20k.目前还可以接受.

  1. 查看编译后的so文件代码
  • libcppnative.so拖入IDA中查看

  • 导出函数如图
    导出函数
    函数有点多,找到我们的JNI方法,看一下伪代码(需要F5插件)

  • JNI方法伪代码,如图:
    jni方法
    可以看到逻辑还算能看.

  • 再将librust_jni_demo.so拖入IDA中查看

  • 导出函数如图
    rust导出函数
    导出函数看起来比较清晰
    -再看看具体代码,代码有点多就不放图片了

_int64 __fastcall Java_com_jni_rust_RustNative_getStringFromRust(__int64 a1)
{
  const char *v1; // x20
  __int64 v3; // x0
  unsigned __int64 v4; // x28
  void **v5; // x22
  __int64 v6; // x23
  __int64 v7; // x1
  __int64 v8; // x1
  __int64 v9; // x8
  unsigned __int64 v10; // x27
  char *v11; // x0
  __int64 v12; // x1
  unsigned int v13; // w9
  unsigned int v14; // w28
  __int64 v15; // x0
  __int64 v16; // x1
  __int64 v17; // x0
  __int64 v18; // x22
  void *v19; // x21
  void *v20; // x0
  const char *v21; // x1
  const char *v22; // x23
  __int64 v23; // x0
  __int64 v24; // x1
  __int64 (__fastcall *v25)(__int64, __int64); // x9
  __int64 v26; // x0
  unsigned __int8 (__fastcall *v27)(__int64); // x8
  __int64 v28; // x20
  __int64 v30; // x8
  __int64 v31; // x9
  char *v32; // x8
  __int64 v33; // x0
  char v34; // w19
  __int64 v35; // x19
  size_t v36; // w0
  size_t v37; // w1
  const char *v38; // [xsp+8h] [xbp-C8h]
  __int128 v39[2]; // [xsp+10h] [xbp-C0h] BYREF
  _DWORD v40[2]; // [xsp+30h] [xbp-A0h]
  __int64 v41; // [xsp+38h] [xbp-98h] BYREF
  const char *v42; // [xsp+40h] [xbp-90h]
  void *v43; // [xsp+48h] [xbp-88h]
  __int128 v44; // [xsp+50h] [xbp-80h]
  __int128 v45; // [xsp+60h] [xbp-70h]

  v1 = "hi bro from rust";
  if ( sub_F878(0LL, "hi bro from rust", 16LL) == 1 )
  {
LABEL_2:
    v3 = sub_E158(8LL);
    v4 = 0LL;
    v5 = &off_3A880;
    v6 = 24LL;
    v42 = (const char *)v7;
    v43 = 0LL;
    v41 = v3;
    v38 = "assertion failed: w <= 4assertion failed: i + w <= bytes.len()assertion failed: 0xD800 <= surrogate && surroga"
          "te <= 0xDFFFEoiCharacterBoundaryUnexpectedParse..BorrowMutError";
    while ( 1 )
    {
      while ( 1 )
      {
        if ( v4 > 0xF )
        {
          v18 = v41;
          v1 = v42;
          v19 = v43;
          if ( v41 )
            goto LABEL_25;
          goto LABEL_24;
        }
        v8 = (unsigned __int8)aHiBroFromRust[v4];
        if ( aHiBroFromRust[v4] )
          break;
        sub_E240(&v41, 192LL);
        sub_E240(&v41, 128LL);
LABEL_8:
        ++v4;
      }
      if ( (v8 & 0x80) == 0 )
      {
        sub_E240(&v41, v8);
        goto LABEL_8;
      }
      v9 = byte_4144[v8];
      if ( (unsigned int)v9 >= 5 )
        goto LABEL_41;
      v10 = v4 + v9;
      if ( v4 + v9 > 0x10 )
      {
        v32 = "assertion failed: i + w <= bytes.len()assertion failed: 0xD800 <= surrogate && surrogate <= 0xDFFFEoiCharacterBoundaryUnexpectedParse..BorrowMutError";
        v6 = 38LL;
        v5 = &off_3A898;
LABEL_40:
        v38 = v32;
LABEL_41:
        v33 = sub_F1A0(v38, v6, v5);
        goto LABEL_49;
      }
      if ( (_DWORD)v9 == 4 )
      {
        v11 = (char *)sub_E28C(v4, v10, &off_3A8C8);
        if ( !v12 )
          goto LABEL_39;
        v13 = (unsigned __int8)*v11;
        if ( (*v11 & 0x80000000) != 0 )
        {
          if ( v13 <= 0xDF )
          {
            v14 = v11[1] & 0x3F | ((v13 & 0x1F) << 6);
          }
          else if ( v13 < 0xF0 )
          {
            v14 = v11[2] & 0x3F | ((v11[1] & 0x3F) << 6) & 0xFFF | ((v13 & 0x1F) << 12);
          }
          else
          {
            v14 = v11[3] & 0x3F | ((v11[2] & 0x3F | ((v11[1] & 0x3F) << 6) & 0xFFF) << 6) & 0xFFE3FFFF | ((v13 & 7) << 18);
            if ( v14 == 1114112 )
            {
LABEL_39:
              v32 = "called `Option::unwrap()` on a `None` valueinternal error: entered unreachable code/rustc/897e37553b"
                    "ba8b42751c67658967889d11ecd120/library/alloc/src/collections/btree/navigate.rs/rustc/897e37553bba8b4"
                    "2751c67658967889d11ecd120/library/alloc/src/slice.rs/cargo/registry/src/github.com-1ecc6299db9ec823/"
                    "gimli-0.25.0/src/read/line.rs";
              v6 = 43LL;
              v5 = &off_3A8E0;
              goto LABEL_40;
            }
          }
        }
        else
        {
          v14 = (unsigned __int8)*v11;
        }
        sub_E2B4(((v14 + 16711680) >> 10) | 0xFFFFD800);
        sub_35DA8();
        v17 = sub_E2B4(v14 & 0x3FF | 0xFFFFDC00);
        LOWORD(v40[0]) = v17;
        BYTE2(v40[0]) = BYTE2(v17);
        sub_35DA8();
        v4 = v10;
      }
      else
      {
        v15 = sub_E28C(v4, v4 + v9, &off_3A8B0);
        sub_E1D0(&v41, v15, v15 + v16);
        v4 = v10;
      }
    }
  }
  v30 = 0LL;
  v19 = &word_10;
  while ( v30 != 16 )
  {
    v31 = (unsigned __int8)aHiBroFromRust[v30];
    if ( (v31 & 0xC0) != 128 && byte_4144[v31] > 3u )
      goto LABEL_2;
    ++v30;
  }
LABEL_24:
  v20 = (void *)sub_E158(v19);
  v22 = v21;
  v18 = (__int64)v20;
  memcpy(v20, v1, (size_t)v19);
  v1 = v22;
LABEL_25:
  v41 = v18;
  v42 = v1;
  v43 = v19;
  v23 = sub_DBF8(&v41);
  *(_QWORD *)&v39[0] = v23;
  *((_QWORD *)&v39[0] + 1) = v24;
  if ( !a1 )
  {
    v34 = 8;
    v28 = 6LL;
    v19 = &unk_4DFD;
    goto LABEL_48;
  }
  if ( !*(_QWORD *)a1 )
    goto LABEL_42;
  v25 = *(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1336LL);
  if ( !v25 )
  {
    v34 = 6;
    v28 = 12LL;
    v19 = &unk_4E18;
    goto LABEL_48;
  }
  v26 = v25(a1, v23);
  if ( !*(_QWORD *)a1 )
  {
LABEL_42:
    v34 = 8;
    v28 = 7LL;
    v19 = &unk_4E03;
    goto LABEL_48;
  }
  v27 = *(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)a1 + 1824LL);
  if ( v27 )
  {
    v28 = v26;
    if ( v27(a1) == 1 )
    {
      v34 = 5;
    }
    else
    {
      if ( v28 )
      {
        sub_13BC0(v39);
        return v28;
      }
      v34 = 7;
      v28 = 19LL;
      v19 = &unk_4E24;
    }
  }
  else
  {
    v34 = 6;
    v28 = 14LL;
    v19 = &unk_4E0A;
  }
LABEL_48:
  sub_13BC0(v39);
  LOBYTE(v41) = v34;
  v42 = (const char *)v19;
  v43 = (void *)v28;
  v44 = v39[0];
  v45 = v39[1];
  *(_DWORD *)((char *)&v41 + 1) = v40[0];
  HIDWORD(v41) = *(_DWORD *)((char *)v40 + 3);
  v33 = sub_FF4C(&unk_5537, 43LL, &v41, &off_3A808, &off_3A828);
LABEL_49:
  __break(1u);
  v35 = v33;
  sub_14284(v39);
  v36 = sub_37A54(v35);
  __break(1u);
  return sub_D4DC(v36, v37);
}

可以看到相比于C++ JNI函数的伪代码,rust代码看起来混乱的多.或许IDA后续升级可以解决?

扩展知识

想在rust层打印日志?println!()信息在Android里面不显示的.现推荐两个rust android logger库.

  1. android_logger,强大的rust android logger库, github地址:https://github.com/Nercury/android_logger-rs.(包体也会比较大).
  2. android_logger_lite,超级轻量级的rust android logger库,包体<1k,专为Android项目打造.github地址:https://github.com/tangxuesong6/android_logger_lite

总结

使用Rust进行JNI开发的整体流程和原本的C++开发流程是非常相似的.而且在目前不使用三方库文件的情况下生成的so文件大小相差不多.但是Rust项目无法在Android studioDebug调试却是一大硬伤.

Android项目地址:https://github.com/tangxuesong6/Android_Rust_JNI_Demo
rust项目地址:https://github.com/tangxuesong6/Rust_JNI_Demo

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

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

相关文章

超算/先进计算的发展与应用是什么?

经过近十年的快速发展&#xff0c;我国在超算领域的实力已达到世界先进水平。1993年&#xff0c;我国第一台高性能计算机“曙光一号并行机”研制成功&#xff0c;打破了国外IT巨头对我国超算技术的垄断。 自此&#xff0c;我国不断加快超级计算机研制步伐。从全球超级计算机TO…

精华推荐 | 【MySQL技术专题】「主从同步架构」全面详细透析MySQL的三种主从复制(Replication)机制的原理和实战开发(原理+实战)

前提概要 随着应用业务数据不断的增大&#xff0c;应用的响应速度不断下降&#xff0c;在检测过程中我们不难发现大多数的请求都是查询操作。此时&#xff0c;我们可以将数据库扩展成主从复制模式&#xff0c;将读操作和写操作分离开来&#xff0c;多台数据库分摊请求&#xff…

NEUQACM双周赛(三)

目录7-1 打字&#xff08;C&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例1:输出样例1:输入样例2:输出样例2:解题思路&#xff1a;7-2 分香肠&#xff08;C&#xff0c;最大公约数&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例:输出样例:解题思路…

节能降耗 | AIRIOT智慧电力综合管理解决方案

电力技术的发展推动各行各业的生产力&#xff0c;与此同时&#xff0c;企业中高能耗设备的应用以及输配电过程中的电能损耗&#xff0c;也在一定程度上加剧了电能供应压力。以工业制造业为例&#xff0c;企业的管理水平、能耗结构、生产组织方式都关系到能源的有效利用率&#…

电子招投标系统nodejs+vue+elementui

前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后&#xff0c;cd到项目目录执行npm install后生成的文件夹&#xff0c;下载了项目需要的依赖项。 2、packag…

电商新模式——链动2+1模式为你带来社交电商新思路

随着流量入口价值的降低&#xff0c;电商 IP 时代的来临&#xff0c;移动社交电商获得了飞速的发展&#xff0c;在运营与营销的过程中&#xff0c;商家们往往为了降低营销成本&#xff0c;主动制造消费理由&#xff0c;通过各类促销、折扣来刺激消费&#xff0c;然而在回归商业…

Web3中文|NFT如何促进教育的发展?

自问世以来&#xff0c;NFT已经被应用于教育、艺术等多个领域。不过&#xff0c;相较于艺术行业&#xff0c;大多数人对NFT在教育界的作用知之甚少。 那么&#xff0c;就让我们来看看它们在课堂内外的影响都有哪些。 得益于区块链技术&#xff0c;NFT可以提高教育质量&#x…

【蓝桥杯选拔赛真题52】Scratch正话反说 少儿编程scratch图形化编程 蓝桥杯选拔赛真题讲解

目录 scratch正话反说 一、题目要求 编程实现 二、案例分析 1、角色分析

Android Rust JNI系列教程(三) Rust与Android互相调用

前言 Rust的JNI流程以及方法实际上和我们常见的C JNI是十分相似的.我们本章将使用Rust实现常见的JNI调用功能.关于更多的用法,可参考官方示例,github地址为https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs. 基本交互功能实现 1. Java传String,返回b…

Java集合复习

文章目录集合概述、collection集合体系特点Collection集合的遍历增强for循环集合概述、collection集合体系特点 集合都是支持泛型的&#xff0c;但是集合只能存储对象&#xff0c;因此集合也叫做对象集合。 public static void main(String[] args) {Collection<String>l…

分布式 | 令人头疼的堆外内存泄露怎么排查?

作者&#xff1a;鲍凤其 爱可生 dble 团队开发成员&#xff0c;主要负责 dble 需求开发&#xff0c;故障排查和社区问题解答。少说废话&#xff0c;放码过来。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联…

OpenFace Win10 运行和抽离部分代码

需求&#xff1a;提取出OpenFace中的GazeAnaLyser 中的部分代码往一个写好的接口里面放&#xff0c;主要实现提取面部的所有关键点&#xff0c;估计出视线的功能&#xff1b; 一&#xff0c;openface的安装与使用 在win10上把openface跑起来这个链接够用了&#xff0c;这里主要…

非零基础自学Golang 第5章 流程控制 5.2 循环控制

非零基础自学Golang 文章目录非零基础自学Golang第5章 流程控制5.2 循环控制5.2.1 for循环5.2.2 break 跳出循环5.2.3 continue 继续循环第5章 流程控制 5.2 循环控制 5.2.1 for循环 Go语言中的循环逻辑通过for关键字实现。不同于其他编程语言&#xff0c;Go语言没有while关…

NeurIPS2022 | OmniVL: 用于Image-Language和Video-Language任务的通用模型

原文标题&#xff1a;OmniVL: One Foundation Model for Image-Language and Video-Language Tasks 论文链接&#xff1a;OmniVL: One Foundation Model for Image-Language and Video-Language Tasks | OpenReview 三模态统一的工作。 一、问题提出 旨在设计一个全视觉语言…

正点原子基于库和寄存器建立keil的工程文件简单理解(不是具体步骤)

下载mdk 个人上传免费的pdf:https://download.csdn.net/download/weixin_43794311/87232741&#xff0c;或直接到正点原子官网下载 https://www.keil.com/download/ 两种建立方式的本质理解 库函数是有人基于寄存器已经完成对寄存器的设置&#xff0c;只要根据函数参数意义&…

【Uni-App】vscode 开发uni-app 配置eslint、prettier 实现代码检查和代码自动格式化

目录一&#xff1a;前言二、利用HBuilderX创建uni-app项目三、配置代码检查和代码自动格式化1. 在vscode中打开项目2. 创建package.json3. 添加eslint、prettier相关依赖4. 配置.eslintrc.js5. 配置.prettierrc.json6. 配置.editorconfig7. 配置.eslintignore如果以上还不生效&…

USB——域,包,事务及传输

USB 域&#xff0c;包&#xff0c;事务及传输 域组成包包组成事务事务组成传输 字节序 LSB 概览 域通常来说有八个 SYNCPIDADDRENDP&#xff1a;FRAMEDATACRCEOP 多个域组成包&#xff0c;USB 的包分为四大类 token&#xff1a;令牌包data&#xff1a;数据包handshake&…

centos7安装samba

关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld.service [rootlocalhost ~]# systemctl disable firewalld.service Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. Removed symlink /etc/systemd/system/basic.target.wants/f…

单目测距(yolo目标检测+标定+测距代码)

单目测距&#xff08;目标检测标定测距&#xff09;** 实时感知本车周围物体的距离对高级驾驶辅助系统具有重要意义&#xff0c;当判定物体与本车距离小于安全距离时便采取主动刹车等安全辅助功能&#xff0c;这将进一步提升汽车的安全性能并减少碰撞的发生。上一章本文完成了目…

Shell 标准输入和输出

无论是要交给程序处理的数据&#xff0c;还是控制脚本的简单命令&#xff0c;都少不了输入和输出。程序要做的第一件事就是处理如同一阴一阳的“输入与输出”。 1 、从文件获取输入 当我们希望向文件输出内容时&#xff0c;我们可以通过符号 > 或 >> 实现。而用代表…