七、freeRTOS_互斥量的使用

目录

1. 互斥量的理论讲解

1.1 解决优先级反转的问题

1.2 解决递归上锁/解锁的问题

 2. 互斥量的常规使用

2.1 常规使用

2.2 优先级反转的例子

2.3 使用继承(互斥量)解决优先级反转

3. 互斥量的缺陷和递归锁


互斥量就是保护临界资源,大家互斥的去使用这些资源。

1. 互斥量的理论讲解

  • 使用方法

  • 跟二进制信号量的对比

    • 能解决优先级反转的问题

    • 能解决递归上锁/解锁的问题

1.1 解决优先级反转的问题

在信号量中,A使用Take函数,然后printf,B发现A没有Give,所以B阻塞在哪里,等到A Give之后,B才来Take,使用二进制信号量如此完美的解决了该问题,但是你在看看下面这个场景!!!

 A在Take之后,进行printf,这时候C给Give了,然后D又来Take了,这时候A、D都在使用串口进行打印,所以乱套了,还是存在风险。

 本来举上面两个例子,就想说明对于临界资源应该是谁上锁,谁来解锁,但是很不幸的是,FreeRTOS的互斥锁,并没有在代码上实现这点,那后续出现上述的问题怎么办呢?答案是只能凭借程序猿的自觉性,写出完美的代码,既然没有实现这一点你讲这么多干啥呢?但是它还有如下的优势:

  • 跟二进制信号量的对比

    • 能解决优先级反转的问题

    • 能解决递归上锁/解锁的问题

 假设有A、B、C三个任务,优先级分别是1、2、3。

图1:假设A先运行,获得了锁lock,然后B在运行,B的优先级比A高,可以抢占A,然后在轮到C在运行,在C函数中,它也想获得那把锁,于是它调用lock函数,因为锁已经被A使用了,所以进入到阻塞状态,现在轮到了B运行,在B运行的过程中,假设它一直没有放弃cpu资源,在这个过程中,A一直没有办法执行,在这种情况下,A的优先级最低,C的优先级最高,优先级最高的C被B给抢占了,这就是优先级高的程序反而不能被执行,这种现象就是优先级反转。

那怎么解决这个问题,解决这个问题的方法就是优先级继承。

图2:假设A先运行,获得了锁lock,然后B在运行,B的优先级比A高,可以抢占A,然后在轮到C在运行,在C函数中,它也想获得那把锁,于是它调用lock函数,因为锁已经被A使用了,所以进入到阻塞状态,C在调用lock的时候它还会做优先级继承,然后A的优先级就变成了3,A继承了C的优先级,这时候运行的不是B,运行的是A,A运行完之后,它进行unlock,释放这个互斥量,这时候A的优先级又变了1,然后轮到C在执行。第一次A lock的时候没有问题,第二次C lock的时候没有办法获得互斥量,所以提升C的优先级,在这个过程中C的优先级并没有被B来反转,这就是优先级继承。

1.2 解决递归上锁/解锁的问题

A先Take ,此时已经lock了,val也变为0了,这时候xxxlib()函数也Take了,这时候处于阻塞状态,两个人各自拿了一把锁,都在相互等待对方释放锁,此时就是死锁的状态了。

 解决该问题的,就要用到递归锁了。

递归锁除了优先级继承外,还有递归的的功能。

红色lock

        黑色lock

        黑色unlock

红色unlock

注意好层次,应该就没有问题了。

 2. 互斥量的常规使用

  • 常规使用:

    • 源码:16_freertos_example_mutex

    • 来自视频配套源码:15_freertos_example_semaphore

  • 优先级反转的例子:

    • 源码:17_freertos_example_mutex_inversion

    • 来自文档配套的源码FreeRTOS_17_mutex_inversion

  • 使用继承(互斥量)解决优先级反转

    • 源码:18_freertos_example_mutex_inheritance

    • 来自文档配套的源码FreeRTOS_18_mutex_inheritance

2.1 常规使用

二级制信号量初始值是0,创建后需要Give一次;互斥量初始值是1,创建后不需要Give一次。

static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;

static SemaphoreHandle_t xSemCalc;
static SemaphoreHandle_t xSemUART;


void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		xSemaphoreGive(xSemCalc);
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xSemaphoreTake(xSemCalc, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", sum);
	}
}

void TaskGenericFunction(void * param)
{
	while (1)
	{
		xSemaphoreTake(xSemUART, portMAX_DELAY);
		printf("%s\r\n", (char *)param);
		xSemaphoreGive(xSemUART);
		vTaskDelay(1);
	}
}


/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");
	xSemCalc = xSemaphoreCreateCounting(10, 0);
	//xSemUART = xSemaphoreCreateBinary();
	//xSemaphoreGive(xSemUART);
	
	xSemUART = xSemaphoreCreateMutex();

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

Task3、Task4互斥的使用串口,他们的信息并没有掺杂在一起。 

2.2 优先级反转的例子

在main函数中,我们创建了(低/中/高优先级)的任务,先让高优先级的任务,打印“HPTask start”之后,故意让它vTaskDelay 10ms,然后让中等优先级的任务,打印 "MPTask start"之后,故意让它vTaskDelay 30ms,这时只剩下最低优先级的任务了,在最低优先级任务里,先是获得锁,故意耗时很久,在释放锁,在vTaskDelay 10ms,这时候到高优先级任务了,打印“HPTask wait for Lock”之后,他想获得这把锁,这个锁之前已经被低优先级的任务获得了,高优先级任务进入阻塞状态,然后又轮到低优先级的任务继续执行,这时中优先级的30ms到了,中优先级任务一直执行,没有主动放弃cpu资源,于是中优先级的任务一直在执行,这时候高优先级的任务一直没有机会执行,这就是优先级反转。

