【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 同步与通信篇 -- 消息队列和邮箱处理 --(四)

请添加图片描述

  • 💌 所属专栏:【BES2500x系列】

  • 😀 作  者:我是夜阑的狗🐶

  • 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询!

  • 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!🤩 🤩 🤩

请添加图片描述

文章目录

  • 前言
  • 1 介绍
  • 2 功能特性
  • 3 同步与通信
      • 3.1 通信
  • 4 同步与通信
      • 4.1 消息队列
        • 4.1.1 定义
        • 4.1.2 创建
        • 4.1.3发送消息
      • 4.2 邮箱处理
        • 4.2.1 定义
        • 4.2.2 创建
        • 4.2.3 发送/释放邮件
        • 4.2.4 获取邮件
  • 总结


前言

  大家好,又见面了,我是夜阑的狗🐶,本文是专栏【BES2500x系列】专栏的第4篇文章;
  今天开始学习BES2500x系列的一天💖💖💖,开启新的征程,记录最美好的时刻🎉,每天进步一点点。
  专栏地址:【BES2500x系列】, 此专栏是我是夜阑的狗对BES2500x系列开发过程的总结,希望能够加深自己的印象,以及帮助到其他的小伙伴😉😉。
  如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。


<<【系列文章索引】>>

1 介绍

  在嵌入式系统中,同步和通信是确保系统内各个组件协调工作的两个核心概念。它们对于实现高效、可靠的嵌入式应用至关重要。前面已经讲过同步概念了,接下来对通信概念进行简要说明。话不多说,那接下来就学习 RTX 系统中通信机制都有哪些吧,让我们原文再续,书接上回吧。😉

在这里插入图片描述

2 功能特性

  在实时操作系统(RTOS)中,任务管理和同步通信是关键组件,它们确保系统的高效和有序执行。本文将探讨这些概念,特别是线程管理、信号量、互斥锁、消息队列和邮箱处理。

  • 任务管理:RTX提供任务创建、调度和优先级管理,确保任务按照优先级及时执行。
  • 同步与通信:包括信号量、互斥锁、消息队列和邮箱,促进任务间的同步和数据交换。
  • 内存管理:内存池和动态内存分配,有效管理有限的系统资源。
  • 定时器服务:虚拟和硬件定时器,支持周期性任务和一次性事件触发。
  • 中断处理:保证中断服务的快速响应,同时保持任务的上下文安全。
  • 线程安全:通过内核级保护机制,防止多线程环境下的数据竞争和死锁。

3 同步与通信

3.1 通信

  通信是指嵌入式系统中不同组件或任务之间交换信息的过程。有效的通信机制对于分布式系统和多处理器系统尤为重要。嵌入式系统中常用的通信方式包括:

序号方法说明
1消息队列(Message Queues)任务间通过发送和接收带有数据的消息来通信,支持异步通信。
2管道(Pipes)一种半双工的数据传输方式,常用于进程间的通信。
3共享内存(Shared Memory)多个任务可以直接读写同一块内存区域,效率高但需要同步机制来避免冲突。
4总线(Buses)I2CSPIUART等硬件接口,用于设备间的数据传输。
5远程过程调用(RPC)允许程序调用网络中另一台计算机上的子程序,模拟本地调用。
6中断(Interrupts)硬件触发的事件,用于通知 CPU 处理紧急或外部事件,是一种快速的通信方式。

  同步和通信机制的选择取决于嵌入式系统的具体需求,包括实时性、资源限制、复杂度以及系统的可靠性要求。合理设计同步和通信策略,是保证嵌入式系统高效稳定运行的关键。

4 同步与通信

4.1 消息队列

  消息队列允许线程间安全地传递固定大小的消息,提供了异步通信的方式。

4.1.1 定义

  消息队列允许线程安全地发送和接收固定大小的数据块。队列维护发送和接收的顺序。一般在文件开头会看到这样的定义:osMessageQDef

  • 代码
