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

RTthread线程间通信(邮箱,消息队列,信号/软件中断)---03信号(软件中断)源码分析

信号

实际使用看这一个

#if defined(RT_USING_SIGNALS)rt_sigset_t     sig_pending;                        /**< the pending signals 记录来了的信号 */rt_sigset_t     sig_mask;                           /**< the mask bits of signal 记录屏蔽的信号 */rt_sighandler_t *sig_vectors;                       /**< vectors of signal handler 记录处理函数 */void            *si_list;                           /**< the signal infor list 挂起的信号的信息链表 */
#endif

线程管理结构体

typedef void (*rt_sighandler_t)(int signo);

处理函数

struct siginfo_node
{siginfo_t si;struct rt_slist_node list;
};

这一个是用来记录挂起的信号的的信息

信号的处理除了会在这里面显示的位置进行, 还会在切换任务的时候处理

安装

rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
{rt_base_t level;rt_sighandler_t old = RT_NULL;rt_thread_t tid = rt_thread_self();//看一看是不是有效的值if (!sig_valid(signo)) return SIG_ERR;level = rt_hw_interrupt_disable();if (tid->sig_vectors == RT_NULL){//这一个线程之前没有安转过rt_thread_alloc_sig(tid);}if (tid->sig_vectors){old = tid->sig_vectors[signo];if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL;else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler;else tid->sig_vectors[signo] = handler;}rt_hw_interrupt_enable(level);return old;
}
void rt_thread_alloc_sig(rt_thread_t tid)
{int index;rt_base_t level;rt_sighandler_t *vectors;//获取一个足以记录处理所有信号的函数的数组vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX);RT_ASSERT(vectors != RT_NULL);for (index = 0; index < RT_SIG_MAX; index ++){//初始化为默认的函数vectors[index] = _signal_default_handler;}//把这一个数组记录在线程里面level = rt_hw_interrupt_disable();tid->sig_vectors = vectors;rt_hw_interrupt_enable(level);
}
//默认的函数
static void _signal_default_handler(int signo)
{LOG_I("handled signo[%d] with default action.", signo);return ;
}

删除(屏蔽)

//实际上是更新一下线程里面的屏蔽值
void rt_signal_mask(int signo)
{rt_base_t level;rt_thread_t tid = rt_thread_self();level = rt_hw_interrupt_disable();tid->sig_mask &= ~sig_mask(signo);rt_hw_interrupt_enable(level);
}

解除

