UCOSIII信号量与互斥量

10 篇文章 1 订阅
9 篇文章 0 订阅

在 UCOSIII 中有可能会有多个任务会访问共享资源,因此信号量最早用来控制任务存取共享资源,现在信号量也被用来实现任务间的同步以及任务和 ISR 间同步。在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII 引入了互斥信号量这个概念。

信号量

信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。信号量分为两种:二进制信号量与计数型信号量,二进制信号量只能取 0 和 1 两个值,计
数型信号量不止可以取 2 个值,在共享资源中只有任何可以使用信号量,中断服务程序则不能使用

二进制信号量

某一资源对应的信号量为 1 的时候,那么就可以使用这一资源,如果对应资源的信号量为0,那么等待该信号量的任务就会被放进等待信号量的任务表中。在等待信号量的时候也可以设置超时,如果超过设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。任务以“发信号”的方式操作信号量。可以看出如果一个信号量为二进制信号量的话,一次只能一个任务使用共享资源。

计数型信号量

有时候我们需要可以同时有多个任务访问共享资源,这个时候二进制信号量就不能使用了,计数型信号量就是用来解决这个问题的。比如某一个信号量初始化值为 10,那么只有前 10 个请求该信号量的任务可以使用共享资源,以后的任务需要等待前 10 个任务释放掉信号量。每当有任务请求信号量的时候,信号量的值就会减 1,直到减为 0。当有任务释放掉信号量的时候信号量的值就会加 1。

创建信号量

OSSemCreate()

void OSSemCreate ( OS_SEM *p_sem,
				   CPU_CHAR *p_name,
				   OS_SEM_CTR cnt,
				   OS_ERR *p_err)

p_sem : 指向信号量控制块,我们需要按照如下所示方式定义一个全局信号量,并将这个信号量的指针传递给函数 OSSemCreate()。
OS_SEM TestSem;
p_name: 指向信号量的名字。
cnt : 设置信号量的初始值,如果此值为 1,代表此信号量为二进制信号量,如果大于 1的话就代表此信号量为计数型信号量。
p_err : 保存调用此函数后的返回的错误码。

请求信号量

当一个任务需要独占式的访问某个特定的系统资源时,需要与其他任务或中断服务程序同步,或者需要等待某个事件的发生,应该调用函数
OSSemPend()

OS_SEM_CTR OSSemPend ( OS_SEM *p_sem,
					   OS_TICK timeout,
					   OS_OPT opt,
					   CPU_TS *p_ts,
					   OS_ERR *p_err)

p_sem : 指向一个信号量的指针。
timeout : 指定等待信号量的超时时间(时钟节拍数),如果在指定时间内没有等到信号量则允许任务恢复执行。如果指定时间为 0 的话任务就会一直等待下去,直到等到信号量。
opt : 用于设置是否使用阻塞模式,有下面两个选项。
OS_OPT_PEND_BLOCKING 指定信号量无效时,任务挂起以等待信号量。
OS_OPT_PEND_NON_BLOCKING 信号量无效时,任务直接返回。
p_ts : 指向一个时间戳,用来记录接收到信号量的时刻,如果给这个参数赋值 NULL,则说明用户没有要求时间戳。
p_err : 保存调用本函数后返回的错误码。

发送信号量

任务获得信号量以后就可以访问共享资源了,在任务访问完共享资源以后必须释放信号量,
释放信号量也叫发送信号量,使用函数 OSSemPost()发送信号量。如果没有任务在等待该信号
量的话则 OSSemPost()函数只是简单的将信号量加 1,然后返回到调用该函数的任务中继续运行。
如果有一个或者多个任务在等待这个信号量,则优先级最高的任务将获得这个信号量,然后由
调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进
行任务切换,运行这个就绪任务,
OSSemPost()

OS_SEM_CTR OSSemPost ( OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)

p_sem : 指向一个信号量的指针
opt : 用来选择信号量发送的方式。
OS_OPT_POST_1 仅向等待该信号量的优先级最高的任务发送信号量。
OS_OPT_POST_ALL 向等待该信号量的所有任务发送信号量。
OS_OPT_POST_NO_SCHED 该选项禁止在本函数内执行任务调度操作。即使
该函数使得更高优先级的任务结束挂起进入就绪状态,也不会执行任务调度,而是会在其他后续函数中完成任务调度。
p_err : 用来保存调用此函数后返回的错误码

