先看官方描述:
【提供例子为,一个任务为生产者,一个任务为消费者,一个共享仓库】
当生产者到来时,要先确认仓库是不是full信号量满了?如果满了放不下了就要等,不满就可以继续一下步;就是获取仓库的mutex互斥锁,如果之前仓库有锁,
生产者还得等等,等仓库解锁后,最后可以往仓库更新数据了。最后释放empty信号量和 mutex互斥锁,完成一个生产的操作。(因为消费者只关心empty,所以要+1)
当消费者到来时,要先确认仓库是不是empty,如果空了就取不到数据了要等一会,当不空了说明有数据了,就要获取仓库的mutex互斥锁,如果之前仓库有锁,
消费者也要等等,等仓库解锁后,最后可以往仓库取数据了。最后释放full信号量和 mutex互斥锁,完成一个消费的操作。(因为生产者只关心full,所以要full+1)
官方的源码有点乱,就改成上述原理描述的:
在TencentOS-tiny\board\SWM320_DISCOVERY\BSP\Src下新建tOS_semaphore.c,内容如下
#include "tos_k.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; // 信号量FULL k_sem_t empty; // 信号量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"); } /*---------------------消费数据----------------------------------------------*/ static void consume_item(void) { printf("cosume item:\n"); printf("%d\t", resource.buffer[--resource.cursor]); printf("\n"); } /*---------------------生产者任务-------------------------------------------*/ void entry_task_producer(void *arg) { size_t salt = 0; k_err_t err; while (1) { // 获取信号量 -1 err = tos_sem_pend(&full, 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(&empty); // 释放empty+1 tos_task_delay(1000); ++salt; // 数据更新 } } /*---------------------消费任务------------------------------------------------*/ void entry_task_consumer(void *arg) { k_err_t err; while (1) { // 获取信号量 -1 err = tos_sem_pend(&empty, TOS_TIME_FOREVER); if (err != K_ERR_NONE) { continue; // GO TO while(1) } // 获取互斥锁 tos_mutex_pend(&buffer_locker); if (err != K_ERR_NONE) { continue; // GO TO while(1) } consume_item(); // 消费数据 tos_mutex_post(&buffer_locker); // 释放互斥锁 tos_sem_post(&full); // 释放full+1 tos_task_delay(1000); } } /*-------------------------测试的应用函数入口--------------------------------*/ void application_sem(void *arg) // semaphore信号量测试 { // 创建mutex互斥 tos_mutex_create(&buffer_locker); // 创建sem信号量 tos_sem_create(&full, RESOURCE_COUNT_MAX); tos_sem_create(&empty, 0); // 创建task任务 tos_task_create(&task_producer, "producer", entry_task_producer, NULL, 4, stack_task_producer, STK_SIZE_TASK_PRODUCER, 0); tos_task_create(&task_consumer, "consumer", entry_task_consumer, NULL, 4, stack_task_consumer, STK_SIZE_TASK_CONSUMER, 0); }
在TencentOS-tiny\board\SWM320_DISCOVERY\BSP\Inc下的mcu_init.h增加如下
在main.c变更为:
/* 必要头文件.h */ #include "cmsis_os.h" // 原版本 V1.02 #include "mcu_init.h" /* 主main入口 */ int main(void) { /* PCB板初始化 */ board_init(); // mcu_init.h内实现 /* 打印开机信息 */ printf("Welcome to TencentOS v%s\r\n",TOS_VERSION); // mcu_init.h内实现 /* OS内核初始化 */ osKernelInitialize(); // 标准cmsis_os.h /* 用户Create */ //application_entry(NULL); // hello world.c测试,mcu_init.h内关联 //application_timer(NULL); // 软件定时器测试,mcu_init.h内关联 //application_mutex(NULL); // mutex的测试 application_sem(NULL); // semaphore信号量测试 /* start */ osKernelStart(); // 开始OS内核调度 }
SWM32S开发板上仿真正常:
对于这个信号量,官方是用生产者和消费者进行测试的,这个思维新手很难理解,我个人觉得,用一个生活事情更好的讲述
信号量,在生活中我们开车去超市,到了停车厂如果有空车位,就可以开车进去占个车位,这个时候你只关心有没有停车位,
有几个停车位其实你并不关心,想想是不是?如果没有停车位,你就只能等一下了。。---》这就是获取信号量
你进到停车厂里面了,得找XX号停车位进行停车,如果俩位车主同时看到一个车位,哪就是谁近就是谁的位置了,很可能得继续找
其他的车XX号位置,----》这就是获取互斥锁
当你开车离超市时,是先离开xx号停车位,再离开停车后门到马路回家,这就是 ---》这就是释放互斥锁、信号量
在这个生活例子中,门卫是生产者,因他只关心停车厂满没有满?满了就禁止车进来。车主是消费者,因为他只关心
有个XX号空车位。他们的焦点不同,一个关心FULL,一个关心empty。哎呀。不管怎么理解,这方面好像还是有点绕啊 !!