void rt_signal_unmask(int signo)
{rt_base_t level;rt_thread_t tid = rt_thread_self();level = rt_hw_interrupt_disable();//改一下标志tid->sig_mask |= sig_mask(signo);/* let thread handle pended signals */if (tid->sig_mask & tid->sig_pending){//有需要处理的标志rt_hw_interrupt_enable(level);_signal_deliver(tid);}else{rt_hw_interrupt_enable(level);}
}
//根据要处理的有信号的线程的状态进行分支处理
static void _signal_deliver(rt_thread_t tid)
{rt_ubase_t level;level = rt_hw_interrupt_disable();/* thread is not interested in pended signals */if (!(tid->sig_pending & tid->sig_mask)){//没有待处理的信号rt_hw_interrupt_enable(level);return;}if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND){//这一个任务挂起了(他在等待这一个信号)/* resume thread to handle signal */rt_thread_resume(tid);/* add signal state */tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);rt_hw_interrupt_enable(level);//恢复这一个任务/* re-schedule */rt_schedule();//这时候已经看完是不是这一个信号待处理了}else{//这一个任务运行或ready中if (tid == rt_thread_self()){//是当前的在运行的任务/* add signal state */tid->stat |= RT_THREAD_STAT_SIGNAL;rt_hw_interrupt_enable(level);/* do signal action in self thread context */if (rt_interrupt_get_nest() == 0){//直接开启这一个软件线程rt_thread_handle_sig(RT_TRUE);}}else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL)){//不是在运行的任务, 这个时候会为这一个任务开启一个新的栈/* add signal state 更新一下标志 */tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);/* point to the signal handle entry */tid->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;tid->sig_ret = tid->sp;//记录一下之前的栈//这一个看线程处理篇//实际处理使用的之前栈下面的一部分tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,(void *)((char *)tid->sig_ret - 32), RT_NULL);//设置一下这一个线程返回以后的处理rt_hw_interrupt_enable(level);LOG_D("signal stack pointer @ 0x%08x", tid->sp);/* re-schedule */rt_schedule();}else{rt_hw_interrupt_enable(level);}}
}
//这是一个软件中断的线程, 如果需要执行这一个的线程不在runing状态, 会使用一个新的栈空间执行这一个线程
void rt_thread_handle_sig(rt_bool_t clean_state)
{rt_base_t level;rt_thread_t tid = rt_thread_self();struct siginfo_node *si_node;level = rt_hw_interrupt_disable();if (tid->sig_pending & tid->sig_mask){/* if thread is not waiting for signal 等一个信号的话直接返回 */if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT)){//这个时候不是在等一个信号while (tid->sig_pending & tid->sig_mask){//依次处理信号int signo, error;rt_sighandler_t handler;//获取一个待处理的信号si_node = (struct siginfo_node *)tid->si_list;if (!si_node) break;/* remove this sig info node from list  */if (si_node->list.next == RT_NULL)tid->si_list = RT_NULL;//这是最后一个信号elsetid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);//记录下一个信号signo   = si_node->si.si_signo;//获取标号handler = tid->sig_vectors[signo];//获取处理函数tid->sig_pending &= ~sig_mask(signo);//更新挂起的标志rt_hw_interrupt_enable(level);LOG_D("handle signal: %d, handler 0x%08x", signo, handler);if (handler) handler(signo);//执行处理函数level = rt_hw_interrupt_disable();error = -RT_EINTR;rt_mp_free(si_node); /* release this siginfo node 从内存池里面释放 *//* set errno in thread tcb */tid->error = error;}/* whether clean signal status *///这一个标志需要清除if (clean_state == RT_TRUE){tid->stat &= ~RT_THREAD_STAT_SIGNAL;}else{return;}}}rt_hw_interrupt_enable(level);
}
//非当前线程的时候线程返回时候的处理函数(软件中断)
static void _signal_entry(void *parameter)
{rt_thread_t tid = rt_thread_self();/* handle signal 处理一下信号 */rt_thread_handle_sig(RT_FALSE);/* return to thread 返回之前的在处理的任务状态 */tid->sp = tid->sig_ret;//改变栈tid->sig_ret = RT_NULL;LOG_D("switch back to: 0x%08x\n", tid->sp);tid->stat &= ~RT_THREAD_STAT_SIGNAL;//线程处理的里面分析了, 主要是切换一下运行位置以及栈rt_hw_context_switch_to((rt_ubase_t)&(tid->sp));
}

发送信号

#define sig_mask(sig_no)    (1u << sig_no)
int rt_thread_kill(rt_thread_t tid, int sig)
{siginfo_t si;rt_base_t level;struct siginfo_node *si_node;if (!sig_valid(sig)) return -RT_EINVAL;LOG_I("send signal: %d", sig);si.si_signo = sig;si.si_code  = SI_USER;si.si_value.sival_ptr = RT_NULL;level = rt_hw_interrupt_disable();if (tid->sig_pending & sig_mask(sig)){//这一个信号标志已经挂起了, 用这一个新的信息队列覆盖之前的信息/* whether already emits this signal? */struct rt_slist_node *node;struct siginfo_node  *entry;//获取挂起的信号的信息链表si_node = (struct siginfo_node *)tid->si_list;if (si_node)node = (struct rt_slist_node *)&si_node->list;elsenode = RT_NULL;/* update sig info */for (; (node) != RT_NULL; node = node->next){//遍历当前任务待处理的所有信号信息entry = rt_slist_entry(node, struct siginfo_node, list);if (entry->si.si_signo == sig){//用新的信息覆盖memcpy(&(entry->si), &si, sizeof(siginfo_t));rt_hw_interrupt_enable(level);return 0;}}}rt_hw_interrupt_enable(level);//这时候是标志没有挂起, 或者挂起了但是没有信息处理的链表项//获取一个内存块si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0);if (si_node){rt_slist_init(&(si_node->list));//更新一下信息memcpy(&(si_node->si), &si, sizeof(siginfo_t));level = rt_hw_interrupt_disable();//把这个挂入链表里面if (tid->si_list){struct siginfo_node *si_list;//这个里面前面有节点si_list = (struct siginfo_node *)tid->si_list;rt_slist_append(&(si_list->list), &(si_node->list));}else{//前面没有, 这就是第一个tid->si_list = si_node;}/* a new signal 记录一下标志 */tid->sig_pending |= sig_mask(sig);rt_hw_interrupt_enable(level);}else{LOG_E("The allocation of signal info node failed.");}/* deliver signal to this thread */_signal_deliver(tid);return RT_EOK;
}
int rt_system_signal_init(void)
{//这一个会设置rt_mp_alloc返回的大小_rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node));if (_rt_siginfo_pool == RT_NULL){LOG_E("create memory pool for signal info failed.");RT_ASSERT(0);}return 0;
}

等待信号

这一个实际是一直在等待那一个信号, 那一个信号来之前一直挂起, 不会处理其他信号

int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout)
{int ret = RT_EOK;rt_base_t   level;rt_thread_t tid = rt_thread_self();struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL;/* current context checking */RT_DEBUG_IN_THREAD_CONTEXT;/* parameters check */if (set == NULL || *set == 0 || si == NULL ){ret = -RT_EINVAL;goto __done_return;}/* clear siginfo to avoid unknown value 清空一下, 用于记录 */memset(si, 0x0, sizeof(rt_siginfo_t));level = rt_hw_interrupt_disable();/* already pending */if (tid->sig_pending & *set) goto __done;if (timeout == 0){ret = -RT_ETIMEOUT;goto __done_int;}/* suspend self thread 把自己挂起 */rt_thread_suspend(tid);/* set thread stat as waiting for signal */tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT;/* start timeout timer */if (timeout != RT_WAITING_FOREVER){/* reset the timeout of thread timer and start it */rt_timer_control(&(tid->thread_timer),RT_TIMER_CTRL_SET_TIME,&timeout);rt_timer_start(&(tid->thread_timer));}rt_hw_interrupt_enable(level);/* do thread scheduling */rt_schedule();//返回, 可能超时或者有信号来了level = rt_hw_interrupt_disable();/* remove signal waiting flag */tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT;/* check errno of thread */if (tid->error == -RT_ETIMEOUT){//是超时tid->error = RT_EOK;rt_hw_interrupt_enable(level);/* timer timeout */ret = -RT_ETIMEOUT;goto __done_return;}__done://是信号来了/* to get the first matched pending signals */si_node = (struct siginfo_node *)tid->si_list;while (si_node){//遍历一下所有的节点int signo;signo = si_node->si.si_signo;if (sig_mask(signo) & *set){//是在等的这一个*si  = si_node->si;LOG_D("sigwait: %d sig raised!", signo);if (si_prev) si_prev->list.next = si_node->list.next;//这一个信号的链表不在第一个else{//是第一个struct siginfo_node *node_next;if (si_node->list.next){//不是最后一个node_next = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);tid->si_list = node_next;}else{//唯一的信号tid->si_list = RT_NULL;}}/* clear pending */tid->sig_pending &= ~sig_mask(signo);//记录为这一个链表处理完了rt_mp_free(si_node);//释放一下break;}si_prev = si_node;if (si_node->list.next){//后面还有, 获取下一个si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);}else{si_node = RT_NULL;}}//while__done_int:rt_hw_interrupt_enable(level);__done_return:return ret;
}

其他

#ifdef RT_USING_SIGNALS/* check stat of thread for signal */level = rt_hw_interrupt_disable();if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING){extern void rt_thread_handle_sig(rt_bool_t clean_state);rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;rt_hw_interrupt_enable(level);/* check signal status 处理信号 */rt_thread_handle_sig(RT_TRUE);}else{rt_hw_interrupt_enable(level);}
#endif

在任务切换的时候rt_schedule里面

相关文章:

RTthread线程间通信(邮箱,消息队列,信号/软件中断)---03信号(软件中断)源码分析

信号 实际使用看这一个 #if defined(RT_USING_SIGNALS)rt_sigset_t sig_pending; /**< the pending signals 记录来了的信号 */rt_sigset_t sig_mask; /**< the mask bits of signal 记录屏蔽的信号 */rt_sigh…...

老版本labelme如何不保存imagedata

我的版本是3.16&#xff0c;默认英文且不带取消保存imagedata的选项。 最简单粗暴的方法就是在json文件保存时把传递过来的imagedata数据设定为None&#xff0c;方法如下&#xff1a; 找到labelme的源文件&#xff0c;例如&#xff1a;D:\conda\envs\deeplab\Lib\site-packages…...

vscode 如何修改c/c++格式化风格,大括号不换行

在Visual Studio Code&#xff08;VSCode&#xff09;中&#xff0c;若要修改C代码格式化的风格以实现大括号不换行&#xff0c;通常会借助于插件C/C扩展中的ClangFormat配置。以下是具体的步骤&#xff1a; 确保已安装了C/C扩展&#xff1a; 打开VSCode的扩展市场&#xff08;…...

IP协议(2) 和 数据链路层协议基础

IP协议续 1.路由选择 在复杂的网络结构中,我们需要找到一个通往终点的路线,这就是路由选择 举个例子:我们在没有手机导航之前,想去一个地方得是到一个地方问一下路的方式最终找到目的地 路由的过程,其实就是样子问路的过程 1.当IP数据包到达路由器的时候,会查看目的IP 2.路由器…...

Flink-1.18.1环境搭建

下载 下载flink安装包 Index of /dist/flink/flink-1.18.1 下载flink-cdc安装包 Release Release 3.0.0 ververica/flink-cdc-connectors GitHub 安装 添加环境变量 vi ~/.bash_profile export FLINK_HOME=/home/postgres/flink/flink-1.18.1 export PATH=$PATH:$FL…...

deepin20.9安装及配置

安装deepin20.9很简单&#xff0c;刻录u盘 安装 一路next apt install nginx global vim-nox debian11 使用apt安装php, 使php多版本共存_debain11 php5-CSDN博客 vim LeaderF安装问题 - 知乎 debian10安装vue环境, 包括安装node.js-CSDN博客 debian安装vue3 nodejs20-CSD…...

2-2 动手学深度学习v2-损失函数-笔记

损失函数&#xff0c;用来衡量预测值和真实值之间的区别。是机器学习里面一个非常重要的概念。 三个常用的损失函数 L2 loss、L1 loss、Huber’s Robust loss 均方损失 L2 Loss l ( y , y ′ ) 1 2 ( y − y ′ ) 2 l(y,y^{\prime})\frac{1}{2}(y-y^{\prime})^{2} l(y,y′)21…...

非springboot 使用aop 切面

在非Spring Boot应用中使用AOP&#xff08;Aspect Oriented Programming&#xff0c;面向切面编程&#xff09;的代码实现需要依赖Spring AOP库。由于Spring AOP库并不直接支持非Spring应用&#xff0c;你需要将Spring AOP库作为依赖项添加到项目中&#xff0c;并使用Spring AO…...

MongoDB 字段中数据类型不一致序列化异常排查与处理

MongoDB 字段中数据类型不一致序列化异常排查与处理 背景如下&#xff0c;因为项目迁移愿意&#xff0c;一个使用Mongodb的业务拥有C#和Java两组Api。Java Api开发和测试都很顺利。上线一段时间后&#xff0c;客服反馈记录都不见了。查看数据库发现&#xff0c;时间字段拥有两…...

网络安全简介

网络安全&#xff1a; ​ 网络安全攻击分为被动攻击和主动攻击。 1. 被动攻击&#xff1a;是指攻击者从网络上窃取了他人的通信内容&#xff0c;通常把这类的攻击称为截获&#xff0c;被动攻击只要有2种形式&#xff1a;消息内容泄漏攻击和流量分析攻击。由于攻击者没…...

【Docker】.NET Core 6.0 webapi 发布上传到Docker Desktop并启动运行访问,接口返回数据乱码解决方法

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…...

【Android Gradle 插件】自定义 Gradle 插件模块 ⑤ ( 完整总结 )

一、创建自定义插件类型模块 ( Java or Kotlin Library ) 选择 " 菜单栏 / New / New Module… " 选项 , 在 " Create New Module " 对话框中 , 选择 创建 " Java or Kotlin Library " 类型的依赖库 ; 二、手动导入相关依赖 ( Java | Groovy | …...

浅析现代计算机启动流程

文章目录 前言启动流程概述磁盘分区格式MBR磁盘GPT磁盘隐藏分区 传统BIOS引导传统BIOS启动流程 UEFI引导UEFI引导程序UEFI启动流程 引导加载程序启动操作系统相关参考 前言 现代计算机的启动是一个漫长的流程&#xff0c;这个流程中会涉及到各种硬件的配置与交互&#xff0c;包…...

七月论文审稿GPT第2.5和第3版:分别微调GPT3.5、Llama2 13B以扩大对GPT4的优势

前言 自去年7月份我带队成立大模型项目团队以来&#xff0c;我司至今已有5个项目组&#xff0c;其中 第一个项目组的AIGC模特生成系统已经上线在七月官网第二项目组的论文审稿GPT则将在今年3 4月份对外上线发布第三项目组的RAG知识库问答第1版则在春节之前已就绪至于第四、第…...

Android Studio导入项目 下载gradle很慢或连接超时

AS最常见的问题之一就是下载gradle非常慢&#xff0c;还经常出现下载失败的情况&#xff0c;没有gradle就无法build项目&#xff0c;所以一定要先解决gradle的下载问题&#xff0c;下面教大家两种常用方法 手动下载压缩包并替换 1、关闭Android Studio&#xff0c;复制下面的网…...

如何使用VSCode上运行Jupyter,详细案例过程出可视化图

Python作为最受AI喜欢的语言之一&#xff0c;我们与大家共同学习下如何在VS Code上运行Jupyter&#xff0c;并且用简单案例实现出图。 环境 VS Code version: 1.80.1 Python: 3.12.0 小白安装过程&#xff1a; 在准备好基础环境&#xff0c;小白心想&#xff0c;AI可是霸占科…...

Linux中有名管道和无名管道

无名管道基础 进程间通信介绍 常用通信方式 无名管道&#xff08;pipe&#xff09; 有名管道 &#xff08;fifo&#xff09; 信号&#xff08;signal&#xff09; 共享内存(mmap) 套接字&#xff08;socket&#xff09;过时的IPC通信方式 System V IPC 共享内存&#xff08;sh…...

[SWPUCTF 2021 新生赛]easyupload1.0

发现是上传文件第一想到是文件木马 <?php eval ($_POST[123]);?>木马上传burp修改后缀发现flag里面这个是假的 我们猜想是在phpinfo我们上传<?php eval(phpinfo(););?>木马上传burp修改后缀里面 CtrlF 发现flag...

【Linux网络编程三】Udp套接字编程(简易版服务器)

【Linux网络编程三】Udp套接字编程(简易版服务器&#xff09; 一.创建套接字二.绑定网络信息1.构建通信类型2.填充网络信息①网络字节序的port②string类型的ip地址 3.最终绑定 三.读收消息1.服务器端接收消息recvfrom2.服务器端发送消息sendto3.客户端端发送消息sendto4.客户端…...

【Rust】字符串,看这篇就够了

这节课我们把字符串单独拿出来讲&#xff0c;是因为字符串太常见了&#xff0c;甚至有些应用的主要工作就是处理字符串。比如 Web 开发、解析器等。而 Rust 里的字符串内容相比于其他语言来说还要多一些。是否熟练掌握 Rust 的字符串的使用&#xff0c;对 Rust 代码开发效率有很…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

宇树科技,改名了!

提到国内具身智能和机器人领域的代表企业&#xff0c;那宇树科技&#xff08;Unitree&#xff09;必须名列其榜。 最近&#xff0c;宇树科技的一项新变动消息在业界引发了不少关注和讨论&#xff0c;即&#xff1a; 宇树向其合作伙伴发布了一封公司名称变更函称&#xff0c;因…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...