rt_thread线程间通讯
邮箱与消息队列:
邮箱:开销小,效率高(每次4字节,32位任意值或指向缓冲区的指针),相当于MQTT中的代理,函数调用过程中的传地址(指针,浅拷贝)。一(发)对多(收)关系;因为一个邮箱可以有多封邮件,所以接收者可以通过地址(指针==数组名)判断是哪个邮箱发过来的。使用邮箱的前提是保证邮箱接收完成前内容不能改变,或者用邮箱的等待发送(设定超时),否则就用消息队列。
分阻塞(邮箱不存在或超时参数不为0)和非阻塞(中断与线程间发送消息的有效手段)
发送满等待与接收空等待。
创建邮箱:分配内存大小=邮件数*4
注意:发送的内容是(rt_uint32_t)&数组名
接收的内容是(rt_uint32_t *)&指针
/* 邮箱控制块 */ 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); }
邮箱收发内容:
struct msg { rt_uint8_t *data_ptr; rt_uint32_t data_size; }; struct msg* msg_ptr; msg_ptr = (struct msg*)rt_malloc(sizeof(struct msg)); msg_ptr->data_ptr = ...; /* 指向相应的数据块地址 */ msg_ptr->data_size = len; /* 数据块的长度 */ /* 发送这个消息指针给 mb 邮箱 */ rt_mb_send(mb, (rt_uint32_t)msg_ptr); struct msg* msg_ptr; if (rt_mb_recv(mb, (rt_uint32_t*)&msg_ptr) == RT_EOK) { /* 在接收线程处理完毕后,需要释放相应的内存块 */ rt_free(msg_ptr); }
消息队列:是邮箱的扩展,用于线程间的消息交换、使用串口/中断服务接收不定长数据,是一种异步通讯。采用局部变量的深拷贝(不像邮箱还能知道源头是谁发的),使用消息队列要保证消费的速度比生成的快,不然会造成数据的堆叠
msg_queue_head 和 msg_queue_tail;/msg_queue_free
发送队列:插在尾部。
等待方式发送队列:如果满则按超时等待
发送紧急队列:rt_mq_urgent
插在队首
等待队列:
接收队列:
使用场合一:发送消息(做发送消息时,发送的内容如果4字节则退化成邮箱)
void send_op(void *data, rt_size_t length) { struct msg msg_ptr; msg_ptr.data_ptr = data; /* 指向相应的数据块地址 */ msg_ptr.data_size = length; /* 数据块的长度 */ /* 发送这个消息指针给 mq 消息队列 */ rt_mq_send(mq, (void*)&msg_ptr, sizeof(struct msg)); } /*消息队列是直接的数据内容复制,所以都采用了局部变量的方式保存消息结构体,这样也就免去动态内存分配的烦恼了*/ void message_handler() { struct msg msg_ptr; /* 用于放置消息的局部变量 */ /* 从消息队列中接收消息到 msg_ptr 中 */ if (rt_mq_recv(mq, (void*)&msg_ptr, sizeof(struct msg)) == RT_EOK) { /* 成功接收到消息,进行相应的数据处理 */ } }
使用场合2:同步消息:可采用队列+邮箱/信号量方式:
struct msg { rt_uint8_t *data_ptr; /* 数据块首地址 */ rt_uint32_t data_size; /* 数据块大小 */ /*当需要同步消息时以下二选一*/ //struct rt_mailbox ack; /*返回结果是个不同值的状态变量*/ //struct rt_semaphore ack; /*只反馈收到了的结果*/ };
信号
信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。线程之间可以互相通过调用 rt_thread_kill() 发送软中断信号。rt_sigset_t,应用程序能够使用的信号为 SIGUSR1(10)和 SIGUSR2(12)。
用途:用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理,信号的主体单位是某个线程。
对信号的处理:忽略SIG_IGN/默认系统处理SIG_DFL/回调函数
安装信号:该线程要处理什么信号,加入到来后如何处理,建立信号与回调之间的关系。有人说这个异步处理函数可以被阻塞(等待的信号满足时可以立即恢复执行?),
阻塞/屏蔽信号:相当于中断失能
解除阻塞:使能
发送信号:中断发送int rt_thread_kill(rt_thread_t tid, int sig);
//给某个线程哪个信号。
等待信号:没等到则阻塞。也可以不用
使用方法:线程1安装信号并接触信号,并正常进行;线程2发信号给线程1则会直接调用handler处理函数。