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

Rust FFI 与C语言互相调用

参考

https://cloud.tencent.com/developer/article/2077534
https://github.com/shepmaster/rust-ffi-omnibus

cbindgen 简介

二进制方式构建

$ cargo install cbindgen
//默认构建C++头文件 C语言需要 --lang C
$ cd /path/to/my/project && cbindgen . -o target/my_project.h

使用配置构建参考:https://github.com/eqrion/cbindgen/blob/master/docs.md

脚本构建

Cargo.toml

[package]
...
build="build.rs"[build-dependencies]
...
cbindgen = "x.x.x"

build.rs 与 Cargo.toml同级目录,自用的build.rs配置

extern crate cbindgen;use cbindgen::RenameRule::CamelCase;
use cbindgen::{StructConfig, ParseConfig, SortKey::Name};use std::env;
use std::path::PathBuf;
use cbindgen::Config;
use std::vec;fn main() {let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();let package_name = env::var("CARGO_PKG_NAME").unwrap();let output_file = target_dir().join(format!("{}.hpp", package_name)).display().to_string();let structure = StructConfig{rename_fields : CamelCase,..Default::default()};let parse = ParseConfig {parse_deps: true,include: Some(vec![String::from("reqwest::blocking")]),..Default::default()};let config = Config {namespace: Some(String::from("ffi")),includes: vec![String::from("ffi.hpp")],pragma_once: true,cpp_compat:true,sort_by: Name,structure,parse,..Default::default()};cbindgen::generate_with_config(&crate_dir, config).unwrap().write_to_file(&output_file);
}/// Find the location of the `target/` directory. Note that this may be
/// overridden by `cmake`, so we also need to check the `CARGO_TARGET_DIR`
/// variable.
fn target_dir() -> PathBuf {if let Ok(target) = env::var("CARGO_TARGET_DIR") {PathBuf::from(target)} else {PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("target")}
}

Demo程序说明

Cargo.toml 可能需要

[dependencies]
libc = "0.2.139"[lib]
crate_type = ["cdylib"]
name = "demo"
cargo build --release
make
./test_c

makefile

all:demodemo:main.c#或者在这加入cargo build --releasegcc -o demo main.c  -I/usr/include -L./target/release -ldemoclean:rm demo

test脚本

chmod +x test_c

export LD_LIBRARY_PATH=$PWD/target/release:$LD_LIBRARY_PATH./demo

基本数据类型

Rust侧

#[no_mangle]
pub extern "C" fn addition(a: u32, b: u32) -> u32 {a + b
}

C侧

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>extern uint32_t
addition(uint32_t, uint32_t);int main(void) {uint32_t sum = addition(1, 2);printf("%" PRIu32 "\n", sum);
}

对象

这里的对象传递是通过在堆上建立Rust风格的struct内存区,传递给C的是对象的指针,C中保存指针,使用时传入Rust,Rust根据struct信息操作内存。
后面元组部分展示了#[repr©]的方式,直接建立C风格的struct内存区,C中可以直接操作struct内的内存。

Rust侧

extern crate libc;use libc::c_char;
use std::collections::HashMap;
use std::ffi::CStr;pub struct ZipCodeDatabase {population: HashMap<String, u32>,
}impl ZipCodeDatabase {fn new() -> ZipCodeDatabase {ZipCodeDatabase {population: HashMap::new(),}}fn populate(&mut self) {for i in 0..100_000 {let zip = format!("{:05}", i);self.population.insert(zip, i);}}fn population_of(&self, zip: &str) -> u32 {self.population.get(zip).cloned().unwrap_or(0)}
}#[no_mangle]
pub extern "C" fn zip_code_database_new() -> *mut ZipCodeDatabase {Box::into_raw(Box::new(ZipCodeDatabase::new()))
}#[no_mangle]
pub extern "C" fn zip_code_database_free(ptr: *mut ZipCodeDatabase) {if ptr.is_null() {return;}unsafe {Box::from_raw(ptr);}
}#[no_mangle]
pub extern "C" fn zip_code_database_populate(ptr: *mut ZipCodeDatabase) {let database = unsafe {assert!(!ptr.is_null());&mut *ptr};database.populate();
}#[no_mangle]
pub extern "C" fn zip_code_database_population_of(ptr: *const ZipCodeDatabase,zip: *const c_char,
) -> u32 {let database = unsafe {assert!(!ptr.is_null());&*ptr};let zip = unsafe {assert!(!zip.is_null());CStr::from_ptr(zip)};let zip_str = zip.to_str().unwrap();database.population_of(zip_str)
}