怎么解决这个问题,解决问题的关键就是优先级继承。

static volatile uint8_t flagLPTaskRun = 0;
static volatile uint8_t flagMPTaskRun = 0;
static volatile uint8_t flagHPTaskRun = 0;

static void vLPTask( void *pvParameters );
static void vMPTask( void *pvParameters );
static void vHPTask( void *pvParameters );


/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;

int main( void )
{
	prvSetupHardware();
	
    /* 创建互斥量/二进制信号量/ */
    xLock = xSemaphoreCreateBinary( );
	xSemaphoreGive(xLock);


	if( xLock != NULL )
	{
		/* 创建3个任务:LP,MP,HP(低/中/高优先级)
		 */
		xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );
		xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );
		xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );

		/* 启动调度器 */
		vTaskStartScheduler();
	}
	else
	{
		/* 无法创建互斥量/二进制信号量 */
	}

	/* 如果程序运行到了这里就表示出错了,一般是内存不足 */
	return 0;
}

/*-----------------------------------------------------------*/

/*-----------------------------------------------------------*/
static void vLPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	
	uint32_t i;
	char c = 'A';

	printf("LPTask start\r\n");
	
	/* 五险循环 */
	for( ;; )
	{	
		flagLPTaskRun = 1;
		flagMPTaskRun = 0;
		flagHPTaskRun = 0;

		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		
		/* 耗时很久 */
		
		printf("LPTask take the Lock for long time");
		for (i = 0; i < 500; i++) 
		{
			flagLPTaskRun = 1;
			flagMPTaskRun = 0;
			flagHPTaskRun = 0;
			printf("%c", c + i);
		}
		printf("\r\n");
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
		
		vTaskDelay(xTicksToWait);
	}
}

static void vMPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 30UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 1;
	flagHPTaskRun = 0;

	printf("MPTask start\r\n");
	
	/* 让LPTask、vHPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 1;
		flagHPTaskRun = 0;
	}
}

static void vHPTask( void *pvParameters )
{
	const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );	

	flagLPTaskRun = 0;
	flagMPTaskRun = 0;
	flagHPTaskRun = 1;

	printf("HPTask start\r\n");
	
	/* 让LPTask先运行 */	
	vTaskDelay(xTicksToWait);
	
	/* 无限循环 */
	for( ;; )
	{	
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		printf("HPTask wait for Lock\r\n");
		
		/* 获得互斥量/二进制信号量 */
		xSemaphoreTake(xLock, portMAX_DELAY);
		
		flagLPTaskRun = 0;
		flagMPTaskRun = 0;
		flagHPTaskRun = 1;
		
		/* 释放互斥量/二进制信号量 */
		xSemaphoreGive(xLock);
	}
}

运行结果:

2.3 使用继承(互斥量)解决优先级反转

在main函数中,改为下面的代码,即可解决优先级反转的问题。 

     /* 创建互斥量/二进制信号量/ */
    //xLock = xSemaphoreCreateBinary( );
	//xSemaphoreGive(xLock);
	xLock = xSemaphoreCreateMutex( );

运行结果:

3. 互斥量的缺陷和递归锁

本节源码:19_freertos_example_mutex_recursive,源自16_freertos_example_mutex

  • 对于互斥量,本意是:谁持有,就由谁释放

  • 但是FreeRTOS并没有实现这点:A持有,B也可以释放

  • 递归锁实现了

    • 谁持有,就由谁释放

    • 递归上锁/解锁

其实在linux中,互斥锁也没有实现这一点,谁持有,谁释放 。

以下运行结果,与任务1、任务2无关,代码中没有删除任务1、任务2的相关代码。

static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;

static SemaphoreHandle_t xSemCalc;
static SemaphoreHandle_t xSemUART;


void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000000; i++)
			sum++;
		//printf("1");
		xSemaphoreGive(xSemCalc);
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		xSemaphoreTake(xSemCalc, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", sum);
	}
}

void TaskGenericFunction(void * param)
{
	int i;
	while (1)
	{
		xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);

		printf("%s\r\n", (char *)param);
		for (i = 0; i < 10; i++)
		{
			xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);
			printf("%s in loop %d\r\n", (char *)param, i);
			xSemaphoreGiveRecursive(xSemUART);
		}
		
		xSemaphoreGiveRecursive(xSemUART);
		vTaskDelay(1);
	}
}

void Task5Function(void * param)
{
	vTaskDelay(10);
	while (1)
	{
		while (1)
		{
			if (xSemaphoreTakeRecursive(xSemUART, 0) != pdTRUE)
			{
				xSemaphoreGiveRecursive(xSemUART);			
			}
			else
			{
				break;
			}
		}
		printf("%s\r\n", (char *)param);
		xSemaphoreGiveRecursive(xSemUART);
		vTaskDelay(1);
	}
}


/*-----------------------------------------------------------*/

int main( void )
{
	TaskHandle_t xHandleTask1;
		
#ifdef DEBUG
  debug();
#endif

	prvSetupHardware();

	printf("Hello, world!\r\n");
	xSemCalc = xSemaphoreCreateCounting(10, 0);
	//xSemUART = xSemaphoreCreateBinary();
	//xSemaphoreGive(xSemUART);
	
	//xSemUART = xSemaphoreCreateMutex();
	xSemUART = xSemaphoreCreateRecursiveMutex();

	xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
	xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

	xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
	xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);
	xTaskCreate(Task5Function, "Task5", 100, "Task 5 is running", 1, NULL);

	/* Start the scheduler. */
	vTaskStartScheduler();

	/* Will only get here if there was not enough heap space to create the
	idle task. */
	return 0;
}

运行结果: 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值