2.3 任务间通信
2.3.1 互斥量
概述
互斥量又称互斥锁,一般用于共享资源的互斥排他性访问保护。
互斥量在任意时刻处于且仅会处于解锁或锁定状态,当一个任务获取到一把锁后(互斥量锁定),其他任务再尝试获得这把锁时会失败或进入阻塞状态,当该任务释放持有的锁时(互斥量解锁),会唤醒一个正阻塞等待此互斥量的任务,被唤醒的任务将会获取这把锁。
在多任务运行环境中,有些共享资源不具有多线程可重入性,对于这类不希望被多任务同时访问的资源(临界资源),可以采用互斥量来进行保护,后面的编程实例章节会演示这一编程范式。
API讲解
编程实例
1、在tos_config.h中,配置互斥量组件开关TOS_CFG_MUTEX_EN:
#define TOS_CFG_MUTEX_EN 1u
2、编写main.c示例代码:
- #include "tos.h"
- #include "mcu_init.h"
- #define STK_SIZE_TASK_WRITER 512
- #define STK_SIZE_TASK_READER 512
- k_stack_t stack_task_writer[STK_SIZE_TASK_WRITER];
- k_stack_t stack_task_reader[STK_SIZE_TASK_READER];
- k_task_t task_writer;
- k_task_t task_reader;
- extern void entry_task_writer(void *arg);
- extern void entry_task_reader(void *arg);
- k_mutex_t critical_resource_locker;
- // 一片临界区内存
- static uint32_t critical_resource[3];
- static void write_critical_resource(int salt)
- {
- size_t i = 0;
- // 此函数每次向共享内存中按递增顺序写入三个无符号整数
- printf("writting critical resource:\n");
- for (i = 0; i < 3; ++i) {
- printf("%d\t", salt + i);
- critical_resource[i] = salt + i;
- }
- printf("\n");
- }
- void entry_task_writer(void *arg)
- {
- size_t salt = 0;
- k_err_t err;
- while (K_TRUE) {
- // 在向临界区写入数据之前,先尝试获取临界区保护锁
- err = tos_mutex_pend(&critical_resource_locker);
- if (err == K_ERR_NONE) {
- // 成功获取锁之后,向临界区写入数据
- write_critical_resource(salt);
- // 写完数据后,释放互斥锁
- tos_mutex_post(&critical_resource_locker);
- }
- tos_task_delay(1000);
- ++salt;
- }
- }
- static void read_critical_resource(void)
- {
- size_t i = 0;
- // 从临界区读取数据
- printf("reading critical resource:\n");
- for (i = 0; i < 3; ++i) {
- printf("%d\t", critical_resource[i]);
- }
- printf("\n");
- }
- void entry_task_reader(void *arg)
- {
- k_err_t err;
- while (K_TRUE) {
- // 读取临界区数据之前,先尝试获取临界区保护锁
- err = tos_mutex_pend(&critical_resource_locker);
- if (err == K_ERR_NONE) {
- // 成功获取锁之后,从临界区读取数据
- read_critical_resource();
- // 读取数据完毕后,释放互斥锁
- tos_mutex_post(&critical_resource_locker);
- }
- tos_task_delay(1000);
- }
- }
- int main(void)
- {
- board_init();
- tos_knl_init();
- // 创建临界区保护互斥锁
- tos_mutex_create(&critical_resource_locker);
- (void)tos_task_create(&task_writer, "writer", entry_task_writer, NULL,
- 4, stack_task_writer, STK_SIZE_TASK_WRITER, 0);
- (void)tos_task_create(&task_reader, "reader", entry_task_reader, NULL,
- 4, stack_task_reader, STK_SIZE_TASK_READER, 0);
- tos_knl_start();
- }
运行效果
writting critical resource:0 1 2reading critical resource:0 1 2writting critical resource:1 2 3reading critical resource:1 2 3writting critical resource:2 3 4reading critical resource:2 3 4writting critical resource:3 4 5reading critical resource:3 4 5writting critical resource:4 5 6reading critical resource:4 5 6writting critical resource:5 6 7reading critical resource:5 6 7writting critical resource:6 7 8reading critical resource:6 7 8writting critical resource:7 8 9reading critical resource:7 8 9
2.3.2 信号量
概述
信号量是一种实现任务间同步的机制,一般用于多个任务间有限资源竞争访问。
通常来说,一个信号量中持有一个整形数值,用以表示可用资源的数量。当一个信号量的可用资源数量大于0时,任务尝试获取该信号量成功,信号量的可用资源数减一;当一个信号量的可用资源数等于0时,任务尝试获取该信号量失败或进入阻塞状态。信号量的这一模式,当可用资源数为1时,可将其用于资源的互斥访问;或者解决生产者-消费者问题中的资源生产-消费问题。编程实例章节会演示生产者-消费者问题的解决范式。
API讲解
编程实例
1、在tos_config.h中,配置信号量组件开关TOS_CFG_SEM_EN:
#define TOS_CFG_SEM_EN 1u
2、编写main.c示例代码:
- #include "tos.h"
- #include "mcu_init.h"
- #define STK_SIZE_TASK_PRODUCER 512
- #define STK_SIZE_TASK_CONSUMER 512
- k_stack_t stack_task_producer[STK_SIZE_TASK_PRODUCER];
- k_stack_t stack_task_consumer[STK_SIZE_TASK_CONSUMER];
- k_task_t task_producer;
- k_task_t task_consumer;
- extern void entry_task_producer(void *arg);
- extern void entry_task_consumer(void *arg);
- k_mutex_t buffer_locker;
- k_sem_t full;
- k_sem_t empty;
- #define RESOURCE_COUNT_MAX 3
- struct resource_st {
- int cursor;
- uint32_t buffer[RESOURCE_COUNT_MAX];
- } resource = { 0, {0} };
- static void produce_item(int salt)
- {
- printf("produce item:\n");
- printf("%d", salt);
- resource.buffer[resource.cursor++] = salt;
- printf("\n");
- }
- void entry_task_producer(void *arg)
- {
- size_t salt = 0;
- k_err_t err;
- while (K_TRUE) {
- err = tos_sem_pend(&empty, TOS_TIME_FOREVER);
- if (err != K_ERR_NONE) {
- continue;
- }
- err = tos_mutex_pend(&buffer_locker);
- if (err != K_ERR_NONE) {
- continue;
- }
- produce_item(salt);
- tos_mutex_post(&buffer_locker);
- tos_sem_post(&full);
- tos_task_delay(1000);
- ++salt;
- }
- }
- static void consume_item(void)
- {
- printf("cosume item:\n");
- printf("%d\t", resource.buffer[--resource.cursor]);
- printf("\n");
- }
- void entry_task_consumer(void *arg)
- {
- k_err_t err;
- while (K_TRUE) {
- err = tos_sem_pend(&full, TOS_TIME_FOREVER);
- if (err != K_ERR_NONE) {
- continue;
- }
- tos_mutex_pend(&buffer_locker);
- if (err != K_ERR_NONE) {
- continue;
- }
- consume_item();
- tos_mutex_post(&buffer_locker);
- tos_sem_post(&empty);
- tos_task_delay(2000);
- }
- }
- int main(void)
- {
- board_init();
- tos_knl_init();
- tos_mutex_create(&buffer_locker);
- tos_sem_create(&full, 0);
- tos_sem_create(&empty, RESOURCE_COUNT_MAX);
- (void)tos_task_create(&task_producer, "producer", entry_task_producer, NULL,
- 4, stack_task_producer, STK_SIZE_TASK_PRODUCER, 0);
- (void)tos_task_create(&task_consumer, "consumer", entry_task_consumer, NULL,
- 4, stack_task_consumer, STK_SIZE_TASK_CONSUMER, 0);
- tos_knl_start();
- }
运行效果
produce iterm:0cosume iterm:0produce iterm:1produce iterm:2cosume iterm:2produce iterm:3produce iterm:4cosume iterm:4produce iterm:5cosume iterm:5produce iterm:6cosume iterm:6produce iterm:7cosume iterm:7produce iterm:8cosume iterm:8produce iterm:9cosume iterm:9produce iterm:10
2.3.3 事件
概述
事件提供了一种任务间实现同步和信息传递的机制。一般来说,一个事件中包含了一个旗标,这个旗标的每一位表示一个“事件”。
一个任务可以等待一个或者多个“事件”的发生,其他任务在一定的业务条件下可以通过写入特定“事件”唤醒等待此“事件”的任务,实现一种类似信号的编程范式。
API讲解
编程实例
1、在tos_config.h中,配置事件组件开关TOS_CFG_EVENT_EN:
#define TOS_CFG_EVENT_EN 1u
2、编写main.c示例代码:
- #include "tos.h"
- #include "mcu_init.h"
- #define STK_SIZE_TASK_LISTENER 512
- #define STK_SIZE_TASK_TRIGGER 512
- k_stack_t stack_task_listener1[STK_SIZE_TASK_LISTENER];
- k_stack_t stack_task_listener2[STK_SIZE_TASK_LISTENER];
- k_stack_t stack_task_trigger[STK_SIZE_TASK_TRIGGER];
- k_task_t task_listener1;
- k_task_t task_listener2;
- k_task_t task_trigger;
- extern void entry_task_listener1(void *arg);
- extern void entry_task_listener2(void *arg);
- extern void entry_task_trigger(void *arg);
- const k_event_flag_t event_eeny = (k_event_flag_t)(1 << 0);
- const k_event_flag_t event_meeny = (k_event_flag_t)(1 << 1);
- const k_event_flag_t event_miny = (k_event_flag_t)(1 << 2);
- const k_event_flag_t event_moe = (k_event_flag_t)(1 << 3);
- k_event_t event;
- void entry_task_listener1(void *arg)
- {
- k_event_flag_t flag_match;
- k_err_t err;
- while (K_TRUE) {
- // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ALL选项,因此必须是四个事件同时到达此任务才会被唤醒
- err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
- &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ALL | TOS_OPT_EVENT_PEND_CLR);
- if (err == K_ERR_NONE) {
- printf("entry_task_listener1:\n");
- printf("eeny, meeny, miny, moe, they all come\n");
- }
- }
- }
- void entry_task_listener2(void *arg)
- {
- k_event_flag_t flag_match;
- k_err_t err;
- while (K_TRUE) {
- // 此任务监听四个事件,因为使用了TOS_OPT_EVENT_PEND_ANY选项,因此四个事件任意一个到达(包括四个事件同时到达)任务就会被唤醒
- err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
- &flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
- if (err == K_ERR_NONE) {
- printf("entry_task_listener2:\n");
- // 有事件到达,判断具体是哪个事件
- if (flag_match == event_eeny) {
- printf("eeny comes\n");
- }
- if (flag_match == event_meeny) {
- printf("meeny comes\n");
- }
- if (flag_match == event_miny) {
- printf("miny comes\n");
- }
- if (flag_match == event_moe) {
- printf("moe comes\n");
- }
- if (flag_match == (event_eeny | event_meeny | event_miny | event_moe)) {
- printf("all come\n");
- }
- }
- }
- }
- void entry_task_trigger(void *arg)
- {
- int i = 1;
- while (K_TRUE) {
- if (i == 2) {
- printf("entry_task_trigger:\n");
- printf("eeny will come\n");
- // 发送eeny事件,task_listener2会被唤醒
- tos_event_post(&event, event_eeny);
- }
- if (i == 3) {
- printf("entry_task_trigger:\n");
- printf("meeny will come\n");
- // 发送eeny事件,task_listener2会被唤醒
- tos_event_post(&event, event_meeny);
- }
- if (i == 4) {
- printf("entry_task_trigger:\n");
- printf("miny will come\n");
- // 发送eeny事件,task_listener2会被唤醒
- tos_event_post(&event, event_miny);
- }
- if (i == 5) {
- printf("entry_task_trigger:\n");
- printf("moe will come\n");
- // 发送eeny事件,task_listener2会被唤醒
- tos_event_post(&event, event_moe);
- }
- if (i == 6) {
- printf("entry_task_trigger:\n");
- printf("all will come\n");
- // 同时发送四个事件,因为task_listener1的优先级高于task_listener2,因此这里task_listener1会被唤醒
- tos_event_post(&event, event_eeny | event_meeny | event_miny | event_moe);
- }
- tos_task_delay(1000);
- ++i;
- }
- }
- int main(void)
- {
- board_init();
- tos_knl_init();
- tos_event_create(&event, (k_event_flag_t)0u);
- // 这里task_listener1的优先级比task_listener2高,因此在task_trigger发送所有事件时,task_listener1会被唤醒
- // 读者可以尝试将task_listener1优先级修改为5(比task_listener2低),此设置下,在task_trigger发送所有事件时,task_listener2将会被唤醒。
- (void)tos_task_create(&task_listener1, "listener1", entry_task_listener1, NULL,
- 3, stack_task_listener1, STK_SIZE_TASK_LISTENER, 0);
- (void)tos_task_create(&task_listener2, "listener2", entry_task_listener2, NULL,
- 4, stack_task_listener2, STK_SIZE_TASK_LISTENER, 0);
- (void)tos_task_create(&task_trigger, "trigger", entry_task_trigger, NULL,
- 4, stack_task_trigger, STK_SIZE_TASK_TRIGGER, 0);
- tos_knl_start();
- }
运行效果
entry_task_trigger:eeny will comeentry_task_listener2:eeny comesentry_task_trigger:meeny will comeentry_task_listener2:meeny comesentry_task_trigger:miny will comeentry_task_listener2:miny comesentry_task_trigger:moe will comeentry_task_listener2:moe comesentry_task_trigger:all will comeentry_task_listener1:eeny, meeny, miny, moe, they all come
2.3.4 队列
概述
队列提供了一种任务间实现同步和数据传递的机制。事件只能用于任务间传递某类“事件”是否发生的信号,无法传递更为复杂的数据,队列弥补了事件的这一不足,可以在任务间传递不定长度的消息。
API讲解
编程实例
1、在tos_config.h中,配置队列组件开关TOS_CFG_QUEUE_EN:
#define TOS_CFG_QUEUE_EN 1u
2、在tos_config.h中,配置消息队列组件开关TOS_CFG_MSG_EN:
#define TOS_CFG_MSG_EN 1u
3、编写main.c示例代码:
- #include "tos.h"
- #include "mcu_init.h"
- #define STK_SIZE_TASK_RECEIVER 512
- #define STK_SIZE_TASK_SENDER 512
- #define PRIO_TASK_RECEIVER_HIGHER_PRIO 4
- #define PRIO_TASK_RECEIVER_LOWER_PRIO (PRIO_TASK_RECEIVER_HIGHER_PRIO + 1)
- k_stack_t stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
- k_stack_t stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
- k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];
- k_task_t task_receiver_higher_prio;
- k_task_t task_receiver_lower_prio;
- k_task_t task_sender;
- k_queue_t queue;
- extern void entry_task_receiver_higher_prio(void *arg);
- extern void entry_task_receiver_lower_prio(void *arg);
- extern void entry_task_sender(void *arg);
- void entry_task_receiver_higher_prio(void *arg)
- {
- k_err_t err;
- void *msg_received;
- size_t msg_size;
- while (K_TRUE) {
- err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
- if (err == K_ERR_NONE) {
- printf("entry_task_receiver_higher_prio:\n");
- printf("message body: %s\n", (char *)msg_received);
- printf("message size: %d\n", msg_size);
- }
- }
- }
- void entry_task_receiver_lower_prio(void *arg)
- {
- k_err_t err;
- void *msg_received;
- size_t msg_size;
- while (K_TRUE) {
- err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
- if (err == K_ERR_NONE) {
- printf("entry_task_receiver_lower_prio:\n");
- printf("message body: %s\n", (char *)msg_received);
- printf("message size: %d\n", msg_size);
- }
- }
- }
- void entry_task_sender(void *arg)
- {
- int i = 1;
- char *msg_to_one_receiver = "message for one receiver[with highest priority]";
- char *msg_to_all_receiver = "message for all receivers";
- // 此任务不断通过队列queue发送消息
- while (K_TRUE) {
- if (i == 2) {
- printf("entry_task_sender:\n");
- printf("send a message to one receiver, and shoud be the highest priority one\n");
- // 发送消息并唤醒一个等待任务,唤醒的应该是等待任务中优先级最高的
- tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
- }
- if (i == 3) {
- printf("entry_task_sender:\n");
- printf("send a message to all recevier\n");
- // 发送消息并唤醒所有正在等待的任务
- tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
- }
- if (i == 4) {
- printf("entry_task_sender:\n");
- printf("send a message to one receiver, and shoud be the highest priority one\n");
- // 发送消息并唤醒一个等待任务,唤醒的应该是等待任务中优先级最高的
- tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
- }
- if (i == 5) {
- printf("entry_task_sender:\n");
- printf("send a message to all recevier\n");
- // 发送消息并唤醒所有正在等待的任务
- tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
- }
- tos_task_delay(1000);
- ++i;
- }
- }
- int main(void)
- {
- board_init();
- tos_knl_init();
- tos_queue_create(&queue);
- // task_receiver_higher_prio任务的优先级较高
- (void)tos_task_create(&task_receiver_higher_prio, "receiver_higher_prio",
- entry_task_receiver_higher_prio, NULL, PRIO_TASK_RECEIVER_HIGHER_PRIO,
- stack_task_receiver_higher_prio, STK_SIZE_TASK_RECEIVER, 0);
- // task_receiver_lower_prio任务的优先级较低
- (void)tos_task_create(&task_receiver_lower_prio, "receiver_lower_prio",
- entry_task_receiver_lower_prio, NULL, PRIO_TASK_RECEIVER_LOWER_PRIO,
- stack_task_receiver_lower_prio, STK_SIZE_TASK_RECEIVER, 0);
- (void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
- 4, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
- tos_knl_start();
- }
运行效果
entry_task_trigger:send a message to one receiver, and shoud be the highest priority oneentry_task_receiver_higher_prio:message body: message for one receiver[with highest priority]message size: 47entry_task_trigger:send a message to all recevierentry_task_receiver_higher_prio:message body: message for all receiversmessage size: 25entry_task_receiver_lower_prio:message body: message for all receiversmessage size: 25entry_task_trigger:send a message to one receiver, and shoud be the highest priority oneentry_task_receiver_higher_prio:message body: message for one receiver[with highest priority]message size: 47entry_task_trigger:send a message to all recevierentry_task_receiver_higher_prio:message body: message for all receiversmessage size: 25entry_task_receiver_lower_prio:message body: message for all receiversmessage size: 25