C侧

c++ 最好使用包装类,在析构中free对象

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>typedef struct zip_code_database zip_code_database_t;extern zip_code_database_t *
zip_code_database_new(void);extern void
zip_code_database_free(zip_code_database_t *);extern void
zip_code_database_populate(zip_code_database_t *);extern uint32_t
zip_code_database_population_of(const zip_code_database_t *, const char *zip);int main(void) {zip_code_database_t *database = zip_code_database_new();zip_code_database_populate(database);uint32_t pop1 = zip_code_database_population_of(database, "90210");uint32_t pop2 = zip_code_database_population_of(database, "20500");zip_code_database_free(database);printf("%" PRId32 "\n", (int32_t)pop1 - (int32_t)pop2);
}

slice

Rust侧

extern crate libc;use libc::size_t;
use std::slice;#[no_mangle]
pub extern "C" fn sum_of_even(n: *const u32, len: size_t) -> u32 {let numbers = unsafe {assert!(!n.is_null());slice::from_raw_parts(n, len as usize)};numbers.iter().filter(|&v| v % 2 == 0).sum()
}

C侧

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>extern uint32_t
sum_of_even(const uint32_t *numbers, size_t length);int main(void) {uint32_t numbers[] = {1, 2, 3, 4, 5, 6};size_t length = sizeof numbers / sizeof *numbers;uint32_t sum = sum_of_even(numbers, length);printf("%" PRIu32 "\n", sum);
}

字符串C传入

不复制数据

Rust侧

入参 *const c_char

extern crate libc;use libc::c_char;
use std::ffi::CStr;#[no_mangle]
pub extern "C" fn print_c_string(s: *const c_char) {let c_str = unsafe {assert!(!s.is_null());CStr::from_ptr(s)};let str = c_str.to_str().unwrap();println!("printed from rust: {:#?}",str);
}

C侧

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>extern uint32_t
print_c_string(const char *str);int main(void) {print_c_string("göes to élevên");
}

复制数据

在to_owned时复制了数据,不怕C侧free掉原来的内存

Rust侧

extern crate libc;use libc::c_char;
use std::ffi::CStr;#[no_mangle]
pub extern "C" fn print_c_string(s: *const c_char) {let c_str = unsafe {assert!(!s.is_null());CStr::from_ptr(s)};let r_str = c_str.to_str().unwrap().to_owned();println!("printed from rust: {:#?}",r_str);
}

C侧

与前面一样

字符串Rust传出

C侧需要释放的方式

不推荐这几种方式,很容易忘记释放导致内存泄漏。

1:提供专门的函数

普通方式在C侧要主动销毁,推荐采用RAII方式。

Rust侧

extern crate libc;use libc::c_char;
use std::ffi::CString;
use std::iter;#[no_mangle]
pub extern "C" fn theme_song_generate(length: u8) -> *mut c_char {let mut song = String::from("💣 ");song.extend(iter::repeat("na ").take(length as usize));song.push_str("Batman! 💣");let c_str_song = CString::new(song).unwrap();c_str_song.into_raw()
}#[no_mangle]
pub extern "C" fn theme_song_free(s: *mut c_char) {unsafe {if s.is_null() {return;}drop(CString::from_raw(s));};
}

