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

RT_Thread——邮箱

文章目录

  • 一、邮箱的特性
  • 二、邮箱操作函数
    • 2.1 创建/初始化
    • 2.2 删除/脱离
    • 2.3 发邮件
    • 2.4 收邮件
  • 三、邮箱的基本使用

消息队列的本质是链表:

  • 空闲消息块链表:往队列里写入消息时,先从空闲链表中得到消息块;从队列读出消息后,把消息块放入空闲链表
  • 消息块头部链表:消息写入消息块后,该消息块被放到尾部;从队列里读消息时,从头部读。

使用消息队列可以传递各类大小的消息,它使用 memcpy 的方式写入消息、读出消息。如果我们只是传递很小的数据,比如一些数值,可以使用邮箱:它的效率更高。

本章涉及如下内容:

  • 怎么创建、删除邮箱
  • 邮箱中数据如何保存
  • 怎么向邮箱写入数据、怎么从邮箱读取数据
  • 在邮箱上阻塞是什么意思

一、邮箱的特性

邮箱的本质是环形缓冲区:

在这里插入图片描述

  • 邮箱中的每一封邮件,只能容纳 4 字节内容(对于 32 位系统,指针大小刚好为 4 字节);

  • 邮件的发送通常是非阻塞的,线程、中断都可以发送邮件;也可使用阻塞方式发送;

  • 邮件的接收通常是阻塞的,取决于邮箱中是否有邮件

  • 当一个线程向邮箱发送邮件时:

    • 如果邮箱没满,就把数值写入邮箱中
    • 如果邮箱满了
      • 发送线程可以直接返回 -RT_EFULL
      • 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序读了邮箱,会唤醒挂起的线程
  • 当一个线程从邮箱接收邮件时:

    • 如果邮箱不为空,就读取邮箱中的数值
    • 如果邮箱为空
      • 接收线可以直接返回 -RT_ETIMOUT
      • 也可以挂起一段时间,在挂起的期间,别的线程或中断服务程序写了邮箱,会唤醒挂起的线程

二、邮箱操作函数

邮箱由邮箱控制块管理,由结构体 rt_mailbox 表示。

使用邮箱的流程:创建/初始化邮箱、发送邮件、接收邮件、删除/脱离邮箱。

2.1 创建/初始化

邮箱的创建有两种方法:

  • 动态分配内存:rt_mb_create() ,邮箱的内存在函数内部动态分配,分配的内存大小为邮件大小乘以邮箱容量
  • 静态分配内存:rt_mb_init(),邮箱的内存事先分配好,比如可以是数组

rt_mb_create()函数原型如下:

rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);

在这里插入图片描述

rt_mb_init()函数原型如下:

rt_err_t rt_mb_init(rt_mailbox_t mb, const char* name,void* msgpool, rt_size_t size, rt_uint8_t flag)

在这里插入图片描述

2.2 删除/脱离

不再使用一个邮箱时:

  • 删除它:rt_mb_delete(),只能删除使用 rt_mb_create()创建的邮箱
  • 脱离它:rt_mb_detach(),只能脱离使用 rt_mb_init()初始化的邮箱

删除邮箱的函数为 rt_mb_delete(),它会释放内存。原型如下:

rt_err_t rt_mb_delete (rt_mailbox_t mb);

删除邮箱时,如果有线程在等待该邮箱,则内核先唤醒这些线程(线程返回值是 -RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象

脱离邮箱将使邮箱对象被从内核对象管理器中脱离。原型如下:

rt_err_t rt_mb_detach(rt_mailbox_t mb);

脱离消息邮箱时,如果有线程在等待该邮箱,则内核会先唤醒这些线程(线程返回值是 - RT_ERROR)。

2.3 发邮件

RT-Thread 有三个发送邮件的函数:

  • rt_mb_send() 发送邮件
  • rt_mb_send_wait() 等待方式发送邮件
  • rt_mb_urgent() 发送紧急邮件

线程或者中断服务程序都可以通过往邮箱里写入邮件。

发送的邮件,可以是 32 位的任意格式数据,可以是一个整型值或者一个指向某块内存的指针。

使用 rt_mq_send()发送消息时,只有在邮箱有可用的空闲空间时,才能成功发送消息,否则返回错误码(-RT_EFULL)。

使用 rt_mq_send_wait()发送消息时,如果邮箱没有可用的空闲空间会根据 timeout参数等待,超时后才返回错误

使用 rt_mq_urgent()发送消息时,只有在邮箱有可用的空闲空间,它才会把邮件插在邮件队首以便这个邮件能被第一时间读取

发送邮件的函数原型如下:

rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);

参数说明如下:

在这里插入图片描述

等待方式发送邮件的函数原型如下:

rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value,rt_int32_t timeout);

参数说明如下:

在这里插入图片描述

发送紧急邮件的函数原型如下:

rt_err_t rt_mb_urgent (rt_mailbox_t mb, rt_ubase_t value);

参数说明如下:
在这里插入图片描述

2.4 收邮件

当邮箱有邮件时,使用收邮件函数,可以从邮箱接收邮件。

如果没有邮件,根据指定的 timeout 参数等待,直到超时结束。

函数原型如下:

rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);

参数说明如下:

在这里插入图片描述

三、邮箱的基本使用

本节代码为:RT-Thread_10_queue_mailbox。

