接着上一章讲,这一章讲线程间的通信,常见的有邮箱、信号、消息列队,所谓邮箱可以比喻成两个线程,A线程读取实时温度,然后将温度做为邮件传到邮箱,B线程从邮箱读取邮件信息,从而得到温度进行调整电机的的速度。
邮箱具有开销低,效率高的特点,每一封邮箱只能容纳固定4字节的内容(发送邮箱可来自定时器,中断)
邮箱的管理方式
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag);//创建邮箱
参数 | 描述 |
---|---|
name | 邮箱名称 |
size | 邮箱容量 |
flag | 邮箱标志,它可以取如下数值: RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO |
rt_err_t rt_mb_delete (rt_mailbox_t mb);//删除邮箱 mb对应邮箱句柄
rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value);//发送邮箱数据
t_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout);等待发送邮箱数据内容
rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);//接收邮箱信息
#include <rtthread.h>
#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);
消息列队
类似于linux驱动里面的异步通知
rt_mq_t rt_mq_create(const char* name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag);//消息列队的创建
rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);//发送消息
rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer, rt_size_t size, rt_int32_t timeout);//接收消息
#include <rtthread.h>
/* 消息队列控制块 */
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), 25, 5);
rt_thread_startup(&thread1);
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack), 25, 5);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(msgq_sample, msgq sample);
信号 这里的信号可以看成一种中断模式
收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类:
第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。
第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。
第三种方法是,对该信号的处理保留系统的默认值。
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler);//安装信号
void rt_signal_mask(int signo);//阻塞信号
void rt_signal_unmask(int signo);//解除阻塞信号
int rt_thread_kill(rt_thread_t tid, int sig);//发送信号
int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t[] *si, rt_int32_t timeout);//等待信号
#include <rtthread.h>
#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);