概述
任务同步
- 1 信号量
- 1.1 单向同步
- 1.2 信用记录
- 1.3 多个任务等待同一个信号
- 2 任务内嵌信号量
- 2.1 等待任务信号量
- 2.2 发布任务信号量
- 2.3 双向同步
- 3 事件标志组
- 3.1 使用事件标志组
- 4 与多任务同步
μC/OS-III中有两种基本的同步机制:信号量和事件标志组
1 信号量
信号量可以用做一种发信号的机制与一种保护共享资源的机制。前者在使用时,信号量的初始化值为0,后者一般将信号量计数值初始化为非0值,表示资源的初始可用数。但是由于信号量在进行保护共享资源时。可能存在优先级反转的问题,因此,信号量主要当作发信号的机制来使用。解决优先级反转可以采用互斥信号量来解决。信号量更多被用来实现任务间的同步以及任务和ISR间的同步。
函数 | 含义 |
---|---|
OSSemCreater() | 创建信号量 |
OSSemDel() | 删除信号量 |
OSSemPend() | 等待信号量 |
OSsemPendAbort() | 取消等待信号量 |
OSSemPost() | 释放信号量 |
OSSemSet() | 强制设置信号量计数 |
1.1 单向同步
任务使用信号量与ISR(或其他任务)同步的机制,该种情况下,没有数据交换,但可以表示ISR或任务是否运行过。使用信号量进行这种类型同步的操作被叫做单向同步。
(1)高优先级任务首先运行,并再请求信号量,由于信号量不可用,该任务被挂起等待
(3)系统进行任务调度,低优先级任务获得CPU使用权
(5)低优先级任务开始运行
(6)低优先级任务运行时,有中断发生,低优先级任务被中断
(7)中断服务程序运行,并释放信号量
(8)中断服务程序运行结束调用OSIntExit()函数,调度器调度
(9)系统发现有高优先级再等待信号量,调度器调度,使高优先级任务获得CPU使用权
(11) 高优先级任务请求信号量成功,高优先级运行。
OS_SEM MySem;
...
int main()
{
OS_ERR err;
OSInit(&err);
...
/*创建开始任务*/
OSStart(&err);
while(1);
}
void StartTask(void* p_arg)
{
OS_ERR err;
OSSemCreate(&MySem, "My Sem", 0, &err); /*创建信号量,初始值为0*/
/*创建任务*/
OSTaskSusPend(&StartTaskPCB,&err);
/*任务调度*/
}
void MyISR(void)
{
OS_ERR err;
/*清除中断标志位*/
OSSemPost(&MySem, OS_OPT_POST_1, &err); /*释放信号量给等待信号量的最高优先级任务*/
/*Check err*/
}
void MyTask(void* p_arg)
{
OS_ERR err;
CPU_TS ts;
...
while(DEF_ON)
{
OSSemPend(&MySem, 10, OS_OPT_PEND_BLOCKING, &ts, &err); /*阻塞请求信号量,并设置超时时间*/
/*Check err*/
...
}
}
1.2 信用记录
如果一个任务在请求信号量,但是中断发生了很多次导致信号量也释放了很多次,当信号量使计数型信号量时,发布的次数被信号量记录下来,当进行任务调度时,由于信号量的值大于0且不等于1则请求信号量的任务会被多次执行,执行的次数为信号量发布的次数,该种功能被称为信用记录。
(1)高优先级任务运行
(2)中断发生,高优先级任务被中断
(3)中断服务程序运行,发布信号量,退出中断,调度器调度
(7)高优先级任务继续执行
(8)中断再次发生,高优先级任务被中断
(9)中断服务程序运行,发布信号量,由于信号量期间未被请求,信号量的值再前有基础上递增,退出中断,任务调度
(13)高优先级任务继续运行
(14)高优先级任务执行完成,调度器调度
(17)低优先级请求信号量,低优先级运行,期间如果没有比其优先级高的任务就绪,则低优先级任务被执行次数等于信号量的值。
1.3 多个任务等待同一个信号
如果多个任务想同时获得信号量,则可以通过发布信号量的发送模式来操作,即将信号量以广播的形式发送给等待它的所有任务,选项为OS_OPT_POST_ALL,广播之后,如果等待信号量的任务优先级有高于正在执行任务的,则会进行任务调度。
2 任务内嵌信号量
信号量被用作发送信号机制很有用,因此,在μC/OS-III中,每个任务都有自己内嵌的信号量OS_TCB.SemCtr(该值在创建任务时被初始化为0)。如果当事件发生时用户明确知道该给哪个任务发信号,就可以使用任务信号量。
函数 | 含义 |
---|---|
OSTaskSemPend() | 等待任务信号量 |
OSTaskSemPendAbort() | 取消等待任务信号量 |
OSTaskSemPost() | 发布任务信号量 |
OSTaskSemSet() | 强行设置信号量计数值 |
2.1 等待任务信号量
void MyTask(void* p_arg)
{
OS_ERR err;
CPU_TS ts;
...
while(EDF_ON)
{
OSTaskSemPend(10, OS_OPT_PEND_BLOCKING, &ts, &err); /*阻塞等待自己的任务信号量,并设置超时时间10*时间片*/
/*Check Err*/
...
}
}
2.2 发布任务信号量
void MyISR(void)
{
OS_ERR err;
...
OSTaskSemPost(&MyTaskPCB, OS_POST_NONE, &err); /*发布MyTask的任务信号量,并在发布之后调用调度程序*/
/*
发布选项
OS_POST_NONE:(默认选项)表明在发布任务信号量之后调用调度程序
OS_POST_NO_SCHED:表明在发布任务信号量之后不会调用调度程序,可能还需要进行其他的操作,在所有操作完成时才调用调度程序
*/
/*check error*/
}
2.3 双向同步
两个任务可以使用它们的任务信号量来同步它们的行为,这种情况称为双向同步,双向同步与单向同步类似,需要在两个任务互相同步后才能继续运行,双向同步不能用于任务与ISR之间,因为ISR不等待信号量。
OS_TCB MyTask1_TCB;
OS_TCB MyTask2_TCB;
void Task1(void* p_arg)
{
OS_ERR err;
CPU_TS ts;
...
while(DEF_ON)
{
OSTaskSemPost(&MyTask2_TCB, OS_OPT_POST_NONE, &err);
/*check err*/
OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, &ts, &err);
/*check err*/
...
}
}
void Task2(void* p_arg)
{
OS_ERR err;
CPU_TS ts;
...
while(DEF_ON)
{
OSTaskSemPost(&MyTask1_TCB, OS_OPT_POST_NONE, &err);
/*check err*/
OSTaskSemPend(0, OS_OPT_PEND_BLOCKING, &ts, &err);
/*check err*/
...
}
}
3 事件标志组
当任务需要需要与多个任务的发生进行同步时,可以使用事件标志组,当等待多个事件时,任何一个事件的发生,都会使任务被同步,该种同步机制被称为“或”同步;当所有事件发生,任务才会被同步,该种同步机制称为“与”同步。
- 任务或ISR都可以发布事件标志
- 只有任务可以创建、删除事件标志组以及取消其他任务对事件标志组的等待
- 任务可以等待事件标志组中的任意个事件标志
- 任务可以设定使用“或”同步机制或者“与”同步机制
struct os_flag_grp { /* Event Flag Group */
/* --------- GENERIC MEMBERS ----------- */
OS_OBJ_TYPE Type; /* 事件标志组类型名'F','L','A','G'*/
CPU_CHAR *NamePtr; /* 事件标志组别名(ASCII字符串)*/
OS_PEND_LIST PendList; /* 事件标志组等待列表*/
#if OS_CFG_DBG_EN > 0u
OS_FLAG_GRP *DbgPrevPtr;
OS_FLAG_GRP *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
/* ------------ SPECIFIC MEMBERS ------------- */
OS_FLAGS Flags; /* 8, 16 or 32 bit flags */
CPU_TS TS; /* Timestamp of when last post occurred */
};
函数 | 含义 |
---|---|
OSFlagCreate() | 创建事件标志组 |
OSFlagDel() | 删除事件标志组 |
OSFlagPend() | 等待事件组标志 |
OSFlagPendAbort() | 取消等待事件组标志 |
OSFlagPendGetFlagsRdy() | 获取使任务就绪的事件标志 |
OSFlagPost() | 向任务事件组发送标志 |
3.1 使用事件标志组
当任务或ISR向事件标志组发布事件标志时,所有,满足等待条件的任务都会进入就绪态,事件标志组中的每一个事件标志含义由用户决定,可以根据需要使用任意个事件标志组
/*事件标志组为16位(根据OS_FLAGS决定),每一位可以代表一个事件标志*/
#define TEMP_LOW(OS_FLAGS)0x0001 //温度低于阈值标志
#define BATT_LOW(OS_FLAGS)0x0002 //电池电压低于阈值标志
#define SW_PRESSED(OS_FLAGS)0x0004 //开关按下标志
OS_FLAG_GRP MyEventFlagGrp;
void main(void)
{
OS_ERR err;
OSInit(&err);
...
OSFlagCreate((OS_FLAG_GRP*) &MyEventFlagGrp, /*创建的事件标志组*/
(CPU_CHAR*) "My Event flag group", /*事件标志组别名*/
(OS_FLAGS) 0, /*事件标志组初始值*/
(OS_ERR*) &err); /*错误返回码*/
/*check err*/
...
OSStart(&err);
}
void MyTask(void* p_arg)
{
OS_ERR err;
CPU_TS ts;
...
while(DEF_ON)
{
OSFlagPend((OS_FLAG_GRP*) &MyEventFlagGrp, /*请求的事件标志组*/
(OS_FLAGS) TEMP_LOW+BATT_LOW, /*请求的事件标志*/
(OS_TICK) 0, /*请求是否超时(不超时)*/
(OS_OPT) OS_OPT_PEND_FLAG_SET_ANY, /*请求事件组的同步机制(或)*/
(CPU_TS*) &ts, /*最近一次请求成功的时间戳*/
(OS_ERR*) &err); /*错误返回码*/
/*
OS_OPT 请求事件组选项
OS_OPT_PEND_FLAG_CLR_ALL 等待所有请求标志位置0 所有事件标志清零则任务唤醒
OS_OPT_PEND_FLAG_CLR_ANY 等待任意请求标志位置0 任意事件标志清零则任务唤醒
OS_OPT_PEND_FLAG_SET_ALL 等待所有请求标志位置1 所有事件标志置1则任务唤醒
OS_OPT_PEND_FLAG_SET_ANY 等待任意请求标志位置1 任意事件标志置1则任务唤醒
*/
/*Check error*/
...
}
}
void MyISR(void)
{
OS_ERR err;
...
OSFlagPost((OS_FLAG_GRP*) &MyEventFlagGrp, /*发布的事件标志组*/
(OS_FLAGS) BAT_LOW, /*发布的事件标志*/
(OS_OPT) OS_OPT_POST_FLAG_SET, /*发布事件选项(事件标志置0或事件标志置1)*/
(OS_ERR*) &err); /*错误返回码*/
/*check error*/
...
}
4 与多任务同步
通过广播信号量来使多任务同步时一种经常使用的计数,有些时候可能需要多个任务同时开始执行。虽然对单个处理器来说,一次只能执行一个任务,但是可以把它们执行的起点同步到同一个时刻(我的理解是使这些任务的优先级都相同并且它们都处于就绪状态),这种情况称为多任务同步。
(1)每个待同步的任务都需要将一个事件标志置位(并指定OS_OPT_POST_NOSCHED选项)
(2)待同步的任务需要请求信号量
(3)进行广播操作的任务必须在所有与等待同步任务的事件标志都被置位后才进行广播。
(4)当所有等待任务挂起时,广播任务可以通过广播信号量的方法同步所有等待的任务。
注:等待被同步的任务需先设置事件标志再请求信号量(即向OSFlagPost(), 在OSSemPend())
最后
以上就是年轻白昼为你收集整理的μC/OS-III_任务同步的全部内容,希望文章能够帮你解决μC/OS-III_任务同步所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复