自编STM32轻量级操作系统(四)------信号量的实现

你好,这里是风筝的博客,

欢迎和我一起交流。


之前我们已经完成了基础的框架,现在我们来一步步完善它:

添加信号量!

但是,什么是信号量呢?

别急,百度百科上有说:信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。


上面是官方的说法,其实可以说得通俗点:

还记得第一章:自编STM32轻量级操作系统(一)------操作系统实现 里说的过的一个例子吗?共享资源的访问,上厕所那个。

比方说,有三间厕所(资源),厕所一次只能进一个人吧,,,,,,

然后在厕所门口有三把钥匙(信号量),对应三间厕所。有五个人想上厕所,那就只能进去三个人咯,两个人在外面等着,里面出来一个人,把钥匙放回去,第四个人才能进去。

当然,有时信号量也用来显示任务“同步”机制,当一个任务申请不到信号量而休眠时,另一个任务释放信号量来“唤醒”这个任务。


好了,我们看代码是怎么实现的:

记得以前我们定义的TCB任务控制块吗?现在我们需要在定义一个事件控制块ECB


计数器代表厕所钥匙的个数,事件等待表用来记录有多少个在等厕所。


接着是创建一个信号量:


传参cnt为放出钥匙的数量(计数器),这里my_malloc就是我们之前内存管理:自编STM32轻量级操作系统(三)------内存管理写的函数。然后事件等待表设为0,这里也是和任务优先级一样的用法哦。


之前有个东西忘记说了,就是临界区!

为了防止某些操作被打断,MDK里面,利用编译器扩展功能获得程序状态字,保存在保存在局部变量cpu_sr里。


其中,OS_CPU_SR_Save和OS_CPU_SR_Restore是汇编函数,就懒得展开了,有兴趣的可以自己看看,注释我也写有了,在汇编文件里。


接下来是请求信号量:


进入函数,首先,先判断厕所钥匙有没有(计数器是否大于零),如果有,那就拿一把钥匙(计数器自减),上厕所去(return掉)。

如果没有厕所钥匙,设置任务的状态为信号量等待状态。

#define  OS_STAT_PEND_OK                0u  
#define  OS_STAT_PEND_TO                1u

注意要在任务控制块里加上新的成员OSTCBStatPend哦:

typedef struct OS_Tcb  
{
unsigned int *StkPtr;//任务栈顶
unsigned int DLy;//任务延时时钟
unsigned char OSTCBStatPend;//任务状态
   
}TCB; //任务控制块


接下来注意传参time哦,如果time传入的是0,则代表死等,非要等到厕所里的人出来不可,如果是其他数,比如10,好家伙,我就等你10个时钟周期,如果没人出来(信号量释放),一咬牙一跺脚,这个厕所我不上了!!!!

同时为了等待信号量,想自身添加到信号等待表中,并从任务就绪表中删除自己,进入休眠,同时调用OS_Sched函数引发调度!!

如果传参time是10的话,那么10个时钟周期过后,自己任务会被重新添加到任务就绪表,这个函数将会被再次执行,通过判断任务状态,可以知道是等待超时而引发的再次调度,所以在if里面,将自己任务从信号等待表中删除,恢复任务状态为正常运行状态。

那么,如果在第十个时钟周期之前,就获得信号量呢?

看下释放信号量函数:


看这个函数,一开始,先判断有没有任务在等待信号量在等待的?如果有,则从信号等待表中找出优先级最高的任务,添加到任务就绪就绪表中,并设置任务状态为正常运行状态,和等待超时状态区分开来,然后引发一次调度。

如果开始没有任务在等待信号量,并且计数器小于255(这里我设置计数器满值为255),则计数器自加一。


有创建当然有删除了,最后就是删除信号量了:


简单的函数,直接free掉即可。

这里我设置的为:只有没有任务在等待信号量时,才能删除信号量!

还有一个注意的地方:信号量删除之后,千万不要再使用该信号量!!!!!否则后果自负!!!!!!!


最后做一个信号量的小测试:

  1. //#include "stm32f10x.h"  
  2. #include "led.h"  
  3. #include "os.h"  
  4.   
  5. #define TASK_1_STK_SIZE 512  
  6. #define TASK_2_STK_SIZE 512  
  7.   
  8. unsigned int TASK_1_STK[TASK_1_STK_SIZE];  
  9. unsigned int TASK_2_STK[TASK_2_STK_SIZE];  
  10. ECB * s_msg;            //信号量  
  11.   
  12. void Task1(void)  
  13. {  
  14.     while(1)  
  15.     {  
  16.         OS_SemPend(s_msg,0);//请求信号量  
  17.         LED0=!LED0;  
  18.         OSTimeDly(200);  
  19.     }  
  20. }  
  21. void Task2(void)  
  22. {  
  23.     unsigned int i = 0;  
  24.     while(1)  
  25.     {  
  26.         i++;  
  27.         if(10==i)  
  28.         {  
  29.             i=0;  
  30.             OS_SemPost(s_msg);  
  31.         }  
  32.         LED1=!LED1;  
  33.         OSTimeDly(150);  
  34.     }  
  35. }  
  36.   
  37. int main(void)  
  38. {     
  39.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
  40.     LED_Init();  
  41.     Task_Create(Task1,&TASK_1_STK[TASK_1_STK_SIZE-1],0);  
  42.     Task_Create(Task2,&TASK_2_STK[TASK_2_STK_SIZE-1],1);  
  43.       
  44.     s_msg=OS_SemCreate(0);//开始时,信号量计数器为0  
  45.     OS_Start();   
  46.     return 0;  
  47. }  

这样,当Task1运行时,因为得不到信号量,而且函数第二个参数为0,从而进入休眠死等信号量的状态。

当Task2运行十次时,会释放一次信号量,从而引发调度,Task1得到运行,周而复始。

现象为:LED1闪烁五次,LED亮、灭一次。


恩,这章就那么多了,下一章讲升级版:互斥信号量


上一章:自编STM32轻量级操作系统(三)------内存管理


下一章:自编STM32轻量级操作系统(五)------互斥量





  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值