信号量
- 信号量简介
- 信号量保护共享资源
-
- 信号量实现任务同步
- 总结
信号量简介
信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。
信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用
信号量通常分为两种:二进制信号量和计数型信号量。
二进制信号量只能取0和1两个值,计数型信号量的信号量值大于1,计数型信号量的范围由OS_SEM_CTR决定,OS_SEM_CTR可以为8位,16位和32位,取值范围分别为:0255,065535和0~4294967295。
二级制信号量用于那些一次只能一个任务使用的资源,比如I/O设备,打印机计,数型信号量用于某些资源可以同时被几个任务所使用,比如一个缓存池有10个缓存块,那么同时最多可以支持10个任务来使用内存池
第一遍看是不是看不懂,小问题,只要了解到他如何使用就知道它是什么了。
信号量保护共享资源
举个栗子:如果不使用信号量
首先先看一下我的这两个任务,我们定义一个全局数组soure_array
作为公共资源,我们在task1
和task2
两个任务中都使用了soure_array
这个资源:
任务一,将数据复制到soure_array
数组,并且打印出来。
char soure_array[40];
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
char task1_array[] = "task1_suning!";
while(1)
{
printf("任务一:\r\n");
LED0 = !LED0;
memcpy(soure_array,task1_array,sizeof(task1_array));
delay_ms(200);
printf("%s\r\n\r\n",soure_array);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
}
}
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
char task2_array[] = "task2_runing!";
while(1)
{
printf("任务二:\r\n");
LED1 = !LED1;
memcpy(soure_array,task2_array,sizeof(task2_array));
delay_ms(200);
printf("%s\r\n\r\n",soure_array);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
}
}
运行结果:
我们期待的运行结果是任务一输出一次,任务二输出一次。
我们会发现这并不是我们想要的运行结果,这是因为任务二和任务一在同一时间占用了soure_array
这个资源,如何解决呢?使用信号量就可以了。
信号量解决公共资源问题
首先我们得了解一下函数:
我们常用的呢就是标红的三个函数。
创建信号量:
1.创建信号量结构体
OS_SEM my_sem;
2.使用OSSemCreate()
建立一个信号量
在开始任务中创建:
void OSSemCreate (OS_SEM *p_sem,
CPU_CHAR *p_name,
OS_SEM_CTR cnt,
OS_ERR *p_err)
2.信号量的使用
主要用到一下两个函数OSSemPend()
和OSSemPost()
:
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
3.更改后的任务代码如下:
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
char task1_array[] = "task1_suning!";
while(1)
{
printf("任务一:\r\n");
LED0 = !LED0;
OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);
memcpy(soure_array,task1_array,sizeof(task1_array));
delay_ms(200);
printf("%s\r\n\r\n",soure_array);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
OSSemPost(&my_sem,OS_OPT_POST_1,&err);
}
}
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
char task2_array[] = "task2_runing!";
while(1)
{
printf("任务二:\r\n");
LED1 = !LED1;
OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);
memcpy(soure_array,task2_array,sizeof(task2_array));
delay_ms(200);
printf("%s\r\n\r\n",soure_array);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
OSSemPost(&my_sem,OS_OPT_POST_1,&err);
}
}
4.运行结果
这样就达到了我们想要的效果:
我们可以分析它的执行过程:
- 任务二等待到信号量,打印,任务二发送信号量
- 任务一等待到信号量,打印,任务一发送信号量
- 任务二等待到信号量…
如此循环,就可以解决在两个或者多个任务使用同一个资源时出错的问题了。
下面我们在介绍一种信号量的应用:
信号量实现任务同步
首先我么要知道任务同步是什么,就是一个任务每执行一次,另外一个任务也跟着执行一次。这两个任务就算同步,用信号量来实现也是非常简单的:
前面都一样,就是创建任务,创建信号量
void key_task(void *p_arg)
{
OS_ERR err;
u8 key = 0;
p_arg = p_arg;
while(1)
{
key = Key_Scan(0);
if(key == Short_Press)
{
LED0 = !LED0;
printf("任务一:\r\n");
OSSemPost(&my_sem,OS_OPT_POST_1,&err);
printf("当前信号量:%d\r\n\r\n",my_sem.Ctr);
}
OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);
}
}
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
OSSemPend(&my_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);
LED1 = !LED1;
printf("任务二:\r\n");
printf("当前信号量:%d\r\n\r\n",my_sem.Ctr);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);
}
}
我们看一下运行结果:
当我连续按下两次按键之后,任务一发送两次信号量,我们发现信号量数值从0变化到2;然后任务二等待信号量两次,将信号量数值从2变化到0;最终效果就是任务一执行几次,任务二就执行几次。
总结
学过这个之后,我还是有很多问题的,希望有大佬帮我解解惑:
1.在信号量实现保护资源实验中,删除OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s
这个延时,任务执行还是正常的,任务一任务二依次执行,这个不是很理解。
2.二进制信号量和计数信号量的主要区别和在什么情况使用不是很清楚。
希望我的文章可以让你学会信号量,下次见!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)