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

Zephyr 消息队列

文章目录

  • 简介
  • 数据结构
    • k_msgq
  • 定义消息队列
  • 发送消息
    • k_msgq_put
  • 接收消息
    • k_msgq_get
  • wait_q 的双重身份
  • 清理消息队列
    • k_msgq_cleanup
  • 重置消息队列
    • k_msgq_purge
  • 读取数据
    • k_msgq_peek
    • k_msgq_peek_at
  • 缓冲区容量
    • k_msgq_num_free_get
    • k_msgq_num_used_get

简介

  • message queue 用于中断和线程之间进行数据传输的一项服务,其本质是一个环形缓冲区,例如串口驱动中通常会使用环形缓冲区来接收数据,缓冲区中每一项的大小是固定的。
  • Zephyr 中的 msgq 是消息队列的具体实现,它需要提供一个缓冲区用于存储数据,缓冲区的大小是缓存项大小的整数倍,与 Zephyr 中的 queue 不同,消息队列写入和读取会拷贝数据。

数据结构

k_msgq

  • k_msgq 是 Zephyr 中用于描述 消息队列的一个结构体,其成员变量包含了实现消息队列的必要元素:
struct k_msgq {/** Message queue wait queue */_wait_q_t wait_q;/** Lock */struct k_spinlock lock;/** Message size */size_t msg_size;/** Maximal number of messages */uint32_t max_msgs;/** Start of message buffer */char *buffer_start;/** End of message buffer */char *buffer_end;/** Read pointer */char *read_ptr;/** Write pointer */char *write_ptr;/** Number of used messages */uint32_t used_msgs;_POLL_EVENT;/** Message queue */uint8_t flags;SYS_PORT_TRACING_TRACKING_FIELD(k_msgq)
};
  • wait_q
    • 保存因缓冲区为空被阻塞的接收线程,或者是因缓冲区满被阻塞的发送线程。
  • lock
    • 多线程互斥
  • msg_size
    • 消息队列中每一项的大小
  • max_msgs
    • 消息队列中能容纳消息的最大数量
  • buffer_start
    • 缓冲区起始地址
  • buffer_end
    • 缓冲区结束地址
  • read_ptr
    • 类似于环形缓冲区中的 head,每读取一次地址向后偏移固定长度。
  • write_ptr
    • 类似于环形缓冲区中的 tail,每写入一次地址向后偏移固定长度。
  • used_msgs
    • 已经存放的消息条数,每读取一次减1,写入一次加1。
  • flags
    • 消息队列支持两种初始化方式,一种是用户提供缓冲区,另一种是由系统分配缓冲区,当选择由系统分配缓冲区时,在消息队列销毁时需要回收内存,为了标记内存来源,因此使用 flags 保存标志,下面是对应的标志位。
#define K_MSGQ_FLAG_ALLOC	BIT(0)

定义消息队列

  • 消息队列包含3个重要属性,单条消息的大小,缓冲区最大消息容量,缓冲区对齐大小。
  • 定义一个静态缓冲区可使用 K_MSGQ_DEFINE 宏
#define K_MSGQ_DEFINE(q_name, q_msg_size, q_max_msgs, q_align)		\static char __noinit __aligned(q_align)				\_k_fifo_buf_##q_name[(q_max_msgs) * (q_msg_size)];	\STRUCT_SECTION_ITERABLE(k_msgq, q_name) =			\Z_MSGQ_INITIALIZER(q_name, _k_fifo_buf_##q_name,	\q_msg_size, q_max_msgs)
  • 还可通过函数 k_msgq_init 初始化消息队列,该函数需要提供环形缓冲区和一个 k_msgq 对象:
struct msg_item_type { int val;
};static char __aligned(4) s_msgq_buffer[10 * sizeof(struct msg_item_type )];
static struct k_msgq s_msgq;k_msgq_init(&s_msgq, s_msgq_buffer, sizeof(struct msg_item_type ), 10);
  • k_msgq_init 的另一个版本是 k_msgq_alloc_init,它可以从系统中动态分配一段空间作为环形缓冲区:
