目录
一、简介
线程间通信用与两个及两个以上的线程间通过全局变量进行功能间的通信,RTT提供了三种方式:
- 邮箱
- 消息队列
- 信号
下面将对这三种方式进行介绍。
二、邮箱
1、邮箱概念
邮箱用于线程间的通信,优点是开销比较低效率比较高;邮箱中的每一封邮件是4个字节,由于指针也是4个字节,所以经常使用指针来传递大量的信息,一个或多个线程可以从邮箱中获得这些邮件进行处理。
以非阻塞方式发送邮件可以安全地应用于中断服务程序中,是中断、服务线程、定时器等向线程发送消息地有效手段。当邮件收取阻塞时,只能由线程进行收取。
当发送邮件时若邮箱已满,则根据设定的等待时间挂起或返回;如果接收邮件是邮箱为空,则根据超时等待时间挂起或等到接收到新的邮件而唤醒。
2、邮箱控制块
struct rt_mailbox
{
struct rt_ipc_object parent;//继承object父类
rt_uint32_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;//邮箱句柄
3、邮箱相关的函数
rt_err_t rt_mb_init(rt_mailbox_t mb,//邮箱结构体
const char *name,//邮箱名称
void *msgpool,//内存池
rt_size_t size,//邮箱大小
rt_uint8_t flag);//静态初始化邮箱,flag可选RT_IPC_FLAG_FIFO(按进入邮箱的顺序排序)和RT_IPC_FLAG_PRIO(按优先级排队)
rt_err_t rt_mb_detach(rt_mailbox_t mb);//静态创建的邮箱脱离
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_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout);//接收邮件
rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg);//邮箱控制函数,可修改优先级等参数
三、消息队列
1、消息队列概念
消息队列能够接收不固定长度的消息,中断服务可以发送消息但是不能接收消息。但消息队列为空时可以挂起读取消息,采用先进先出的原则。
每个消息队列中包含多个消息框,每个消息框可以存放一条消息。第一个和最后一个消息框分别称为消息链表头和消息链表尾,空闲的消息框会组成一个空闲消息框列表。
2、消息队列控制块
struct rt_messagequeue
{
struct rt_ipc_object parent;//继承object父类
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;//空闲消息链表
};
typedef struct rt_messagequeue *rt_mq_t;//消息队列句柄
3、消息队列相关函数
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);//静态创建队列,flag与邮箱一样
rt_err_t rt_mq_detach(rt_mq_t mq);//静态创建的消息队列脱离
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_delete(rt_mq_t mq);//动态创建的消息队列删除
rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size);//向消息队列发送缓冲区指定大小的信息
rt_err_t rt_mq_urgent(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);//从消息队列读指定大小的信息到缓冲区,可以指定等待时间
rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg);//控制函数,可以修改优先级等参数
四、信号
1、信号概念
信号又称为软中断信号,在软件层次上是对中断机制的模拟,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。
在RT-Thread中,使用rt_sigset_t来定义一个信号集,rt_sigset_t是一个unsigned long类型 ,我们能够使用的两个信号为SIGUSR1和SIGUSR2。
一个线程可以安装一个信号并解除阻塞,同时设定好异常处理方式;然后其他线程可以给这个线程发送信号,触发这个线程对该信号的处理。
当信号传递给线程时,如果该线程处于挂起状态,则改为就绪状态去处理对应的信号;如果处于运行状态,则在当前线程栈的基础上简历新的栈空间去处理对应的信号。
2、信号相关函数
安装信号时的handle有三种处理方法:
- 类似中断,线程指定处理函数来处理信号
- 忽略这个信号,参数设为SIG_IGN
- 对该信号的处理保留系统默认值,使用默认的处理函数_signal_default_handler(),参数设为SIG_DFL
void rt_signal_mask(int signo);//阻塞模式,可以理解为屏蔽信号
void rt_signal_unmask(int signo);//解除阻塞,可以触发软中断
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler);//安装信号,只有SIGUSR1和SIGUSR2开放给用户使用
int rt_signal_wait(const rt_sigset_t *set,//指定等待的信号
rt_siginfo_t *si,//等待信号的类型,指向等到的信号信息的指针
rt_int32_t timeout);//等待信号的到来,没到则将线程挂起,知道等到这个信号或则超过timeout的等待时间
int rt_thread_kill(rt_thread_t tid, int sig);//向指定线程发送信号,tid表示线程,sig是信号
int rt_system_signal_init(void);//信号系统初始化