数据传递
邮箱
特性:
每封邮件固定为 4 字节,
可以直接传输 32 位无符号数
恰好能容纳一个指针,可传递指向缓冲区的地址。但是不能显式的指定数据的长度,在使用的时候一定要注意越界的问题。
多个线程可等待同一个邮箱。
邮箱控制块:
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;
动态创建与删除
rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag)rt_err_t rt_mb_delete (rt_mailbox_t mb)静态初始化与脱离
发送邮件
无等待方式发送:
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);接收邮件
rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout);若传递的是地址,需要把这个数字强制转换为指针:
rt_uint32_t received_data;
rt_mb_recv(&mb, &received_data, RT_WAITING_FOREVER);
数据类型 *received_ptr = (数据类型 *)received_data;消息队列
适合传输大块的不固定长度的数据。
指针传递:
要保证指针越界的问题
要保证指向的内存的生命周期
特性:
FIFO 先进先出队列
长度有限制,单个数据大小有限制
可以把紧急的数据写到消息队列的头部
动态创建与删除
rt_mq_t rt_mq_create ( const char* name, // 队列名称
rt_size_t msg_size, // 消息最大大小
rt_size_t max_msgs, // 队列长度
rt_uint8_t flag ); // 唤醒标志flags:
rt_err_t rt_mq_delete(rt_mq_t mq)删除消息队列时,如果有线程在等待该队列,则内核会先唤醒这些线程(线程返回值 是 - RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象。
静态初始化与脱离
发送消息
无等待发送:
rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size);等待发送:
rt_err_t rt_mq_send_wait(rt_mq_t mq,
const void *buffer,
rt_size_t size,
rt_int32_t timeout);发送紧急消息:
向队列头插入消息。
rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size);接收消息
rt_ssize_t rt_mq_recv (rt_mq_t mq, void* buffer,
rt_size_t size, rt_int32_t timeout);线程同步
信号量
简单的通知机制。
分为二进制信号量和计数信号量。
信号量控制块:
struct rt_semaphore
{
struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
rt_uint16_t value; /* 信号量的值 */
};
/* rt_sem_t 是指向 semaphore 结构体的指针类型 */
typedef struct rt_semaphore* rt_sem_t;
动态创建与删除
rt_sem_t rt_sem_create(const char *name,
rt_uint32_t value,
rt_uint8_t flag);flag:
rt_err_t rt_sem_delete(rt_sem_t sem);
静态初始化与脱离
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
rt_err_t rt_sem_detach(rt_sem_t sem);
P 操作
等待获取信号量:
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);time:
非阻塞方式获取信号量:
rt_err_t rt_sem_trytake(rt_sem_t sem);V 操作
rt_err_t rt_sem_release(rt_sem_t sem);
释放信号量可以唤醒等待该信号量的线程。
事件集
通过设置标志位传递线程状态。
即 FreeRTOS 中的事件组。
特性:
一个事件使用 1 bit 表示
一对多,多对一、多对多的通信
可选择是否清除事件
若发送的事件,接收方还未
线程控制块中存储线程的事件数据。
struct rt_thread
{
......
##if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
##endif
......
}event_set:想等待哪些事件。事件使用(1<<30) | (1<<0) 表示事件 0 和 30
event_info:事件等待条件。
RT_EVENT_FLAG_AND:全部事件发生
RT_EVENT_FLAG_OR:任一事件发生
RT_EVENT_FLAG_CLEAR:事件到达时,是否清除事件
动态创建和删除
rt_event_t rt_event_create(const char* name, rt_uint8_t flag);flag:唤醒策略,RT_IPC_FLAG_FIFO 先进先出 或 RT_IPC_FLAG_PRIO 高优先级
rt_err_t rt_event_delete(rt_event_t event);静态初始化与脱离
rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag);rt_err_t rt_event_detach(rt_event_t event);发送事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);接收事件
rt_err_t rt_event_recv ( rt_event_t event,
rt_uint32_t set,
rt_uint8_t option,
rt_int32_t timeout,
rt_uint32_t* recved);set:期待哪些事件:一般使用宏定义定义事件,使用 | 多个事件
option:接收选项 :RT_EVENT_FLAG_OR 或 RT_EVENT_FLAG_AND 选择清除重置事件标志位:RT_EVENT_FLAG_CLEAR
timeout:指定超时时间
recved:指向接收到的事件
临界资源保护
互斥量
和 FreeRTOS 的互斥量一样:
只能由持有的线程释放
解决了优先级反转问题
支持递归使用
动态创建和删除
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);
rt_err_t rt_mutex_delete (rt_mutex_t mutex);
静态初始化和脱离
rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag);
rt_err_t rt_mutex_detach (rt_mutex_t mutex);
P 操作
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);无等待方式获取信号量:
rt_err_t rt_mutex_trytake(rt_mutex_t mutex);V 操作
rt_err_t rt_mutex_release(rt_mutex_t mutex);信号通知
效率比较高。
应用程序能够使用的信号为 SIGUSR1(10)和 SIGUSR2(12)
信号的使用场景?
信号到达时,如果不是在运行态会怎么处理?

安装信号
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t[] handler);
将信号和线程绑定,这样线程才能接收信号并处理。
signo:信号
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;
void thread1_signal_handler(int sig)
{
rt_kprintf("thread1 received signal %d\n", sig);
}
static void thread1_entry(void *parameter)
{
int cnt = 0;
/* 安装信号 */
rt_signal_install(SIGUSR1, thread1_signal_handler);
rt_signal_unmask(SIGUSR1);
while (cnt < 10)
{
rt_kprintf("thread1 count : %d\n", cnt);
cnt++;
rt_thread_mdelay(100);
}
}
int signal_sample(void)
{
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_CMD_EXPORT(signal_sample, signal sample);