信号量访问共享资源区实验

OS_SEM MY_SEM; //定义一个信号量,用于访问共享资源
//创建一个信号量
OSSemCreate ((OS_SEM* )&MY_SEM, //指向信号量
			 (CPU_CHAR* )"MY_SEM", //信号量名字
			 (OS_SEM_CTR )1, //信号量值为 1
			 (OS_ERR* )&err);
//任务 1 的任务函数
void task1_task(void *p_arg)
{
	OS_ERR err;
	u8 task1_str[]="First task Running!";
	while(1)
	{
		printf("\r\n 任务 1:\r\n");
		LCD_Fill(0,110,239,319,CYAN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); // 请求信号量(1)
		memcpy(share_resource,task1_str,sizeof(task1_str)); //向共享资源区拷贝数据
		delay_ms(200);
		printf("%s\r\n",share_resource); //串口输出共享资源区数据
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); // 发送信号量 (2)
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}
//任务 2 的任务函数
void task2_task(void *p_arg)
{
	OS_ERR err;
	u8 task2_str[]="Second task Running!";
	while(1)
	{
		printf("\r\n 任务 2:\r\n");
		LCD_Fill(0,110,239,319,BROWN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); // 请求信号量(3)
		memcpy(share_resource,task2_str,sizeof(task2_str)); //向共享资源区拷贝数据
		delay_ms(200);
		printf("%s\r\n",share_resource); //串口输出共享资源区数据
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); // 发送信号量 (4)
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}

任务同步实验

信号量现在更多的被用来实现任务的同步以及任务和 ISR 间的同步,信号量用于任务同步
在这里插入图片描述
图 10.4.1 中用一个小旗子代表信号量,小旗子旁边的数值 N 为信号量计数值, 表示发布信号量的次数累积值,ISR 可以多次发布信号量,发布的次数会记录为 N。一般情况下,N 的初始值是 0,表示事件还没有发生过。在初始化时,也可以将 N 的初值设为大于零的某个值,来表示初始情况下有多少信号量可用。

等待信号量的任务旁边的小沙漏表示等待任务可以设定超时时间。超时的意思是该任务只会等待一定时间的信号量,如果在这段时间内没有等到信号量,UCOSIII 就会将任务置于就绪表中,并返回错误码。

例 10-3 :创建 3 个任务,任务 A 用于创建其他两个任务和一个初始值为 0 的信号量,任务 C必须征得任务 B 的同意才能执行一次操作。
答:这个问题显然是一个任务同步的问题,在两个任务之间设置一个初值为 0 的信号量来实现两个任务的合作。任务 B 通过发信号量表示同意与否,任务 C 一直请求信号量,当信号量大于1的时候任务C才能执行接下来的操作。

OS_SEM SYNC_SEM; //定义一个信号量,用于任务同步
//创建一个信号量
OSSemCreate ((OS_SEM* )&SYNC_SEM,
			 (CPU_CHAR* )"SYNC_SEM",
			 (OS_SEM_CTR )0,
			 (OS_ERR* )&err);
//任务 1 的任务函数
void task1_task(void *p_arg)
{
	u8 key;
	OS_ERR err;
	while(1)
	{
		key = KEY_Scan(0); //扫描按键
		if(key==WKUP_PRES)
		{
			OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);// 发送信号量 (1)
			LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); // 显示信号量值 (2)
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延时 10ms
	}
}
//任务 2 的任务函数
void task2_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	while(1)
	{
		// 请求信号量
		OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); (3)
		num++;
		LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //显示信号量值
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]); //刷屏
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}

(1) 当 KEY_UP 键按下的时候调用 OSSemPost()函数发送一次信号量。
(2) 信号量 SYNC_SEM 的字段 Ctr 用来记录信号量值,我们每调用一次 OSSemPost()函数Ctr 字段就会加一,这里我们将 Ctr 的值显示在 LCD 上,来观察 Ctr 的变化。
(3) 任务 2 请求信号量 SYNC_SEM,如果请求到信号量的话就会执行任务 2 下面的代码,如果没有请求到的话就会一直阻塞函数。当调用函数 OSSemPend()请求信号量成功的话,SYNC_SEM 的字段 Ctr 就会减一,直到为 0。在任务 2 中我们也将信号量 SYNC_SEM 的字段Ctr 显示在 LCD 上,观察其变化。