static struct k_msgq s_msgq;
k_msgq_alloc_init(&s_msgq, sizeof(struct msg_item_type ), 10);

发送消息

k_msgq_put

  • 消息队列的消息发送函数:
    • int k_msgq_put(struct k_msgq *msgq, const void *data, k_timeout_t timeout)
  • 该函数的实现函数为 z_impl_k_msgq_put
int z_impl_k_msgq_put(struct k_msgq *msgq, const void *data, k_timeout_t timeout)
{/* 当 k_msgq_put 在中断中调用时,timeout 不等于 K_NO_WAIT 会触发断言 */__ASSERT(!arch_is_in_isr() || K_TIMEOUT_EQ(timeout, K_NO_WAIT), "");struct k_thread *pending_thread;k_spinlock_key_t key;int result;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, put, msgq, timeout);/* 消息队列未满时,如果等待队列中存在等待线程,* 可将数据写入 swap_data 指向的内存中(线程在等待时已将缓存首地址放入swap_data),* 反之如果没有线程处于等待状态,将数据放入环形缓冲区中*/if (msgq->used_msgs < msgq->max_msgs) {/* message queue isn't full */pending_thread = z_unpend_first_thread(&msgq->wait_q);if (pending_thread != NULL) {SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, 0);/* give message to waiting thread */(void)memcpy(pending_thread->base.swap_data, data,msgq->msg_size);/* wake up waiting thread */arch_thread_return_value_set(pending_thread, 0);z_ready_thread(pending_thread);z_reschedule(&msgq->lock, key);return 0;} else {/* put message in queue */(void)memcpy(msgq->write_ptr, data, msgq->msg_size);msgq->write_ptr += msgq->msg_size;if (msgq->write_ptr == msgq->buffer_end) {msgq->write_ptr = msgq->buffer_start;}msgq->used_msgs++;
#ifdef CONFIG_POLLhandle_poll_events(msgq, K_POLL_STATE_MSGQ_DATA_AVAILABLE);
#endif /* CONFIG_POLL */}result = 0;} /* 队列满且等待时间为0,返回 -ENOMSG */else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {/* don't wait for message space to become available */result = -ENOMSG;} /* 队列满且等待时间不为0,将当前线程的 swap_data 指向数据首地址,然后将调用线程挂起 */else {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, put, msgq, timeout);/* wait for put message success, failure, or timeout */_current->base.swap_data = (void *) data;result = z_pend_curr(&msgq->lock, key, &msgq->wait_q, timeout);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, result);return result;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, result);k_spin_unlock(&msgq->lock, key);return result;
}
  • k_msgq_put 用于将消息写入环形缓冲区,该函数允许在中断中使用,在中断使用时 timeout 必须是0.
  • 当等待队列非空时,可将数据直接拷贝至 tcb 中 swap_data 指向的内存中,相比放入队列后再从队列中将数据拷贝到用户空间,这种方式提高了效率。
  • 只有当等待队列为空时,才会将数据暂存入环形缓冲区中,等待线程读取。

接收消息

k_msgq_get

  • 消息队列的消息接收函数:
    • int k_msgq_get(struct k_msgq *msgq, void *data, k_timeout_t timeout)
  • 该函数的实现函数为 z_impl_k_msgq_get