程序开头一些定义:

#include <string.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>#define THREAD_PRIORITY         15  //设置线程优先级
#define THREAD_STACK_SIZE       512 //设置线程栈大小
#define THREAD_TIMESLICE        15  //设置线程时间片大小static struct rt_thread *thread1;   //定义线程1句柄指针
static struct rt_thread *thread2;   //定义线程2句柄指针static struct rt_mailbox mb;        //邮箱控制块
static char mb_pool[128];           //放置邮件的内存池

main 函数中创建了邮箱、创建了发送线程、接收线程:代码如下:

int main(void)
{rt_err_t result;/* 初始化邮箱 */result = rt_mb_init(&mb,                       //邮箱对象的句柄"mbt",                     //邮箱的名字&mb_pool[0],               //内存池指向mb_poolsizeof(mb_pool) / 4,       //邮箱中能容纳的邮件数量,每封邮件占四字节  RT_IPC_FLAG_FIFO);         //如果有多个线程等待,按照先来先得到的方法分配邮件 if (result != RT_EOK){rt_kprintf("rt_mb_init ERR\n");return -1;}/* 创建动态线程thread1,优先级为 THREAD_PRIORIT-1 = 14 */thread1 = rt_thread_create("thread1",          //线程名字thread1_entry,         //入口函数RT_NULL,               //入口函数参数THREAD_STACK_SIZE,     //栈大小THREAD_PRIORITY - 1,   //线程优先级THREAD_TIMESLICE);     //线程时间片大小/* 判断创建结果,再启动线程1 */if (thread1 != RT_NULL)rt_thread_startup(thread1);				   /* 创建动态线程thread2,优先级为 THREAD_PRIORIT = 15 */thread2 = rt_thread_create("thread2",          //线程名字thread2_entry,         //入口函数RT_NULL,               //入口函数参数THREAD_STACK_SIZE,     //栈大小THREAD_PRIORITY,       //线程优先级THREAD_TIMESLICE);     //线程时间片大小/* 判断创建结果,再启动线程2 */if (thread2 != RT_NULL)rt_thread_startup(thread2);		return 0;
}

发送线程的函数中,不断往邮箱发送邮件,代码如下:

/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{rt_err_t result;int count = 0;/* 线程1 */while(1){/* 发送邮箱 */count++;result = rt_mb_send(&mb, (rt_ubase_t)count);if (result != RT_EOK){rt_kprintf("rt_mb_send ERR\n");}rt_kprintf("rt_mb_send:%d\n\r", count);	rt_thread_mdelay(10);}
}

接收线程的函数中,读邮件、打印,代码如下:

/* 线程2的入口函数 */
static void thread2_entry(void *parameter)
{int val;/* 线程2 */while(1){/* 接收消息 */if (rt_mb_recv(&mb, (rt_ubase_t *)&val, RT_WAITING_FOREVER) == RT_EOK){rt_kprintf("rt_mb_recv:%d\n\r", val);}rt_thread_mdelay(5);}
}

运行结果如下图所示:

在这里插入图片描述

http://www.xdnf.cn/news/13415.html

相关文章:

  • Monorepo + PNPM 搭建高效多项目管理
  • 基于流形迁移学习的快速动态多目标进化算法(MMTL-MOEA/D)求解FDA1-FDA5和dMOP1-dMOP3,提供完整MATLAB代码
  • ubuntu桌面x11异常修复
  • 《安富莱嵌入式周报》第354期: 开源36通道16bit同步数据采集卡,开源PoE以太网GPIB,分体式键盘DIY,微软WSL开源,USB转车载以太网
  • 第5章 类的基本概念 笔记
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | EventKey Codes(键盘码)
  • Vim 调用外部命令学习笔记
  • 主键(PRIMARY KEY)与唯一键(UNIQUE KEY)的区别详解
  • 代码随想录算法训练营第60期第六十四天打卡
  • Maven 多仓库配置及缓存清理实战分享
  • 反向工程与模型迁移:打造未来商品详情API的可持续创新体系
  • 工厂方法模式(Factory Method Pattern)
  • 一个完整的日志收集方案:Elasticsearch + Logstash + Kibana+Filebeat (三)
  • 通义灵码 AI IDE 上线!智能体+MCP 从手动调用工具过渡到“AI 主动调度资源”
  • 欧盟手机和平板电脑生态设计和能源标签法案解析
  • 零基础玩转物联网-串口转以太网模块如何快速实现与MQTT服务器通信
  • 【Elasticsearch】Elasticsearch 近实时高速查询原理
  • 0610_特性和反射_加密和解密_单例模式
  • Python爬虫基础之Selenium详解
  • 如何排查 Docker 容器资源占用过高的问题?
  • RabbitMq详解
  • 期权卖方是谁?
  • CVE-2024-23897源码分析与漏洞复现(Jenkins 任意文件读取)
  • Mybatisplus3.5.6,用String处理数据库列为JSONB字段
  • 配置Linux的网络为静态IP地址的一些方法
  • http协议同时传输文本和数据的新理解
  • 可编辑前端列表页面,让你的用户直接粘贴录入数据
  • pdf.js在iOS移动端分页加载优化方案(ios移动端反复刷新加载问题)
  • dedecms 织梦自定义表单留言增加ajax验证码功能
  • 传统影像的盲区:心血管疾病诊断的新突破与未来