C侧

#include <stdio.h>
#include <stdint.h>extern char *
theme_song_generate(uint8_t length);extern void
theme_song_free(char *);int main(void) {char *song = theme_song_generate(5);printf("%s\n", song);theme_song_free(song);
}

2:malloc函数传入rust

rust侧

use libc::{c_char, c_void};
use std::ffi::CString;
use std::usize;const STRING : &str= "hello markrenChina";type Allocator = unsafe extern fn(usize) -> *mut c_void;#[no_mangle]
pub unsafe extern fn get_string_with_allocator(allocator: Allocator) -> *mut c_char {let ptr: *mut c_char = allocator(STRING.as_bytes().len()).cast();copy_string(ptr);ptr
}///这里展示与缓冲区方式不一样的函数copy api
#[no_mangle]
pub unsafe extern fn copy_string(ptr: *mut c_char) {let bytes = STRING.as_bytes();let len = bytes.len();std::ptr::copy(STRING.as_bytes().as_ptr().cast(), ptr, len);std::ptr::write(ptr.offset(len as isize) as *mut u8, 0u8);
}#[no_mangle]
pub unsafe extern fn free_string(ptr: *const c_char) {let _ = CString::from_raw(ptr as *mut _);
}

C侧

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>typedef void *(*Allocator)(uintptr_t);char *get_string_with_allocator(Allocator allocator);/***这里展示与缓冲区方式不一样的函数copy api*/
void copy_string(char *ptr);void free_string(const char *ptr);int main() {char* rust_string = get_string_with_allocator(malloc);printf("%s\n",rust_string);free(rust_string);  //This use free not free_string
}

3:Rust调用glibc

具体见示例工程

Rust侧

#[no_mangle]
pub unsafe extern fn get_string_with_malloc() -> *mut c_char{let ptr: *mut c_char = libc::malloc(get_string_len()).cast();copy_string(ptr);ptr
}

C侧

int main() {char* rust_string = get_string_with_malloc();printf("%s\n",rust_string);free(rust_string);  //This use free not free_string
}

回调的方式

回调方式不需要C侧主动释放,因为是借用Rust的字符串,但是可能需要注意线程。

Rust侧

use std::ffi::*;type Callback = unsafe extern fn(*const c_char);#[no_mangle]
pub unsafe extern "C" fn rust_call_c(callback: Callback ){let c_string = CString::new("su").expect("CString new failed");callback(c_string.as_ptr())
}

C侧

#include <stdio.h>typedef void (*Callback)(const char*);void rust_call_c(Callback callback);void callback(const char* string) {printf("printed from C: %s \n", string);
}int main() {rust_call_c(callback);return 0;
}

C传入缓冲区方式

windows系统调用常见的方式。
不同上面的C侧主动释放的方式,传入缓冲区的方式是,C侧申请(malloc)C侧释放(free)的方式是常规的代码操作。
c++可以通过包装,传出std::string,析构释放。

Rust侧

use libc::{c_char, c_int };
use std::{slice, ptr ,usize};const HELLO: &str = "hello worls";#[no_mangle]
pub unsafe extern "C" fn get_string_api(buffer: *mut c_char, length: *mut usize) -> c_int {if buffer.is_null() {if length.is_null(){return -1;}else {*length = HELLO.asbytes().len() + 1;return 0;}}let buffer = slice::from_raw_parts_mut(buffer as *mut u8, *length);if HELLO.len() >= buffer.len() {return -1;}ptr::copy_nonoverlapping(HELLO.as_ptr(),buffer.as_mut_ptr(),HELLO.len(),);buffer[HELLO.len()] = 0;HELLO.len() as c_int
}

C侧