int z_impl_k_msgq_get(struct k_msgq *msgq, void *data, k_timeout_t timeout)
{__ASSERT(!arch_is_in_isr() || K_TIMEOUT_EQ(timeout, K_NO_WAIT), "");k_spinlock_key_t key;struct k_thread *pending_thread;int result;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, get, msgq, timeout);/* 当环形缓冲区中不为空时,从中读取一条消息 */if (msgq->used_msgs > 0U) {/* take first available message from queue */(void)memcpy(data, msgq->read_ptr, msgq->msg_size);msgq->read_ptr += msgq->msg_size;if (msgq->read_ptr == msgq->buffer_end) {msgq->read_ptr = msgq->buffer_start;}msgq->used_msgs--;/* 从等待队列中获取一个因缓冲区满,无法写入数据而被挂起的线程 */pending_thread = z_unpend_first_thread(&msgq->wait_q);if (pending_thread != NULL) {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, get, msgq, timeout);/* 将线程需写入的数据放入缓冲区中 */(void)memcpy(msgq->write_ptr, pending_thread->base.swap_data,msgq->msg_size);msgq->write_ptr += msgq->msg_size;if (msgq->write_ptr == msgq->buffer_end) {msgq->write_ptr = msgq->buffer_start;}msgq->used_msgs++;/* 唤醒该线程 */arch_thread_return_value_set(pending_thread, 0);z_ready_thread(pending_thread);z_reschedule(&msgq->lock, key);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, 0);return 0;}result = 0;}/* 缓冲区中没有数据,且等待时间为0,返回 -ENOMSG */else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {/* don't wait for a message to become available */result = -ENOMSG;} /* 缓冲区中没有数据,且等待时间不为0,将调用线程添加到等待队列并挂起 */else {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, get, msgq, timeout);/* wait for get message success or timeout */_current->base.swap_data = data;result = z_pend_curr(&msgq->lock, key, &msgq->wait_q, timeout);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, result);return result;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, result);k_spin_unlock(&msgq->lock, key);return result;
}
  • k_msgq_get 首先会判断缓冲区中是否有数据,如果缓冲区中有数据,会从中取出一条数据拷贝到用户空间。
  • 缓冲区中有数据就意味着等待队列中可能存在因缓冲区满无法写入而被阻塞的线程,此时由于从缓冲区中读出一条数据,可以进行写入,便将被挂起线程中未写入的数据拷贝到缓冲区中,同时解除挂起。
  • 如果缓冲区无数据且等待时间为0,返回 -ENOMSG。
  • 如果缓冲区无数据且等待时间不为0,将当前线程挂起。

wait_q 的双重身份

  • 消息队列与其他 IPC 对象中的 wait_q 有所不同,以Mutex为例,只有当加锁时需要进入等待,因此 wait_q 中存放的一定是因加锁被阻塞的线程。
  • 而在消息队列中无论是发送数据还是接收数据都可能进入等待,并且它们使用的是同一个等待队列。
  • 首先来看一下进入等待队列的条件:
    • 发送线程在缓冲区满,且等待时间不为0时,将被放入等待队列中。
    • 接收线程在缓冲区空,且等待时间不为0时,将被放入等待队列中。
  • 两种情况不可能同时存在,因此可以共用一个等待队列。

清理消息队列

k_msgq_cleanup

  • 清理消息队列主要作用是,回收系统分配的缓冲区。
  • 为了安全,在清理时会检查消息队列是否仍然在被使用
int k_msgq_cleanup(struct k_msgq *msgq)
{SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, cleanup, msgq);CHECKIF(z_waitq_head(&msgq->wait_q) != NULL) {SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, cleanup, msgq, -EBUSY);return -EBUSY;}if ((msgq->flags & K_MSGQ_FLAG_ALLOC) != 0U) {k_free(msgq->buffer_start);msgq->flags &= ~K_MSGQ_FLAG_ALLOC;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, cleanup, msgq, 0);return 0;
}

重置消息队列

k_msgq_purge

  • void k_msgq_purge(struct k_msgq *msgq)
void z_impl_k_msgq_purge(struct k_msgq *msgq)
{k_spinlock_key_t key;struct k_thread *pending_thread;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC(k_msgq, purge, msgq);/* 唤醒等待队列中被阻塞的线程,然后返回-ENOMSG */while ((pending_thread = z_unpend_first_thread(&msgq->wait_q)) != NULL) {arch_thread_return_value_set(pending_thread, -ENOMSG);z_ready_thread(pending_thread);}/* 然后将缓冲区中的数据清空 */msgq->used_msgs = 0;msgq->read_ptr = msgq->write_ptr;z_reschedule(&msgq->lock, key);
}
  • 该函数用于清空消息队列,如果有线程因数据发送或接收而进入等待,同时会将被挂起的线程唤醒。

读取数据