// 定义一个名为app_test1_queue的消息队列,可存储128个uint32_t类型的元素。
osMessageQDef(app_test1_queue, 128, uint32_t);// 声明一个osMessageQId类型的变量app_test1_queue_id,用于保存消息队列的句柄。
// 在程序运行时,需要通过调用相关API初始化并获取有效的句柄值。
osMessageQId app_test1_queue_id = NULL;

  这段代码首先使用 osMessageQDef 宏定义了一个名为 app_test1_queue 的消息队列,它可以存储128个32位无符号整数。然后声明了一个变量 app_test1_queue_id ,用于存储消息队列的标识符(句柄),初始值设为 NULL 。在实际应用中,需要通过操作系统提供的API来初始化这个消息队列,并将返回的句柄赋值给app_test1_queue_id

/*** 定义一个消息队列。* * 这个宏用于静态定义一个消息队列,它会创建一个静态队列控制块和一个用于存储消息的数据缓冲区。* * @param name 消息队列的名称。* @param queue_sz 队列中能容纳的消息数量。* @param type 消息队列中每条消息的数据类型。*/
#define osMessageQDef(name, queue_sz, type) \
static StaticQueue_t os_mq_cb_##name; \
static uint32_t os_mq_data_##name[(queue_sz) * sizeof(type)]; \
const osMessageQDef_t os_messageQ_def_##name = \
{ (queue_sz), \{ NULL, 0U, (&os_mq_cb_##name), sizeof(StaticQueue_t), \(&os_mq_data_##name), sizeof(os_mq_data_##name) } }

  此宏定义了三个静态变量:一个静态队列控制块,一个消息数据数组,和一个用于OS的消息队列定义结构体。这个结构体包含了队列的大小、指针到静态队列控制块和消息数据数组的地址,以及这些数组的大小。这使得在系统运行时能够直接使用这个消息队列而无需动态分配内存。

  • 参数/函数讲解
序号参数/函数说明
1osMessageQId声明变量,用于存储消息队列的标识符(句柄),初始值设为NULL。
2osMessageQDef定义了静态变量:静态队列控制块,消息数据数组和用于OS的消息队列定义结构体
4.1.2 创建

  通过 osMessageQueueCreate() 函数创建消息队列,指定队列容量和消息大小。

  • 代码
/*** 初始化app_test1_queue消息队列。** 这个函数负责创建名为app_test1_queue的消息队列,并将成功创建的句柄保存到全局变量app_test1_queue_id。* 如果消息队列创建失败,它会记录错误信息并返回-1。** @return*   - 0: 消息队列创建成功。*   - -1: 创建消息队列失败。*/
static int32_t app_test1_queue_init(void)
{// 使用osMessageCreate函数创建消息队列,并将句柄保存到全局变量app_test1_queue_id = osMessageCreate(osMessageQ(app_test1_queue), NULL);// 检查创建是否成功,如果失败则打印错误信息并返回-1if (app_test1_queue_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_queue");return -1;}// 创建成功,返回0return 0;
}

  这段代码定义了一个名为 app_test1_queue_init 的静态函数,用于初始化之前定义的消息队列app_test1_queue 。它通过调用o sMessageCreate 函数创建消息队列,并检查返回的句柄是否有效。如果创建失败,它会记录错误信息并返回 -1 ;否则,返回 0 表示成功。

  • 参数/函数讲解
序号参数/函数说明
1osMessageCreate创建消息队列
4.1.3发送消息

  使用 osMessageQueueSend()osMessageQueuePut() 函数向队列发送消息。

  • 代码
/*** 尝试向app_test1_queue中发送消息。** 此函数检查消息队列是否有足够的空间接收至少6条消息。如果队列有足够空间,* 它将向队列中放入一个值为0xFF的消息,不设置优先级。** 注意:这个函数没有处理消息队列满的情况,因此在队列满时不会阻塞。*/
void app_test1_queue_put(void)
{// 检查消息队列是否有超过5个空闲槽位if (osMessageGetSpace(app_test1_queue_id) > 5) {// 向消息队列app_test1_queue_id中插入一个值为0xFF的消息,优先级设为0osMessagePut(app_test1_queue_id, 0xFF, 0);}
}

  这个函数 app_test1_queue_put 尝试将一个值为 0xFF 的消息放入名为 app_test1_queue 的消息队列中。首先,它检查队列是否有足够的空间容纳至少6个新消息。如果满足条件,就调用 osMessagePut 将消息放入队列,否则不做任何操作。注意,这个函数没有处理队列已满的情况,所以如果队列已满,消息将不会被发送,也不会阻塞调用线程。

  • 参数/函数讲解
序号参数/函数说明
1osMessageGetSpace检查消息队列的空闲槽位
2osMessagePut将消息放入队列

4.2 邮箱处理

  邮箱是用于线程间交换结构化数据的对象池。每个邮箱包含一组预先分配的内存块,线程可以申请、发送和接收这些内存块。

4.2.1 定义

  一般在文件开头会看到这样的定义:osMailQDef

  • 代码
osMailQDef (app_test1_mailbox, APP_TEST1_MAX_MAILBOX, APP_TEST1_MAIL); 
/*** app_test1_mailbox: 邮箱队列定义** 使用osMailQDef宏定义一个名为'app_test1_mailbox'的邮箱队列,最大邮件数为APP_TEST1_MAX_MAILBOX,* 邮件类型为APP_TEST1_MAIL。*/// 邮箱队列ID,用于后续操作
static osMailQId app_test1_mailbox_id = NULL;/*** app_test1_mail_alloc - 分配并初始化一个APP_TEST1_MAIL类型的邮件** @param mail 指向邮件指针的指针,用于存放新分配的邮件地址。** 返回值: 成功分配时返回0,失败则返回非0值。** 此函数为内部使用,负责从'app_test1_mailbox'邮箱队列中分配一个新的邮件,并将其地址存储在* 输入参数'mail'指向的变量中。具体实现省略。*/
static int app_test1_mail_alloc(APP_TEST1_MAIL** mail)
{// ...
}

  osMailQDef 定义了一个名为 app_test1_mailbox 的邮箱队列,用于存储 APP_TEST1_MAIL 类型的数据。APP_TEST1_MAX_MAILBOX 定义了邮箱队列可容纳的最大邮件数量。这个邮箱队列可以用于多线程或任务之间的数据通信,确保数据安全地传递。

/*** 定义一个邮箱队列。* * 该宏用于静态定义一个邮箱队列以及相关的OS邮箱队列结构体。它为指定的邮箱队列分配内存,* 并初始化OS邮箱队列结构体。* * @param name 邮箱队列的名称。* @param queue_sz 邮箱队列中邮件的最大数量。* @param type 邮件中元素的类型。*/
#define osMailQDef(name, queue_sz, type) \
static uint32_t os_mailQ_m_##name[3+((sizeof(type)+3)/4)*(queue_sz)]; \
osMailQDef_t os_mailQ_def_##name = \
{ {(queue_sz), sizeof(type), (os_mailQ_m_##name)}, NULL, {NULL} }

  在上述宏定义中:

  (1) 第一部分定义了一个静态数组 os_mailQ_m_##name,用于存储邮箱队列中的邮件。数组大小根据邮件类型 type 的大小和队列大小 queue_sz 动态计算得出。
  (2) 第二部分定义了一个 osMailQDef_t 类型的结构体 os_mailQ_def_##name,其中包含了邮箱队列的配置信息,如队列大小、邮件类型大小以及邮件存储区的指针。

  • 参数/函数讲解
序号参数/函数说明
1osMailQDef定义了的邮箱队列,用于存储 APP_TEST1_MAIL 类型的数据
2app_test1_mailbox_id是一个全局变量,用于存储邮箱队列的标识符,方便后续操作
3app_test1_mail_alloc用于从 app_test1_mailbox 中分配一个新的邮件,并将分配的邮件地址通过参数 mail 返回
4os_mailQ_m_##name用于存储邮箱队列中的邮件
5osMailQDef_t定义结构体,其中包含了邮箱队列的配置信息
4.2.2 创建

  通过 osMailQCreate() 函数创建邮箱,指定邮箱的大小和数据类型。

  • 代码
/*** app_test1_mailbox_init - 初始化app_test1_mailbox邮箱队列** @return: 成功初始化时返回0,失败则返回-1。** 此函数用于初始化之前定义的'app_test1_mailbox'邮箱队列。它调用osMailCreate函数创建邮箱队列,* 并将返回的邮箱ID存储在全局变量'app_test1_mailbox_id'中。如果创建失败,函数会输出错误信息* "Failed to Create app_test_thread1_mailbox",并返回-1表示初始化失败。*/
static int32_t app_test1_mailbox_init(void)
{app_test1_mailbox_id = osMailCreate(osMailQ(app_test1_mailbox), NULL);if (app_test1_mailbox_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_mailbox");return -1;}return 0;
}

  这个函数 app_test1_mailbox_init 负责初始化之前通过 osMailQDef 宏定义的 app_test1_mailbox 邮箱队列。如果初始化成功,它将返回0;如果失败(即无法创建邮箱队列),它会打印错误信息并返回-1。

  • 参数/函数讲解
序号参数/函数说明
1osMailCreate创建邮箱队列
4.2.3 发送/释放邮件

  使用 osMailQAlloc() 分配邮箱中的空间,然后用 osMailPut() 发送邮件。

  • 代码
/*** app_test1_mail_send - 发送一个APP_TEST1_MAIL类型的邮件到app_test1_mailbox** @param mail 需要发送的邮件对象指针。** 返回值: 成功发送时返回0,失败则返回非0值。** 此函数用于将一个APP_TEST1_MAIL类型的邮件对象发送到'app_test1_mailbox'邮箱队列中。* 具体实现省略,可能涉及到邮箱队列的同步原语以保证线程安全。*/
static int app_test1_mail_send(APP_TEST1_MAIL* mail)
{// ...
}/*** app_test1_mail_free - 释放app_test1_mailbox中的一个邮件对象** @param mail_p 已分配的邮件对象指针。** 返回值: 成功释放时返回0,失败则返回非0值。** 此函数用于释放'app_test1_mailbox'邮箱队列中已分配的一个邮件对象,以便于后续再使用。* 具体实现省略,可能涉及邮箱队列的同步原语以保证线程安全。*/
static int app_test1_mail_free(APP_TEST1_MAIL* mail_p)
{// ...
}
  • 参数/函数讲解
序号参数/函数说明
1app_test1_mail_send用于向 app_test1_mailbox 邮箱队列中发送邮件
2app_test1_mail_free用于向 app_test1_mailbox 邮箱队列中释放已分配的邮件
4.2.4 获取邮件

  线程通过 osMailGet() 函数获取邮件,可以选择等待或立即返回。

  • 代码
/*** @brief          获取应用测试1的邮件对象* * @description    该函数从内部数据结构中获取一个`APP_TEST1_MAIL`类型的邮件对象。*                 如果邮件可用,它将分配内存并填充邮件内容,然后将其指针返回。* * @param[out]     mail_p     指向接收`APP_TEST1_MAIL`结构体指针的指针。*                            如果成功获取邮件,此参数将被设置为有效邮件对象的指针。* * @return         成功获取邮件对象返回0,否则返回非0错误代码:*                 -1:邮件队列为空*                 -2:内存分配失败*                 其他值:可能表示其他错误情况** @note           实现应考虑线程安全,可能需要加锁来保护数据结构。*                 如果队列为空,可以选择阻塞等待,直到有新邮件到达。*/
static int app_test1_mail_get(APP_TEST1_MAIL** mail_p)
{// 实现获取邮件对象的逻辑,包括检查队列、分配内存、填充邮件内容等// ...if (/* 邮件队列为空或分配内存失败等错误条件 */) {return -1; // 或者 -2}return 0; // 成功获取邮件
}

  app_test1_mail_get 函数用于从 app_test1_mailbox 邮箱队列中取出一个邮件对象。当邮箱队列为空时,函数可能阻塞等待,直到有新的邮件可供消费。函数返回0表示成功获取邮件,非0值表示队列为空或出现错误。具体实现细节被省略,实际操作中可能需要考虑线程同步问题。

  • 参数/函数讲解
序号参数/函数说明
1app_test1_mail_get用于从邮箱队列中取出一个邮件对象

<<【系列文章索引】>>

请添加图片描述


总结

  感谢观看,这里就是 同步与通信篇 – 消息队列和邮箱处理,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹

在这里插入图片描述

  也欢迎你,关注我。👍 👍 👍

  原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!!💕 💕 💕 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。🎉

更多专栏订阅:

  • 😀 【LeetCode题解(持续更新中)】

  • 🥇 【恒玄BES】

  • 🌼 【鸿蒙系统】

  • 💎 【蓝牙协议栈】

  • 🎃 【死机分析】

  • 👑 【Python脚本笔记】

  • 🚝 【Java Web项目构建过程】

  • 💛 【微信小程序开发教程】

  • 【JavaScript随手笔记】

  • 🤩 【大数据学习笔记(华为云)】

  • 🦄 【程序错误解决方法(建议收藏)】

  • 🔐 【Git 学习笔记】

  • 🚀 【软件安装教程】



订阅更多,你们将会看到更多的优质内容!!

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

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

相关文章

【Node-RED 4.0.2】4.0版本新增特性(官方版)

二、重要功能 *1.时间戳格式改进 过去&#xff0c;node-red 只提供了 最原始的 timestamp 的格式&#xff08;1970-01-01 ~ now&#xff09; 但是现在&#xff0c;额外增加了 2 种格式&#xff1a; ISO 8601 -A COMMON FORMAT&#xff08;YYYY-MM-DDTHH:mm:ss:sssZ&#xff…

Linux环境安装配置nginx服务流程

Linux环境的Centos、麒麟、统信操作系统安装配置nginx服务流程操作&#xff1a; 1、官网下载 下载地址 或者通过命令下载 wget http://nginx.org/download/nginx-1.20.2.tar.gz 2、上传到指定的服务器并解压 tar -zxvf nginx-1.20.1.tar.gzcd nginx-1.20.1 3、编译并安装到…

阿里Nacos下载、安装(保姆篇)

文章目录 Nacos下载版本选择Nacos安装Windows常见问题解决 更多相关内容可查看 Nacos下载 Nacos官方下载地址&#xff1a;https://github.com/alibaba/nacos/releases 码云拉取&#xff08;如果国外较慢或者拉取超时可以试一下国内地址&#xff09; //国外 git clone https:…

[数据集][目标检测]桥梁检测数据集VOC+YOLO格式1116张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1116 标注数量(xml文件个数)&#xff1a;1116 标注数量(txt文件个数)&#xff1a;1116 标注…

【RabbitMQ实战】Springboot 整合RabbitMQ组件,多种编码示例,带你实践 看完这一篇就够了

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、对RabbitMQ管理界面深入了解1、在这个界面里面我们可以做些什么&#xff1f; 二、编码练习&#xff08;1&#xff09;使用direct exchange(直连型交换机)&a…

snowflake 不再是个数据仓库公司了

标题先上结论&#xff0c;为啥这么认为&#xff0c;且听接下来道来。 snowflake 非常成功&#xff0c;开创了云数仓先河&#xff0c;至今在数仓架构上也是相对比较先进的&#xff0c;国内一堆模仿的公司&#xff0c;传统上我们会认为 snowflake 肯定是一家数据仓库公司。不过最…

3D Gaussian Splatting代码中的forward和backward两个文件代码解读

3dgs代码前向传播部分 先来讨论一下glm&#xff0c;因为定义变量的时候用到了这个。 glm的解释 glm 是指 OpenGL Mathematics&#xff0c;这是一个针对图形编程的数学库。它的全称是 OpenGL Mathematics (GLM)&#xff0c;主要用于 OpenGL 的开发。这个库是基于 C 的模板库&…

什么是CC攻击,如何防止网站被CC攻击的方法

前言 “CC攻击的原理就是攻击者控制某些主机不停地发大量数据包给对方服务器造成服务器资源耗尽&#xff0c;一直到宕机崩溃。” 什么是CC攻击&#xff1f; CC攻击前身是一个名为Fatboy的攻击程序&#xff0c;而之所以后来人们会称之为CC&#xff0c;也叫HTTP-FLOOD&#xff…

【AI提升】如何使用大模型:本机离线和FastAPI服务调用

大模型本身提供的功能&#xff0c;类似于windows中的一个exe小工具&#xff0c;我们可以本机离线调用然后完成具体的功能&#xff0c;但是别的机器需要访问这个exe是不可行的。常见的做法就是用web容器封装起来&#xff0c;提供一个http接口&#xff0c;然后接口在后端调用这个…

lodash.js 工具库

lodash 是什么? Lodash是一个流行的JavaScript实用工具库,提供了许多高效、高兼容性的工具函数,能够方便地处理集合、字符串、数值、函数等多种数据类型,大大提高工作效率。 lodash官网 文档参见:Lodash Documentation lodash 在Vue中怎么使用? 1、首先安装 lodash np…

JDK动态代理-AOP编程

AOPTest.java&#xff0c;相当于main函数&#xff0c;经过代理工厂出来的Hello类对象就不一样了&#xff0c;这是Proxy.newProxyInstance返回的对象&#xff0c;会hello.addUser会替换为invoke函数&#xff0c;比如这里的hello.addUser("sun", "13434");会…

Python 作业题1 (猜数字)

题目 你要根据线索猜出一个三位数。游戏会根据你的猜测给出以下提示之一&#xff1a;如果你猜对一位数字但数字位置不对&#xff0c;则会提示“Pico”&#xff1b;如果你同时猜对了一位数字及其位置&#xff0c;则会提示“Fermi”&#xff1b;如果你猜测的数字及其位置都不对&…

无人机生态环境监测、图像处理与GIS数据分析综合实践技术应用

构建“天空地”一体化监测体系是新形势下生态、环境、水文、农业、林业、气象等资源环境领域的重大需求&#xff0c;无人机生态环境监测在一体化监测体系中扮演着极其重要的角色。通过无人机航空遥感技术可以实现对地表空间要素的立体观测&#xff0c;获取丰富多样的地理空间数…

QT+winodow 代码适配调试总结(二)

已经好多年了&#xff0c; linux环境下不同版本的QT程序开发和部署&#xff0c;突然需要适配window环境程序调试&#xff0c;一堆大坑&#xff0c;还真是一个艰巨的任务&#xff0c;可是kpi下的任务计划&#xff0c;开始吧&#xff01;&#xff01; 1、首先我们自定义的动态库…

vue3使用v-html实现文本关键词变色

首先看应用场景 这有一段文本内容&#xff0c;是项目的简介&#xff0c;想要实现将文本中的关键词进行变色处理 有如下关键词 实现思路 遍历文本内容&#xff0c;找到关键词&#xff0c;并使用某种方法更改其字体样式。经过搜寻资料决定采用v-html实现&#xff0c;但是v-h…

boost asio异步服务器(4)处理粘包

粘包的产生 当客户端发送多个数据包给服务器时&#xff0c;服务器底层的tcp接收缓冲区收到的数据为粘连在一起的。这种情况的产生通常是服务器端处理数据的速率不如客户端的发送速率的情况。比如&#xff1a;客户端1s内连续发送了两个hello world&#xff01;,服务器过了2s才接…

机械拆装-基于Unity-总体设计

目录 前言 1. 系统总体设计 2. 装配功能实现的详细设计 2.1 装配顺序 2.2 装配思想实现的难点 3. 场景实现中的难点与解决 3.1 相机控制 3.2 零件的拖拽和旋转 3.3 装配位置提示 总结 前言 在工业设计和制造领域&#xff0c;零部件的拆装技术是一个重要的应用场景&#xf…

MySQL:设计数据库与操作

设计数据库 1. 数据建模1.1 概念模型1.2 逻辑模型1.3 实体模型主键外键外键约束 2. 标准化2.1 第一范式2.2 链接表2.3 第二范式2.4 第三范式 3. 数据库模型修改3.1 模型的正向工程3.2 同步数据库模型3.3 模型的逆向工程3.4 实际应用建议 4. 数据库实体模型4.1 创建和删除数据库…

【C++进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫

二叉树1&#xff1a;深入理解数据结构第一弹——二叉树&#xff08;1&#xff09;——堆-CSDN博客 二叉树2&#xff1a;深入理解数据结构第三弹——二叉树&#xff08;3&#xff09;——二叉树的基本结构与操作-CSDN博客 二叉树3&#xff1a;深入理解数据结构第三弹——二叉树…

ubuntu22.04速装中文输入法

附送ubuntu安装chrome wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo dpkg -i google-chrome-stable_current_amd64.deb