#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>/**仿windows系统函数的方式    * if buffer NULL @return need len* @retrun -1 fail*/
int get_string_api(char *buffer, uintptr_t *length);int main(){size_t* len;get_string_api(NULL,len);printf("len = %ld\n", *len);char * buff = malloc((*len) * sizeof(char ));get_string_api(buff,len);printf("string = %s\n", buff);free(buff);
}

元组

#[repr©]这种以C语言方式的结构体内存布局方式,能被大部分高级语言解析并使用。

Rust侧

use std::convert::From;// A Rust function that accepts a tuple
fn flip_things_around_rust(tup: (u32, u32)) -> (u32, u32) {let (a, b) = tup;(b + 1, a - 1)
}// A struct that can be passed between C and Rust
#[repr(C)]
pub struct Tuple {x: u32,y: u32,
}// Conversion functions
impl From<(u32, u32)> for Tuple {fn from(tup: (u32, u32)) -> Tuple {Tuple { x: tup.0, y: tup.1 }}
}impl From<Tuple> for (u32, u32) {fn from(tup: Tuple) -> (u32, u32) {(tup.x, tup.y)}
}// The exported C method
#[no_mangle]
pub extern "C" fn flip_things_around(tup: Tuple) -> Tuple {flip_things_around_rust(tup.into()).into()
}

C侧

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>typedef struct {uint32_t x;uint32_t y;
} tuple_t;extern tuple_t
flip_things_around(tuple_t);int main(void) {tuple_t initial = { .x = 10, .y = 20 };tuple_t result = flip_things_around(initial);printf("(%" PRIu32 ",%" PRIu32 ")\n", result.x, result.y);
}

vector给C

Rust侧

extern crate libc;use libc::size_t;
use std::mem;#[no_mangle]
pub extern "C" fn counter_generate(size: size_t, vec: *mut *mut i16) -> size_t {let mut counted: Vec<_> = (0..).take(size).collect();counted.shrink_to_fit();let ret = counted.len();unsafe { *vec = counted.as_mut_ptr() };mem::forget(counted);ret
}#[no_mangle]
pub extern "C" fn counter_free(arr: *mut i16, size: size_t) {unsafe {if arr.is_null() {return;}Vec::from_raw_parts(arr, size, size)};
}

C侧

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>extern size_t
counter_generate(size_t size, int16_t **vec);extern void
counter_free(int16_t *vec, size_t size);int main(void) {int16_t *vec;size_t vec_len = counter_generate(10, &vec);for (size_t i = 0; i < vec_len; i++) {printf("%" PRId16 "..", vec[i]);}printf("\n");counter_free(vec, vec_len);
}

CMakeList.txt配置Rust编译

简单介绍一下,使用时,在根目录下新建build文件夹,编译输出在这个文件夹下

cd build && cmake.. && make

假设我们的rust 模块叫 rustlib

父CMakeList.txt

add_subdirectory(rustlib)

rustlib目录下CMakeList.txt