优先级反转

优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破!!!z1!@坏任务的预期顺序,可能会导致严重的后果
在这里插入图片描述
(1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。
(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
(3) 任务 L 获得信号量并开始使用该共享资源。
(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
(5) 任务 H 开始运行。
(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务
L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。
(7) 任务 L 继续运行。
(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务 L
的 CPU 使用权。
(9) 任务 M 处理该处理的事。
(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。
(11) 任务 L 继续运行。
(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。
(13) 任务 H 得到该信号量并接着运行。在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级反转。

优先级反转试验
创建 4 个任务,任务 A 用于创建 B、C 和 D 这三个任务,A 还创建了一个初始值为 1的信号量 TEST_SEM,任务 B 和 D 都请求信号量 TEST_SEM,其中任务优先级从高到底分别为:B、C、D。

OS_SEM TEST_SEM; //定义一个信号量
//创建一个信号量
OSSemCreate ((OS_SEM* )&TEST_SEM,
			 (CPU_CHAR* )"TEST_SEM",
			 (OS_SEM_CTR )1, //信号量初始值为 1
			 (OS_ERR* )&err);
//高优先级任务的任务函数
void high_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(5,110,115,314); //画一个矩形
	LCD_DrawLine(5,130,115,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延时 500ms
		num++;
		printf("high task Pend Sem\r\n");
		OSSemPend(&TEST_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量(1)
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充区域
 		LED1 = ~LED1;
		OSSemPost(&TEST_SEM,OS_OPT_POST_1,&err); //释放信号量(2)
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延时 500ms
	}
}
//中等优先级任务的任务函数
void middle_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(125,110,234,314); //画一个矩形
	LCD_DrawLine(125,130,234,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充区域
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}
//低优先级任务的任务函数
void low_task(void *p_arg)
{
	static u32 times;
	OS_ERR err;
	while(1)
	{
		OSSemPend(&TEST_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量(3)
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) (4)
		{
			OSSched(); //发起任务调度
		}
		OSSemPost(&TEST_SEM,OS_OPT_POST_1,&err); //释放信号量 (5)
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}

实验结果:
在这里插入图片描述
(1)、low_task 任务获得下信号量 TEST_SEM 开始运行。
(2)、high_task 请求信号量 TEST_SEM,但是此时信号量 TEST_SEM 被任务 low_task 占用着,因此 high_task 就要一直等待,直到 low_task 任务释放信号量 TEST_SEM。
(3)、由于high_task没有请求到信号量TEST_SEM,只能一直等待,红色部分代码中high_task没有运行,而middle_task一直在运行,给人的感觉就是middle_task的任务优先级高于high_task。但是事实上 high_task 任务的任务优先级是高于 middle_task 的,这个就是优先级反转!
(4)、high_task 任务因为获得信号量 TEST_SEM 而运行从例-4 中可以看出,当一个低优先级任务和一个高优先级任务同时使用同一个信号量,而系统中还有其他中等优先级任务时。如果低优先级任务获得了信号量,那么高优先级的任务就会处于等待状态,但是,中等优先级的任务可以打断低优先级任务而先于高优先级任务运行(此时高优先级的任务在等待信号量 ,所以不能运行),这是就出现了优先级反转的现象。

互斥信号量

为了避免优先级反转这个问题,UCOSIII 支持一种特殊的二进制信号量:互斥信号量, 用它可以解决优先级反转问题
在这里插入图片描述
(1) 任务 H 与任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行中。
(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应资源的互斥型信号量。
(3) 任务 L 获得互斥型信号量并开始使用该共享资源。
(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
(5) 任务 H 开始运行。
(6) 任务 H 运行过程中也要使用任务 L 在使用的资源,考虑到任务 L 正在占用着资源,UCOSIII 会将任务 L 的优先级升至同任务 H 一样,使得任务 L 能继续执行而不被其他中等优先级的任务打断。
(7) 任务 L 以任务 H 的优先级继续运行,注意此时任务 H 并没有运行,因为任务 H 在等待任务 L 释放掉互斥信号量。
(8) 任务 L 完成所有的任务,并释放掉互斥型信号量,UCOSIII 会自动将任务 L 的优先级恢复到提升之前的值,然后 UCOSIII 会将互斥型信号量给正在等待着的任务 H。
(9) 任务 H 获得互斥信号量开始执行。
(10) 任务 H 不再需要访问共享资源,于是释放掉互斥型信号量。
(11) 由于没有更高优先级的任务需要执行,所以任务 H 继续执行。
(12) 任务 H 完成所有工作,并等待某一事件发生,此时 UCOSIII 开始运行在任务 H 或者任务 L 运行过程中已经就绪的任务 M。
(13) 任务 M 继续执行。
注意!只有任务才能使用互斥信号量(中断服务程序则不可以),UCOSIII 允许用户嵌套使用互斥型信号量,一旦一个任务获得了一个互斥型信号量,则该任务最多可以对该互斥型信号量嵌套使用 250 次,当然该任务只有释放相同的次数才能真正释放这个互斥型信号量。
在这里插入图片描述
创建互斥信号量
OSMutexCreate()

void OSMutexCreate (OS_MUTEX *p_mutex,
					CPU_CHAR *p_name,
					OS_ERR *p_err)

p_mutex : 指向互斥型信号量控制块。互斥型信号量必须有用户应用程序进行实际分配,可以使用如下所示代码。
OS_MUTEX MyMutex;
p_name : 互斥信号量的名字
p_err : 调用此函数后返回的错误码

请求互斥型信号量
当一个任务需要对资源进行独占式访问的时候就可以使用函数 OSMutexPend(),如果该互斥信号量正在被其他的任务使用,那么 UCOSIII 就会将请求这个互斥信号量的任务放置在这个互斥信号量的等待表中。任务会一直等待,直到这个互斥信号量被释放掉,或者设定的超时时间到达为止。如果在设定的超时时间到达之前信号量被释放,UCOSIII 将会恢复所有等待这个信号量的任务中优先级最高的任务。
注意!如果占用该互斥信号量的任务比当前申请该互斥信号量的任务优先级低的话,OSMutexPend()函数会将占用该互斥信号量的任务的优先级提升到和当前申请任务的优先级一样。当占用该互斥信号量的任务释放掉该互斥信号量以后,恢复到之前的优先级。

void OSMutexPend (OS_MUTEX *p_mutex,
				  OS_TICK timeout,
				  OS_OPT opt,
				  CPU_TS *p_ts,
				  OS_ERR *p_err)

p_mutex : 指向互斥信号量。
timeout : 指定等待互斥信号量的超时时间(时钟节拍数),如果在指定的时间内互斥信号量没有释放,则允许任务恢复执行。该值设置为 0 的话,表示任务将会一直等待下去,直到信号量被释放掉。
opt : 用于选择是否使用阻塞模式。
OS_OPT_PEND_BLOCKING 指定互斥信号量被占用时,任务挂起等待该互斥信号量。
OS_OPT_PEND_NON_BLOCKING 指定当互斥信号量被占用时,直接返回
任务。
注意!当设置为 OS_OPT_PEND_NON_BLOCKING,是 timeout 参数就没有意义了,应该设置为 0。
p_ts : 指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。
p_err : 用于保存调用此函数后返回的错误码。

发送互斥信号量
OSMutexPend()

void OSMutexPost (OS_MUTEX *p_mutex,
				  OS_OPT opt,
				  OS_ERR *p_err)

p_mutex: 指向互斥信号量。
opt: 用来指定是否进行任务调度操作
OS_OPT_POST_NONE 不指定特定的选项
OS_OPT_POST_NO_SCHED 禁止在本函数内执行任务调度操作。
p_err: 用来保存调用此函数返回的错误码。

互斥信号量试

OS_MUTEX TEST_MUTEX; //定义一个互斥信号量
/创建一个互斥信号量
OSMutexCreate((OS_MUTEX* )&TEST_MUTEX,
              (CPU_CHAR* )"TEST_MUTEX",
			  (OS_ERR* )&err);
//高优先级任务的任务函数
void high_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(5,110,115,314); //画一个矩形
	LCD_DrawLine(5,130,115,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延时 500ms
		num++;
		printf("high task Pend Sem\r\n");
		OSMutexPend (&TEST_MUTEX,0,\ // 请求互斥信号量 (1)
		OS_OPT_PEND_BLOCKING,0,&err);
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充区域
		LED1 = ~LED1;
		OSMutexPost(&TEST_MUTEX,OS_OPT_POST_NONE,&err);// 释放互斥信号量 (2)
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延时 500ms
	}
}
//中等优先级任务的任务函数
void middle_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(125,110,234,314); //画一个矩形
	LCD_DrawLine(125,130,234,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充区域
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}
//低优先级任务的任务函数
void low_task(void *p_arg)
{
	static u32 times;
	OS_ERR err;
	while(1)
	{
		OSMutexPend (&TEST_MUTEX,0,\ // 请求互斥信号量 (3)
		OS_OPT_PEND_BLOCKING,0,&err);
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) (4)
		{
			OSSched(); // 发起任务调度
		}
		OSMutexPost(&TEST_MUTEX,\ (5)
		OS_OPT_POST_NONE,&err);// 释放互斥信号量
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时 1s
	}
}

(1)、high_task 任务中请求互斥信号量 TEST_MUTEX。
(2)、high_task 任务中释放互斥信号量 TEST_MUTEX。
(3)、low_task 任务中请求会互斥信号量 TEST_MUTEX。
(4)、这里用来模拟 low_task 任务长时间占用互斥信号量 TEST_MUTEX。
(5)、low_task 任务中释放互斥信号量 TEST_MUTEX。
在这里插入图片描述
(1)、middle_task 任务运行。
(2)、low_task 获得互斥信号量运行。
(3)、high_task 请求信号量,在这里会等待一段时间,等待 low_task 任务释放互斥信号量。但是 middle_task 不会运行,因为由于 low_task 正在使用互斥信号量,所以 low_task 任务优先级暂时提升到了一个高优先级(比 middle_task 任务优先级高),所以 middle_task 任务不能在打断low_task 任务的运行了!
(4)、high_task 任务获得互斥信号量而运行。从上面的分析可以看出互斥信号量有效的抑制了优先级反转现象的发生。

任务内嵌信号量

前面我们使用信号量时都需要先创建一个信号量,不过在 UCOSIII 中每个任务都有自己的内嵌的信号量,这种功能不仅能够简化代码,而且比使用独立的信号量更有效。任务信号量是直接内嵌在 UCOSIII 中的,任务信号量相关代码在 os_task.c 中的。
等待任务信号量
OSTaskSemPend()

OS_SEM_CTR OSTaskSemPend ( OS_TICK timeout,
						   OS_OPT opt,
						   CPU_TS *p_ts,
						   OS_ERR *p_err)

timeout : 如果在指定的节拍数内没有收到信号量任务就会因为等待超时而恢复运行,如果 timeout 为 0 的话任务就会一直等待,直到收到信号量。
opt: 用于选择是否使用阻塞模式。
OS_OPT_PEND_BLOCKING 指定互斥信号量被占用时,任务挂起等待该互斥信号量。
OS_OPT_PEND_NON_BLOCKING 指定当互斥信号量被占用时,直接返回任务。
注意!当设置为 OS_OPT_PEND_NON_BLOCKING,是 timeout 参数就没有意义了,应该设置为 0。
p_ts : 指向一个时间戳,记录发送、终止或删除互斥信号量的时刻。
P_err: 调用此函数后返回的错误码。

发布任务信号量
OSTaskSemPost()

OS_SEM_CTR OSTaskSemPost (OS_TCB *p_tcb,
						  OS_OPT opt,
						  OS_ERR *p_err

p_tcb : 指向要用信号通知的任务的 TCB,当设置为 NULL 的时候可以向自己发送信
量。
opt: 用来指定是否进行任务调度操作
OS_OPT_POST_NONE 不指定特定的选项
OS_OPT_POST_NO_SCHED 禁止在本函数内执行任务调度操作。
p_err: 调用此函数后返回的错误码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值