文章目录
- 一.前言
-
- 二.信号量
- 1.信号与信号量介绍
- 2.信号量常用函数
- 3.信号量使用流程(互斥信号量和信号量两种)
- 4.互斥型信号量使用
- 5.使用一般信号量做任务同步
- 三.信号量集(事件标志组)
- 1.信号量集概念
- 2.信号量集的结构组成
- 3.等待任务
- 4.等待任务链表
- 5.信号量集常用函数
- 6.信号量集使用步骤*****
- 四.总结
一.前言
1.任务间的同步
应用程序中的各个任务,必须通过彼此之间的有效合作,才能完成一项大规模的工作。因此在多任务合作工作的过程中,操作系统应该解决两个问题:一是各任务之间应该具有一种互斥关系,即对于某个共享资源,如果一个任务正在使用,则其他任务只能等待,直到请求资源被释放后才能使用。二是相关的任务的执行上要有先后次序,一个任务要等其他伙伴发来通知,或建立某个条件后才能继续执行。任务间的这种制约性的合作机制叫做任务间的同步。
2.事件
UCOSII系统中使用到的信号量、消息邮箱、消息队列来实现任务之间通信。统一称为事件。
两个任务通过事件通信的示意图:
二.信号量
1.信号与信号量介绍
使用信号量的最初目的是为了给共享资源设立一个标志。
信号量通常分为两种:信号和信号量。
信号只能取值为0或1,用于互斥任务之间的通信,用于那些只能一个任务使用的资源。信号量值大于1,可用于那些资源可以同时被几个任务所使用。调用释放信号量函数post(),信号量值加1,调用请求信号量函数pend(),信号量值减1。同时可以设置请求信号量的等待时限。
2.信号量常用函数
OSSemCreate();
OSSemDel();
OSSemPend();
OSSemPendAbrot();
OSSemPost();
OSSemSet();
其中主要用到的函数是创建,请求,发送。
3.信号量使用流程(互斥信号量和信号量两种)
定义信号量:
OS_EVENT * sem_mutex;
OS_EVENT * sem_ordinary;
创建信号量:
sem_mutex=OSMutexCreate(2,&err);
sem_ordinary=OSSemCreate(0);
请求信号量:
OSMutexPend(sem_mutex,0,&err);
OSSemPend(sem_ordinary,0,&err);
释放信号量:
OSMutexPost(sem_mutex);
OSSemPost(sem_ordinary);
4.互斥型信号量使用
u8 share_resource[]="share_resource";
void task1_task(void *p_arg)
{
while(1)
{
OSMutexPend(sem_mutex,0,&err);
printf("task1=%s\n",share_resource);
OSMutexPost(sem_mutex);
}
}
void task2_task(void *p_arg)
{
while(1)
{
OSMutexPend(sem_mutex,0,&err);
printf("task2=%s\n",share_resource);
OSMutexPost(sem_mutex);
}
}
可以看出,任务1和任务2共享了资源share_resource[],因此未来保障共享资源同一时刻只能被一个任务使用,因此要采用互斥型信号量做通信,每个信号开始前先请求信号量,(信号量变为0,另一个任务请求不到),任务执行结束后释放信号量(信号量值为1)。
5.使用一般信号量做任务同步
void task1_task(void *p_arg)
{
u8 key;
u8 err;
while(1)
{
key=KEY_Scan(0);
if(key=WKUP_PRES)
{
OSSemPost(sem_ordinary);
LCD_ShowxNum(192,50,sem_ordinary->OSEventCnt,3,16,0X80);
}
delay_ms(10);
}
}
void task2_task(void *p_arg)
{
u8 err;
while(1)
{
OSSemPend(sem_ordinary,0,&err);
LCD_ShowxNum(192,50,sem_ordinary->OSEventCnt,3,16,0X80);
LED1=!LED1;
delay_ms(1000);
}
}
由上面可以看出,通过信号量sem_ordinary实现了任务1与任务2之间的同步,按键按下后任务1发送信号量,信号量计数加1,任务二每隔1S请求一次信号量,信号量值减1,减至0后无限等待。
三.信号量集(事件标志组)
1.信号量集概念
有时候一个任务需要与多个事件同步,这个时候就需要使用事件标志组。事件标志组与事件之间有两种同步机制:“或”同步和“与”同步。
在UCOSII中事件标志组为OS_FLAG_GRP,如果需要使用事件标志组的时候需要将宏OS_CFG_FLAG_EN置1。
2.信号量集的结构组成
UCOSII把信号量集的功能分为两部分:标志组(存放了信号量集的所有信号)和等待任务链表(链表中的每个节点对应一个叫做OS_FLAG_NODE的结构,实质上就是等待任务控制块)。也就是说,UCOSII信号量集由一个标志组和多个等待任务控制块组成。
标志组OSFlagFlags实际上就是一个位图,其长度可以在OS_CFG.H中定制,系统默认16位。该位图每一位对应一个信号量,位图的作用是接收和保存其他任务所发送来的信号量值,所以可以看做是输入信号暂存器。
信号量集需要一个控制块,这个控制块叫做标志组OS_FLAG_GRP,OSFlagFlags是标志组的成员。标志组是一个结构体,具体如下:
typedef struct struct
{
INT8U OSFlagType;
void *OSFlagWaitList;
OS_FLAGS OSFlagFlags;
} OS_FLAG_GRP;
其中:
1.OSFlagFlags为核心成员;
2.OSFlagType为信号量集标识,其值固定为OS_EVENT_TYPE_FLAG;
3.OSFlagWaitList指针有两种用途,主要是指向一个链表,该链表中存放了给信号量集的全部等待任务。
OS_FLAG_GRP如下图:
3.等待任务
等待任务就是那些以及向信号量集发出了请求操作的任务。
信号量集等待任务的操作比较复杂,等待任务必须完成以下两个操作:
1.在多个信号量的输入中挑选等待任务感兴趣的输入(过滤)。
2.把挑选出来的输入按照等待任务所希望的逻辑来运算,以得出输出(与或)。
这里定义了一个OSFlagNodeWaitType变量来指定对筛选出来的信号的逻辑运算。可选值和意义见下表:
OSFlagFlags,OSFlagNodeFlags,OSFlagNodeWaitType三者之间的关系如下图所示:
4.等待任务链表
信号量集用双向链表来组织等待任务,每一个等待任务都是该链表中的一个节点(Node)。标志组OS_FLAG_GRP的成员OSFlagWaitList就指向了信号量集的这个等待任务链表。
等待任务链表与标志组的整个信号量集示意图如下所示:
5.信号量集常用函数
OSFlagCreate();
OSFlagDel();
OSFlagPend();
OSFlagPendAbort();
OSFlagPendGetFlagsRdy();
OSFlagPost();
6.信号量集使用步骤*****
1.定义信号量集
OS_FLAG_GRP * flags;
2.创建信号量集
flags=OSFlagCreate(0,&err);
3.请求信号量集
void uart1_task(void *pdata)
{
u8 err;
u16 flag_receive=0;
while(1)
{
flag_receive=OSFlagPend(flags,0X0003,OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME,0,&err);
if(flag_receive==1)printf("KEY0 DOWN \n");
if(flag_receive==2)printf("KEY1 DOWN \n");
if(flag_receive==3)printf("KEY2 DOWN \n");
if(flag_receive==6)printf("KEY_UP DOWN \n");
delay_ms(50);
};
}
4.发送信号量集
void key_task(void *pdata)
{
u8 key=0;
u8 err=0;
while(1)
{
key=KEY_Scan(0);
switch(key)
{
case 1:
OSFlagPost(flags,1,OS_FLAG_SET,&err);
break;
case 2:
OSFlagPost(flags,2,OS_FLAG_SET,&err);
break;
case 3:
OSFlagPost(flags,3,OS_FLAG_SET,&err);
break;
case 4:
OSFlagPost(flags,8,OS_FLAG_SET,&err);
break;
default:break;
}
delay_ms(10);
};
}
最终的实验现象:按下KEY2,串口打印KEY2 DOWN,按下KEY0和KEY1,串口打印KEY2 DOWN。
5.查询信号量集状态(备用)
#if OS_FLAG_QUERY_EN > 0u
OS_FLAGS OSFlagQuery (OS_FLAG_GRP *pgrp,
INT8U *perr
)
{
....................................................
}
6.删除信号量集
OS_FLAG_GRP *OSFlagDel(
OS_FLAG_GRP *pgrp,
INT8U opt,INT8U *err;
)
四.总结
1.信号量表明一个共享资源被使用情况的标志,该标志实质上是一个计数器,如果计数器的初值大于1,则叫做信号量,如果计数器的值只能为1或0,则叫做信号。
2.能防止出现优先级反转的信号叫做互斥型信号量。
3.信号量集实现了多个信号量的组合功能,它是一个多输入多输出系统,使一个任务可以与多个任务进行同步。
4.信号量集的多个信号量输入值由标志组来存放,等待任务控制块对标志组中的输入信号进行过滤并实施逻辑运算,其结果就是等待任务所请求的信号量值。
5.每个信号量集都有一个等待任务链表,链表的每一个节点都通过任务控制块关联着一个任务。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)