资料介绍
前 面 几 篇 文 章 介 绍 了 线 程( 任 务 )间 的 同 步 机 制 :信 号 量 、互 斥 量 、
事 件 集 。 接 下 来 我 们 学 习 线 程 ( 任 务 ) 之 间 的 通 信 机 制 。
一 般 来 说 ,RTOS 均 会 提 供 两 种 线 程 间 通 信 的 机 制 :消 息 邮 箱 和 消
息 队 列 。 RT-Thread 同 样 如 此 。
本 篇 文 章 介 绍 RT-Thread 消 息 邮 箱 相 关 的 内 容 。 邮 箱 的 工 作 机 制
1. 理 解 消 息 邮 箱
邮 箱 是 一 种 简 单 的 线 程 间 消 息 传 递 的 方 式 ,其 特 点 是 开 销 较 低 ,效
率 较 高 。 邮 箱 中 的 每 一 封 邮 件 可 以 容 纳 固 定 大 小 的 内 容 ( 针 对 32
位 处 理 器 , 可 容 纳 4 字 节 内 容 , 所 以 一 封 邮 件 恰 好 可 以 容 纳 一 个
指 针 ) 。
邮 箱 的 工 作 示 意 图 如 下 , 中 断 服 务 例 程 或 者 线 程 把 一 封 4 字 节 长
度 的 邮 件 发 送 到 邮 箱 中 ,一 个 或 多 个 线 程 可 以 从 邮 箱 中 读 取 这 些 邮
件 并 进 行 处 理 。
在 中 断 服 务 例 程 中 ,只 能 用 非 阻 塞 的 方 式 发 送 邮 件 。线 程 中 可 以 设
定 发 送 超 时 时 间 , 以 阻 塞 的 方 式 发 送 邮 件 。
当 一 个 线 程 向 邮 箱 发 送 邮 件 时 ,如 果 邮 箱 没 满 ,将 把 邮 件 复 制 到 邮
箱 中 。如 果 邮 箱 已 经 满 了 ,发 送 线 程 可 以 设 置 超 时 时 间 ,选 择 等 待
挂 起 或 直 接 返 回 -RT_EFULL 。
接 收 邮 件 过 程 中 , 当 邮 箱 中 不 存 在 邮 件 且 超 时 时 间 不 为 0 时 , 邮
件 收 取 过 程 将 变 成 阻 塞 方 式 。 此 时 , 只 能 由 线 程 进 行 邮 件 的 收 取 。
2. 邮 箱 控 制 块
RT-Thread 中 管 理 邮 箱 的 数 据 结 构 为 邮 箱 控 制 块 ,有 结 构 体 struc t
rt_mai lbox 表 示 。 另 外 , rt_mai lbox_t 表 示 的 是 邮 箱 的 句 柄 , 即 指 向
邮 箱 控 制 块 的 指 针 。 邮 箱 控 制 块 结 构 体 定 义 如 下 :
struct rt_mailbox
{
struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
rt_ubase_t *msg_pool; /* 邮箱缓冲区的开始地址 */
rt_uint16_t size; /* 邮箱缓冲区的大小 */
rt_uint16_t entry; /* 邮箱中邮件的数目 */
rt_uint16_t in_offset; /* 邮件进入邮箱的偏移指针 */
rt_uint16_t out_offset; /* 邮件出邮箱的偏移指针 */
rt_list_t suspend_sender_thread; /* 发送线程的挂起等待队列 */
};
typedef struct rt_mailbox *rt_mailbox_t;
rt_mai lbox 对 象 从 rt_ipc_object 中 派 生 , 由 IPC 容 器 管 理 。 结 构
体 rt_ipc_objec t 定 义 如 下 :
struct rt_object
{
char name[RT_NAME_MAX]; /* 内核对象名称 */
rt_uint8_t type; /* 内核对象类型 */
rt_uint8_t flag; /* 内核对象的参数 */
#ifdef RT_USING_MODULE
void *module_id; /* 应用程序模块 ID */
#endif
rt_list_t list; /* 内核对象管理链表 */
};
struct rt_ipc_object
{
struct rt_object parent; /* 继承自 rt_object */
rt_list_t suspend_thread; /* 挂起的线程链表 */
};
结 构 体 定 义 中 , 继 承 关 系 一 目 了 然 , 不 再 赘 述 。
管 理 邮 箱
RT-Thread 邮 箱 相 关 的 操 作 函 数 如 下 所 示 , 包 含 : 创 建 / 初 始 化
邮 箱 、 发 送 邮 件 、 接 收 邮 件 、 删 除 / 脱 离 邮
箱 。
本 文 只 重 点 介 绍 几 种 常 用 的 接 口 函 数 。
1. 创 建 邮 箱
RT-Thread 创 建 一 个 邮 箱 有 两 种 方 式 : 动 态 创 建 、 静 态 初 始 化 。
动 态 创 建 一 个 邮 箱 的 系 统 函 数 如 下 ,调 用 这 个 函 数 创 建 一 个 邮 箱 时 ,
内 核 会 先 从 对 象 管 理 器 中 分 配 一 个 邮 箱 对 象 ,然 后 创 建 一 个 邮 箱 控
制 块 ,接 着 对 邮 箱 控 制 块 进 行 初 始 化 ,包 括 邮 箱 缓 冲 区 地 址 、邮 件
数 目 、 发 送 邮 件 在 邮 箱 中 的 偏 移 等 。
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag)
在 对 邮 箱 控 制 块 初 始 化 期 间 ,内 核 会 动 态 分 配 一 块 内 存 空 间 用 来 存
放 消 息 邮 件 ,这 块 内 存 的 大 小 等 于 邮 件 大 小( 4 字 节 )与 邮 箱 容 量
的 乘 积 。
rt_mb_c reate()函 数 的 参 数 , name 为 邮 箱 名 称 ; si ze 表 示 邮 箱 容 量 ;
flag 为 邮 箱 的 标 志 , 取 值
为 RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO。
邮 箱 创 建 成 功 ,则 返 回 邮 箱 控 制 块 指 针 ;创 建 失 败 ,则 返 回 RT_NULL。
静 态 方 式 创 建 邮 箱 需 要 两 步 : ( 1) 定 义 一 个 邮 箱 控 制 块 和 一 段 存
放 邮 件 的 缓 冲 区 ( 2) 对 邮 箱 控 制 块 进 行 初 始 化 。
邮 箱 控 制 块 初 始 化 函 数 接 口 如 下 :
rt_err_t rt_mb_init(rt_mailbox_t mb,
const char* name,
void* msgpool,
rt_size_t size,
rt_uint8_t flag)
参 数 mb 为 邮 箱 控 制 块 的 指 针 ; name 为 邮 箱 名 称 ; msgpool 为 邮 箱
缓 冲 区 指 针 ;si ze 为 邮 箱 容 量 ;flag 为 邮 箱 标 志 ,与 rt_mb_c reate() 相
同 。
这 里 的 si ze 参 数 指 定 的 是 邮 箱 的 容 量 ,即 如 果 msgpool 指 向 的 缓 冲
区 的 字 节 数 是 N, 那 么 邮 箱 容 量
应 该 是 N/4。
函 数 rt_mb_init() 的 返 回 值 为 RT_EOK。
创 建 邮 箱 的 标 志 变 量 取 值 有 两 种 :
RT_IPC_FLAG_FIFO,等 待 邮 箱 的 线 程 按 照 先 进 先 出 的 方 式 进 行 排 列 。
RT_IPC_FLAG_PRIO, 等 待 邮 箱 的 线 程 按 照 优 先 级 的 方 式 进 行 排 列 。
2. 发 送 邮 件
RT-Thread 提 供 的 发 送 邮 件 接 口 函 数 有 两 种 : 一 种 是 无 等 待 超 时
接 口 , 一 种 是 有 等 待 超 时 。
线 程 或 者 中 断 服 务 程 序 可 以 通 过 邮 箱 给 其 他 线 程 发 送 消 息 ,发 送 邮
件 的 函 数 接 口 如 下 , 此 函 数 没 有 等 待 超 时 参 数 。
rt_err_t rt_mb_send (rt_mailbox_t mb, rt_ubase_t value)
参 数 mb 为 邮 箱 对 象 的 句 柄 ; value 为 邮 件 内 容 。
发 送 成 功 , 函 数 返 回 RT_EOK; 发 送 失 败 , 返 回 -RT_EFULL, 表 示
邮 箱 已 经 满 了 。
等 待 方 式 发 送 邮 件 的 函 数 接 口 如 下 , 这 个 函 数 有 等 待 超 时 参 数 :
rt_err_t rt_mb_send_wait (rt_mailbox_t mb,
rt_ubase_t value,
rt_int32_t timeout)
此 函 数 的 参 数 timeout 为 发 送 等 待 超 时 时 间 ,单 位 为 系 统 时 钟 节 拍 。
其 他 参 数 与 rt_mb_send() 相 同 。
如 果 邮 箱 已 经 满 了 , 发 送 线 程 会 根 据 设 定 的 timeout 参 数 等 待 邮 箱
中 因 为 收 取 邮 件 而 空 出 空 间 。 若 超 时 时 间 到 达 依 然 没 有 空 出 空 间 ,
则 发 送 线 程 将 会 被 唤 醒 并 返 回 错 误 码 。
返 回 RT_EOK 表 示 发 送 成 功 ; 返 回 -RT_ETIMEOUT 表 示 超 时 ; 返
回 -RT_ERROR 表 示 发 送 失 败 。
邮 件 的 内 容 可 以 是 32 位 任 意 格 式 的 数 据 ,一 个 整 型 值 或 一 个 指 向
某 个 缓 冲 区 的 指 针 。 可 以 根 据 自 己 的 实 际 应 用 进 行 设 定 。
注 意 :在 中 断 服 务 例 程 中 发 送 邮 件 时 ,应 该 采 用 无 等 待 延 时 的 方 式
发 送 , 直 接 使 用 rt_mb_send() 或 者 等 待 超 时 设 定 为 0 的 函 数
rt_mb_send_wait()。
3.接 收 邮 件
线 程 接 收 邮 件 的 函 数 接 口 如 下 ,线 程 接 收 邮 件 时 ,需 要 指 定 接 收 邮
件 的 邮 箱 句 柄 、 邮 件 存 放 位 置 以 及 等 待 的 超 时 时 间 。
rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
参 数 mb 为 邮 箱 的 句 柄 ;value 为 邮 箱 消 息 存 储 地 址 ;timeout 为 等 待
超 时 时 间 。
接 收 成 功 ,则 返 回 RT_EOK;接 收 超 时 ,则 返 回 -RT_ETIMEOUT;接
收 失 败 , 返 回 -RT_ERROR。
只 有 当 邮 箱 中 有 邮 件 时 ,接 收 者 才 能 立 即 取 到 邮 件 并 返 回 RT_EOK;
否 则 接 收 线 程 会 根 据 设 定 的 超 时 时 间 ,挂 起 在 等 待 线 程 队 列 或 者 立
即 返 回 ( 超 时 时 间 设 定 为 0) 。
实 战 演 练
举 例 来 说 明 邮 箱 操 作 函 数 的 用 法 , 代 码 如 下 。 动 态 创 建 两 个 线 程 ,
一 个 线 程 往 邮 箱 中 发 送 邮 件 , 一 个 线 程 从 邮 箱 中 收 取 邮 件 。
#include
#define THREAD_PRIORITY 8
#define THREAD_TIMESLICE 5
/* 邮 箱 控 制 块 */
rt_mailbox_t mb_handle;
static char mb_str1[] = "I'm a mail!";
static char mb_str2[] = "this is another mail!";
static char mb_str3[] = "over";
/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
char *str;
while (1)
{
rt_kprintf("thread1: try to recv a mail\n");
/* 从邮箱中收取邮件 */
if (rt_mb_recv(mb_handle, (rt_ubase_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
{
rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);
if (str == mb_str3)
{
break;
}
/* 延时 100ms */
rt_thread_mdelay(100);
}
}
}
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
rt_uint8_t count = 0;
while (count < 10)
{
count ++;
if (count & 0x1)
{
/* 发送 mb_str1 地址到邮箱中 */
rt_mb_send(mb_handle, (rt_uint32_t)&mb_str1);
}
else
{
/* 发送 mb_str2 地址到邮箱中 */
rt_mb_send(mb_handle, (rt_uint32_t)&mb_str2);
}
/* 延 时 200ms */
rt_thread_mdelay(200);
}
/* 发送邮件告诉线程 1, 线程 2 已经运行结束 */
rt_mb_send(mb_handle, (rt_uint32_t)&mb_str3);
}
int main()
{
/* 线程控制块指针 */
rt_thread_t thread1 = RT_NULL;
rt_thread_t thread2 = RT_NULL;
/* 创建一个邮箱 */
mb_handle = rt_mb_create("mt", 32, RT_IPC_FLAG_FIFO);
if (mb_handle == RT_NULL)
{
rt_kprintf("create mailbox failed.\n");
return -1;
}
/* 动态创建线程 1 */
thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL,
1024, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
if(thread1 != RT_NULL)
{
/* 启动线程 */
rt_thread_startup(thread1);
}
/* 动态创建线程 2 */
thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL,
1024, THREAD_PRIORITY, THREAD_TIMESLICE);
if(thread2 != RT_NULL)
{
/* 启动线程 */
rt_thread_startup(thread2);
}
}
编 译 , 运 行 结 果 如 下 :
该 例 程 中 , 线 程 2 发 送 邮 件 , 共 发 送 了 11 次 ; 线 程 1 接 收 邮
件 ,接 收 到 了 11 封 邮 件 ,并 将 邮 件 内 容 打 印 出 来 ,并 根 据 判 断 结
束 运 行 。 其 他 操 作 函 数
对 于 RT-Thread 邮 箱 操 作 来 说 , 还 有 删 除 邮 箱 的 函 数 没 有 介 绍 。
可 以 简 单 了 解 一 下 。
1. 删 除 动 态 创 建 的 邮 箱
部分文件列表
文件名 |
大小 |
RT-Thread快速入门-消息邮箱.pdf |
297K |
全部评论(0)