# cargo 设置
if (CMAKE_BUILD_TYPE STREQUAL "Debug")set(CARGO_CMD cargo build)set(TARGET_DIR "debug")
else ()set(CARGO_CMD cargo build --release)set(TARGET_DIR "release")
endif ()set(RUSTLIB_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/librustlib.so")add_custom_target(rustlib ALLCOMMENT "Compiling rustlib module"COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} ${CARGO_CMD}COMMAND cp ${RUSTLIB_SO} ${CMAKE_CURRENT_BINARY_DIR}WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(rustlib PROPERTIES LOCATION ${CMAKE_CURRENT_BINARY_DIR})add_test(NAME rustlib_testCOMMAND cargo testWORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

使用的模块

配置CMakeList.txt

set(RUSTLIB_BUILD_DIR ${CMAKE_BINARY_DIR}/rustlib)
include_directories(${RUSTLIB_BUILD_DIR })
...
get_target_property(RUSTLIB_DIR rustlib LOCATION)
target_link_libraries(gui ${RUSTLIB_DIR }/librustlib.so)
add_dependencies(xxx rustlib)

相关文章:

Rust FFI 与C语言互相调用

参考 https://cloud.tencent.com/developer/article/2077534 https://github.com/shepmaster/rust-ffi-omnibus cbindgen 简介 二进制方式构建 $ cargo install cbindgen //默认构建C头文件 C语言需要 --lang C $ cd /path/to/my/project && cbindgen . -o target/…...

从全局变量寻找到Tomcat回显方式

前言 对于回显的获取主要是在ApplicationFilterChain类的lastServicedRequest / lastServicedResponse两个属性&#xff0c;是使用的ThreadLocal进行修饰的&#xff0c;并且&#xff0c;在执行请求的过程中&#xff0c;通过反射修改属性值&#xff0c;能够记录下当前线程的req…...

Tapdata Connector 实用指南:数据入仓场景之数据实时同步到 BigQuery

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata 是一个以低延迟数据移动为核心优势构建的现代数据平台&#xff0c;内置 60 数据连接器&#xff0c;拥有稳定的实时采集和传输能力、秒级响应的数据实时计算能力、稳定易用的数据实时服务能力&#xff0c;以及低代码可视化操作…...

关于机器人状态估计(12)-VIO/VSLAM的稀疏与稠密

VIO三相性与世界观室内ALL IN ONE 首先以此链接先对近期工作的视频做个正经的引流&#xff0c;完成得这么好的效果&#xff0c;仅仅是因为知乎限流1分钟以内的视频&#xff0c;导致整个浏览量不到300&#xff0c;让人非常不爽。 这套系统已经完成了&#xff0c;很快将正式发布…...

Python每日一练(20230220)

目录 1. 存在重复元素 II 2. 按要求实现程序功能 3. 分割链表 附录 链表 1. 存在重复元素 II 给定一个整数数组和一个整数 k&#xff0c;判断数组中是否存在两个不同的索引 i 和 j&#xff0c;使得 nums [i] nums [j]&#xff0c;并且 i 和 j 的差的 绝对值 至多为 k。 …...

技术总监的“技术提升”

技术负责人的能力要求是什么?成本中心技术负责人最重要的工作是让其他CXO理解、认可并且支持技术部的工作&#xff0c;否则作为成本部门&#xff0c;在公司的地位会很低。技术创新光是让其他部门理解还不行&#xff0c;技术还需要创造价值&#xff0c;所以需要做技术创新。上面…...

kettle安装部署_简单认识_Spoon勺子界面---大数据之kettle工作笔记002

然后我们来看一下这个kettle的安装,很简单,下载解压就可以了 上面的地址是官网很烂 下面的地址好一些 这个是官网可以看到很慢,很不友好 这个是下面那个地址,可以看到 最新的是9.0了,一般都用 一般都用8.2 这里下载这个就可以了 下载以后可以看到有个pdi...

第三章 Kafka生产问题总结及性能优化实践

第三章 Kafka生产问题总结及性能优化实践 1、线上环境规划 JVM参数设置 kafka 是 scala 语言开发&#xff0c;运行在 JVM 上&#xff0c;需要对 JVM 参数合理设置&#xff0c;参看 JVM 调优专题 修改 bin/kafka-start-server.sh 中的 JVM 设置&#xff0c;假设机器是 32G 内…...

Comparable和Comparator的区别

一、概述 Comparable和Comparator都是用来实现比较的&#xff0c;一般用于集合中元素的比较 基本包装类型&#xff0c;Integer、Long以及String都实现了Comparable接口&#xff0c;该接口的排序逻辑必须写在比较对象中&#xff0c;所以又叫自然排序 我们一般集合排序使用的Col…...

全15万字丨PyTorch 深度学习实践、基础知识体系全集;忘记时,请时常回顾。

✨ ✨我们抬头便看到星光&#xff0c;星星却穿越了万年. ✨ ✨ &#x1f3af;作者主页&#xff1a;追光者♂ &#x1f338;个人简介&#xff1a;在读计算机专业硕士研究生、CSDN-人工智能领域新星创作者&#x1f3c6;、2022年度博客之星人工智能领域TOP4&#x1f31f;、阿里云…...

简洁易用的记账小程序——微点记账

背景 由于每个月的信用卡账单太过吓人&#xff0c;记性也不是特别的好&#xff0c;加上微信支付宝账单中有些明细不是很明确。比如在京东花销的明细不会记录用户购买了什么&#xff0c;只会记录那个通道支出的。所以&#xff0c;才会有了想自己开发一款记账小程序&#xff0c;…...

Windows平台上达梦数据库的ODBC安装与配置

文章目录概述安装包准备安装ODBC驱动配置ODBC数据源概述 最近很多公司都在响应信创&#xff0c;需要切换到国产数据库&#xff0c;然而很多数据库的一些基础组件的使用都没有一个很明确的官方文档。为了避免更多的人踩坑&#xff0c;本人将踩过的坑总结成博文&#xff0c;分享…...

哈希表的介绍

1.哈希表的介绍 在哈希表中插入、删除或查找一个元素都只需要O(1)的时间&#xff0c;因此经常被用来优化时间效率。 在Java中&#xff0c;哈希表有两个对应的类型&#xff0c;即HashSet和HashMap。 2.HashSet的应用 若每个元素都只有一个值&#xff0c;则用HashSet&#xf…...

spring cloud gateway 实现redis动态路由及自动项目路由上报

前言 spring cloud gateway默认为内存存储策略&#xff0c;通过配置文件加载的方式生成路由定义信息 可以看到&#xff0c;RouteDefinitionRepository继承了两个父接口&#xff0c;分别为RouteDefinitionLocator和RouteDefinitionWriter&#xff0c;RouteDefinitionLocator定…...

c++函数对象(仿函数)、谓词、内建函数对象

1、函数对象 1.1 概念 重载函数调用操作符的类&#xff0c;这个类的对象就是函数对象&#xff0c;在使用这个函数对象对应使用重载的&#xff08;&#xff09;符号时&#xff0c;行为类似于函数调用&#xff0c;因此这个函数也叫仿函数。 注意&#xff1a;函数对象&#xff0…...

物联网对供应链管理的影响

物联网对于许多行业来说都是一项革命性技术&#xff0c;其应用领域涉及零售、交通、金融、医疗保健和能源等行业。物联网在供应链等流程中已经展示了其深度的潜力。管理、预测和监督应用程序有助于车队运输经理提高配送的运营效率&#xff0c;并增加决策的准确性。如今&#xf…...

c++ 那些事 笔记

GitHub - Light-City/CPlusPlusThings: C那些事 1. ① extern extern关键字&#xff0c;C语言extern关键字用法详解 如果全局变量不在文件的开头定义&#xff0c;其有效的作用范围只限于其定义处到文件结束。如果在定义点之前的函数想引用该全局变量&#xff0c;则应该在…...

心跳机制Redis

 进入命令传播阶段候&#xff0c;master与slave间需要进行信息交换&#xff0c;使用心跳机制进行维护&#xff0c;实现双方连接保持在线 master心跳&#xff1a; 指令&#xff1a;PING 周期&#xff1a;由repl-ping-slave-period决定&#xff0c;默认10秒 作用&#…...

蓝桥杯算法训练合集十七 1.数字反转2.试题39713.矮人采金子4.筛法5.机器指令

目录 1.数字反转 2.试题3971 3.矮人采金子 4.筛法 5.机器指令 1.数字反转 问题描述 给定一个整数&#xff0c;请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式&#xff0c;即除非给定的原数为零&#xff0c;否则反转后得到的新数的最高位数字不应为零&…...

第一章 初识 Spring Security

第一章 初识 Spring Security 1、权限管理 权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现了对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...