k_msgq_peek

  • int k_msgq_peek(struct k_msgq *msgq, void *data)
  • 该函数以先进先出的方式从消息队列中读取一条消息,但是不会修改 read_str 将队列项移出队列,因此该操作无需等待。

k_msgq_peek_at

  • int k_msgq_peek_at(struct k_msgq *msgq, void *data, uint32_t idx)
  • k_msgq_peek_at 是 k_msgq_peek 的另一个版本,以 read_str 作为第一项,向后偏移 idx 项之后的数据,将其拷贝到 data 指向的内存中。

缓冲区容量

k_msgq_num_free_get

  • uint32_t k_msgq_num_free_get(struct k_msgq *msgq)
  • k_msgq_num_free_get 用于获取缓冲区中剩余容量,返回值代表可存储消息的数量。

k_msgq_num_used_get

  • uint32_t k_msgq_num_used_get(struct k_msgq *msgq)
  • k_msgq_num_used_get 用于获取已经使用的容量,即 used_msgs 的值。

相关文章:

Zephyr 消息队列

文章目录 简介数据结构k_msgq 定义消息队列发送消息k_msgq_put 接收消息k_msgq_get wait_q 的双重身份清理消息队列k_msgq_cleanup 重置消息队列k_msgq_purge 读取数据k_msgq_peekk_msgq_peek_at 缓冲区容量k_msgq_num_free_getk_msgq_num_used_get 简介 message queue 用于中…...

Jenkins自动化部署实例讲解

文章目录 前言实例讲解基本环境全局工具配置创建任务任务配置源码管理构建步骤&#xff08;Build Steps&#xff09;第一步&#xff1a;调用Maven第二步&#xff1a;执行shell启动容器 后记 前言 你平常在做自己的项目时&#xff0c;是否有过部署项目太麻烦的想法&#xff1f;…...

RK356X 解除UVC摄像头预览分辨率1080P限制

平台 RK3566 Android 11 概述 UVC&#xff1a; USB video class&#xff08;又称为USB video device class or UVC&#xff09;就是USB device class视频产品在不需要安装任何的驱动程序下即插即用&#xff0c;包括摄像头、数字摄影机、模拟视频转换器、电视卡及静态视频相机…...

English Learning - L2-14 英音地道语音语调 重音技巧 2023.04.10 周一

English Learning - L2-14 英音地道语音语调 重音技巧 2023.04.10 周一 课前热身重音日常表达节奏单词全部重读的句子间隔时间非重读单词代词和缩约词助动词声临其境语调预习 课前热身 学习目标 重音 重弱突出&#xff0c;重音突出核心表达的意思 重音是落在重读单词上&…...

3.6 n维随机变量

学习目标&#xff1a; 学习n维随机变量需要掌握一定的数学知识&#xff0c;包括多元微积分、线性代数和概率论等。要学习n维随机变量&#xff0c;我会采取以下步骤&#xff1a; 复习相关的数学知识&#xff1a;首先&#xff0c;我会复习多元微积分、线性代数和概率论的基本知…...

JavaSE学习进阶day06_02 Set集合和Set接口

第二章 Set系列集合和Set接口 Set集合概述&#xff1a;前面学习了Collection集合下的List集合&#xff0c;现在继续学习它的另一个分支&#xff0c;Set集合。 set系列集合的特点&#xff1a; Set接口&#xff1a; java.util.Set接口和java.util.List接口一样&#xff0c;同样…...

基于matlab分析卫星星座对通信链路的干扰

一、前言 此示例说明如何分析从中地球轨道 &#xff08;MEO&#xff09; 中的卫星星座到位于太平洋的地面站的下行链路上的干扰。干扰星座由低地球轨道&#xff08;LEO&#xff09;的40颗卫星组成。此示例确定下行链路闭合的时间、载波噪声加干扰比以及链路裕量。 此示例需要卫…...

Python中的异常——概述和基本语法

Python中的异常——概述和基本语法 摘要&#xff1a;Python中的异常是指在程序运行时发生的错误情况&#xff0c;包括但不限于除数为0、访问未定义变量、数据类型错误等。异常处理机制是Python提供的一种解决这些错误的方法&#xff0c;我们可以使用try/except语句来捕获异常并…...

