Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
学习笔记
【rtthread学习笔记系列】第三篇:线程间通信
发布于 2021-05-12 09:15:25 浏览:1299
订阅该版
[tocm] # 一、线程间通信 在裸机中使用全局变量进行功能间的通信,rtthread提供了三个工具用于线程间的通信。 * 邮箱: * 消息队列 * 信号 # 二、邮箱 ## 2.1 邮箱概念 邮箱中的每一份邮件内容为4字节,在32位系统中刚好为一个指针的大小。rtthread将邮箱抽象成rt_mailbox。 ``` struct rt_mailbox { struct rt_ipc_object parent; rt_uint32_t* msg_pool; /* 邮 箱 缓 冲 区 的 开 始 地 址 */ rt_uint16_t size; /* 邮 箱 缓 冲 区 的 大 小 */ rt_uint16_t entry; /* 邮 箱 中 邮 件 的 数 目 */ rt_uint16_t in_offset, out_offset; /* 邮 箱 缓 冲 的 进 出 指 针 */ rt_list_t suspend_sender_thread; /* 发 送 线 程 的 挂 起 等 待 队 列 */ }; typedef struct rt_mailbox* rt_mailbox_t; ``` ## 2.2 邮箱api 创建邮箱有动态和静态两种方式: rt_mb_create、rt_mb_init,与之对应的删除邮箱的方式为:rt_mb_delete和rt_mb_detach。在创建邮箱之后,可以使用发送和接收api:rt_mb_send和rt_mb_recv发送和接收邮件,rt_mb_send在邮箱为满时会返回 -RT_EFULL,增强版的rt_mb_send_wait在邮箱满时会挂起等待。 ``` //创建一个邮箱 /* name:邮箱名称 size:邮箱容量 flag:标志 返回:邮箱句柄 */ rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag); //删除邮箱:当在删除时有线程挂起在这个邮箱时,会唤醒这些线程,线程返回-RT_ERROR. /* mb:邮箱句柄 */ rt_err_t rt_mb_delete (rt_mailbox_t mb); //初始化邮箱 /* mb:邮箱句柄 name:邮箱名称 msgpool:缓冲区 size:邮箱容量 flag:标志:可取RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO */ rt_err_t rt_mb_init(rt_mailbox_t mb, const char* name, void* msgpool, rt_size_t size, rt_uint8_t flag) //邮箱脱离 /* mb:邮箱句柄 */ rt_err_t rt_mb_detach(rt_mailbox_t mb); //发送邮件 /* mb:邮箱句柄 value:发送的内容:32位任意值或者指针 */ rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value); //等待方式发送邮件 /* mb:邮箱句柄 value:发送内容 timeout:超时时间 */ rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout); //接收邮件 /* mb:邮箱句柄 value:接收数据缓冲区 timeout:超时时间 */ rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout); ``` ## 2.3 邮箱示例 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:邮箱例程 * * 这个程序会创建2个动态线程,一个静态的邮箱对象,其中一个线程往邮箱中发送邮件, * 一个线程往邮箱中收取邮件。 */ #include
#define THREAD_PRIORITY 10 #define THREAD_TIMESLICE 5 /* 邮箱控制块 */ static struct rt_mailbox mb; /* 用于放邮件的内存池 */ static char mb_pool[128]; static char mb_str1[] = "I'm a mail!"; static char mb_str2[] = "this is another mail!"; static char mb_str3[] = "over"; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; /* 线程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, (rt_uint32_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); } } /* 执行邮箱对象脱离 */ rt_mb_detach(&mb); } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; /* 线程2入口 */ static void thread2_entry(void *parameter) { rt_uint8_t count; count = 0; while (count < 10) { count ++; if (count & 0x1) { /* 发送mb_str1地址到邮箱中 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str1); } else { /* 发送mb_str2地址到邮箱中 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str2); } /* 延时200ms */ rt_thread_mdelay(200); } /* 发送邮件告诉线程1,线程2已经运行结束 */ rt_mb_send(&mb, (rt_uint32_t)&mb_str3); } int mailbox_sample(void) { rt_err_t result; /* 初始化一个mailbox */ result = rt_mb_init(&mb, "mbt", /* 名称是mbt */ &mb_pool[0], /* 邮箱用到的内存池是mb_pool */ sizeof(mb_pool) / 4, /* 邮箱中的邮件数目,因为一封邮件占4字节 */ RT_IPC_FLAG_FIFO); /* 采用FIFO方式进行线程等待 */ if (result != RT_EOK) { rt_kprintf("init mailbox failed.\n"); return -1; } rt_thread_init(&thread1, "thread1", thread1_entry, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", thread2_entry, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(mailbox_sample, mailbox sample); ``` # 三 消息队列 ## 3.1 消息队列概念 消息队列用于发送不固定长度的消息,线程获得的消息是最先进入消息队列的消息。rtthread将消息队列抽象成rt_messagequeue。 ``` struct rt_messagequeue { struct rt_ipc_object parent; void* msg_pool; /* 指 向 存 放 消 息 的 缓 冲 区 的 指 针 */ rt_uint16_t msg_size; /* 每 个 消 息 的 长 度 */ rt_uint16_t max_msgs; /* 最 大 能 够 容 纳 的 消 息 数 */ rt_uint16_t entry; /* 队 列 中 已 有 的 消 息 数 */ void* msg_queue_head; /* 消 息 链 表 头 */ void* msg_queue_tail; /* 消 息 链 表 尾 */ void* msg_queue_free; /* 空 闲 消 息 链 表 */ rt_list_t suspend_sender_thread; /* 发 送 线 程 的 挂 起 等 待 队 列 */ }; typedef struct rt_messagequeue* rt_mq_t; ``` ## 3.2 消息队列api ``` //创建消息队列 /* name:消息队列的名称 msg_size:消息的最大长度,单位:字节 max_msgs:最大消息个数 flag:标志:可取RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO 返回:消息队列句柄 */ rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag); //删除消息队列 /* mq:消息队列句柄 */ rt_err_t rt_mq_delete(rt_mq_t mq); //初始化消息队列 /* name:消息队列名称 msgpool:缓冲区 msg_size:最大消息大小 pool_size:缓冲区大小 flag:标志 */ rt_err_t rt_mq_init(rt_mq_t mq, const char* name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag); //消息队列脱离 /* mq:消息队列句柄 */ rt_err_t rt_mq_detach(rt_mq_t mq); //发送消息 /* mq:消息队列句柄 buffer:发送缓冲区 size:发送消息的大小 */ rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size); //等待方式发送消息 /* mq:消息队列句柄 buffer:发送缓冲区 size:发送消息大小 timeout:等待超时时间 */ rt_err_t rt_mq_send_wait(rt_mq_t mq, const void *buffer, rt_size_t size, rt_int32_t timeout); //发送紧急消息,消息放在队首,接收线程将第一个接收到这条消息 /* mq:消息队列句柄 buffer:发送缓冲区 size:发送消息大小 */ rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size); //接收消息 /* mq:消息队列句柄 buffer:接收缓冲区 size:接收消息大小 timeout:接收超时时间 */ rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer, rt_size_t size, rt_int32_t timeout); ``` ## 3.3 消息队列示例 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:消息队列例程 * * 这个程序会创建2个动态线程,一个线程会从消息队列中收取消息;一个线程会定时给消 * 息队列发送 普通消息和紧急消息。 */ #include
#define THREAD_PRIORITY 25 #define THREAD_TIMESLICE 5 /* 消息队列控制块 */ static struct rt_messagequeue mq; /* 消息队列中用到的放置消息的内存池 */ static rt_uint8_t msg_pool[2048]; ALIGN(RT_ALIGN_SIZE) static char thread1_stack[1024]; static struct rt_thread thread1; /* 线程1入口函数 */ static void thread1_entry(void *parameter) { char buf = 0; rt_uint8_t cnt = 0; while (1) { /* 从消息队列中接收消息 */ if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) == RT_EOK) { rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf); if (cnt == 19) { break; } } /* 延时50ms */ cnt++; rt_thread_mdelay(50); } rt_kprintf("thread1: detach mq \n"); rt_mq_detach(&mq); } ALIGN(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; /* 线程2入口 */ static void thread2_entry(void *parameter) { int result; char buf = 'A'; rt_uint8_t cnt = 0; while (1) { if (cnt == 8) { /* 发送紧急消息到消息队列中 */ result = rt_mq_urgent(&mq, &buf, 1); if (result != RT_EOK) { rt_kprintf("rt_mq_urgent ERR\n"); } else { rt_kprintf("thread2: send urgent message - %c\n", buf); } } else if (cnt >= 20)/* 发送20次消息之后退出 */ { rt_kprintf("message queue stop send, thread2 quit\n"); break; } else { /* 发送消息到消息队列中 */ result = rt_mq_send(&mq, &buf, 1); if (result != RT_EOK) { rt_kprintf("rt_mq_send ERR\n"); } rt_kprintf("thread2: send message - %c\n", buf); } buf++; cnt++; /* 延时5ms */ rt_thread_mdelay(5); } } /* 消息队列示例的初始化 */ int msgq_sample(void) { rt_err_t result; /* 初始化消息队列 */ result = rt_mq_init(&mq, "mqt", &msg_pool[0], /* 内存池指向msg_pool */ 1, /* 每个消息的大小是 1 字节 */ sizeof(msg_pool), /* 内存池的大小是msg_pool的大小 */ RT_IPC_FLAG_FIFO); /* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init message queue failed.\n"); return -1; } rt_thread_init(&thread1, "thread1", thread1_entry, RT_NULL, &thread1_stack[0], sizeof(thread1_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread1); rt_thread_init(&thread2, "thread2", thread2_entry, RT_NULL, &thread2_stack[0], sizeof(thread2_stack), THREAD_PRIORITY, THREAD_TIMESLICE); rt_thread_startup(&thread2); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(msgq_sample, msgq sample); ``` # 四 信号 ## 4.1 信号概念 信号本质是软中断, 用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。 ## 4.2 信号api ``` //安装信号 /* signo:信号值,可取SIGUSR1,SIGUSR2 handle:信号处理方式:1类似于中断的操作、 SIG_IGN:忽略某个信号; SIG_DFL:默认的信号处理方式 */ rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler); //信号阻塞,信号传递不到安装该信号的线程。 /* signo:信号值 */ void rt_signal_mask(int signo); //接触信号阻塞 void rt_signal_unmask(int signo); //发送信号 /* tid:线程句柄 sig:信号值 */ int rt_thread_kill(rt_thread_t tid, int sig); //等待信号 /* set:信号 si:存储信号的指针 timeout:超时时间 */ int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t[] *si, rt_int32_t timeout); ``` ## 4.3 信号示例 本示例采用软中断的方式处理信号,示例先创建的线程1安装了SIGUSR1信号,当示例发送信号时,线程1执行软中断程序。 ``` /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-24 yangjie the first version */ /* * 程序清单:信号例程 * * 这个例子会创建一个线程,线程安装信号,然后给这个线程发送信号。 * */ #include
#define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 5 static rt_thread_t tid1 = RT_NULL; /* 线程1的信号处理函数 */ void thread1_signal_handler(int sig) { rt_kprintf("thread1 received signal %d\n", sig); } /* 线程1的入口函数 */ static void thread1_entry(void *parameter) { int cnt = 0; /* 安装信号 */ rt_signal_install(SIGUSR1, thread1_signal_handler); rt_signal_unmask(SIGUSR1); /* 运行10次 */ while (cnt < 10) { /* 线程1采用低优先级运行,一直打印计数值 */ rt_kprintf("thread1 count : %d\n", cnt); cnt++; rt_thread_mdelay(100); } } /* 信号示例的初始化 */ int signal_sample(void) { /* 创建线程1 */ tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); rt_thread_mdelay(300); /* 发送信号 SIGUSR1 给线程1 */ rt_thread_kill(tid1, SIGUSR1); return 0; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(signal_sample, signal sample); ```
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
happycode999
这家伙很懒,什么也没写!
文章
28
回答
6
被采纳
0
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
SFUD
msh
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1443
个答案
289
次被采纳
张世争
805
个答案
174
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部