Tomcat 部署与优化

1. Tomcat概述 Tomcat是Java语言开发的&#xff0c;Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;是Apache软件基金会的Jakarta项目中的一个核心项目&#xff0c;由Apache、Sun和其他一些公司及个人 共同开发而成。Tomcat属于轻量级应用服务器&#xff0c;在…...

多模态之论文笔记ViLT

文章目录 ViLT: Vision-and-Language Transformer Without Convolution or Region Supervision一. 简介1.1 摘要1.2 文本编码器&#xff0c;图像编码器&#xff0c;特征交互复杂度分析1.2 特征交互方式分析1.3 图像特征提取分析 二. 方法 Vision-and-Language Transformer2.1.方…...

微服务架构下认证和鉴权理解

认证和鉴权 从单体应用到微服务架构&#xff0c;优势很多&#xff0c;但是并不是代表着就没有一点缺点了。 微服务架构&#xff0c;意味着每个服务都是松散耦合的。因此&#xff0c;作为软件工程师和架构师&#xff0c;我们在分布式架构中面临着安全挑战。微服务对外开放的端…...

Qt 网络编程之美:探索 URL、HTTP、服务发现与请求响应

Qt 网络编程之美&#xff1a;探索 URL、HTTP、服务发现与请求响应&#xff08;The Beauty of Qt Network Programming: Exploring URL, HTTP, Service Discovery, and Request-Response 引言&#xff08;Introduction&#xff09;QUrl 类&#xff1a;构建和解析 URL&#xff08…...

毕业2年,跳槽到下一个公司就25K了,厉害了···

本人本科就读于某普通院校&#xff0c;毕业后通过同学的原因加入软件测试这个行业&#xff0c;角色也从测试小白到了目前的资深工程师&#xff0c;从功能测试转变为测试开发&#xff0c;并顺利拿下了某二线城市互联网企业的Offer&#xff0c;年薪 30W 。 选择和努力哪个重要&a…...

设计模式 -- 适配器模式

前言 月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同…...

STM32之增量式编码器电机测速

STM32之增量式编码器电机测速 编码器编码器种类按监测原理分类光电编码器霍尔编码器 按输出信号分类增量式编码器绝对式编码器 编码器参数分辨率精度最大响应频率信号输出形式 编码器倍频 STM32的编码器模式编码器模式编码器的计数方向仅在TI1计数电机正转&#xff0c;向上计数…...

一图看懂 xlsxwriter 模块:用于创建 Excel .xlsx 文件, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlsxwriter 模块&#xff1a;用于创建 Excel .xlsx 文件, 资料整理笔记&#xff08;大全&#xff09; 摘要模块图类关系图模块全展开【xlsxwriter】统计常量模块1 xlsxwrit…...

【社区图书馆】NVMe协议的命令

声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 前言 命令由host提交到内存中的SQ队列中,更新TDBxSQ后,NVMe控制器通过DMA的方式将SQ中的命令(怎么取,如何取,取多少,因设计而异)取到控制器缓冲区…...

Nginx网站服务

Nginx概述 Nginx 是开源、高性能、高可靠、低资源消耗的 Web 和反向代理服务器&#xff0c;而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&#xff0c;即使运行几个月也不需要重新启动&#xff0c;还能在不间断服务的情况下对软件版本进行热更新。对HTTP并发…...

第八篇 Spring 集成JdbcTemplate

《Spring》篇章整体栏目 ————————————————————————————— 【第一章】spring 概念与体系结构 【第二章】spring IoC 的工作原理 【第三章】spring IOC与Bean环境搭建与应用 【第四章】spring bean定义 【第五章】Spring 集合注入、作用域 【第六章】…...

双塔模型:微软DSSM模型浅析

1.背景 DSSM是Deep Structured Semantic Model (深层结构语义模型) 的缩写&#xff0c;即我们通常说的基于深度网络的语义模型&#xff0c;其核心思想是将query和doc映射到到共同维度的语义空间中&#xff0c;通过最大化query和doc语义向量之间的余弦相似度&#xff0c;从而训…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...