• ucos(九)互斥锁和死锁


    一、概述

    互斥锁,亦称:互斥信号量。
      在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个任务(线程)访问该对象(任务之间访问到相同的函数、相同的全局变量)。某个任务得到互斥锁后,就可以访问共享资源,其他任务等待该任务释放互斥锁才能进行访问。
      何时可以用普通信号量替代互斥锁?如果没有任务对共享资源访问有截止的时间,那么普通信号量可以替代互斥锁;反之则必须使用互斥锁。因为前者会造成无界优先级反转,后者却不会。
    0
    void task(void *parg)
    {
    	while(1)
    	{
    		加锁
    		访问共享资源
    		解锁(立即)
    
    		.....
    		加锁
    		访问共享资源
    		解锁(立即)
    		....
    
    	}
    
    }  

    二、函数接口

    1.创建互斥锁

    void  OSMutexCreate (OS_MUTEX  *p_mutex,
                         CPU_CHAR  *p_name,
                         OS_ERR    *p_err)
    
    参数:
    • p_mutex,互斥锁对象
    • p_name,互斥锁名字
    • p_err,返回错误码,没有错误的就返回OS_ERR_NONE
     
    返回值:无

     

    2.等待互斥锁

     
    若等待成功,则锁定共享资源
     
    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,则OSMutexPend就会直接返回而不再等待互斥锁被释放。
    • p_ts,用于记录等待互斥锁花了多长时间,默认写NULL,不记录。
    • p_err,返回错误码,没有错误的就返回OS_ERR_NONE
     
    说明:
    • 如果占有互斥锁是一个较低优先级多任务,那么UCOSIII就会临时提升它的优先级,使得其等于此时想要获取互斥锁的任务优先级。
     

    3.释放互斥锁,解锁

     
    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_ERR_NONE。
     

    三、死锁(或抱死)

      死锁(dead lock)也称做抱死(deadly embrace),指两个任务无限制地互相等待对方控制着的资源。
    假设任务T1正独占资源R1,任务T2正独占资源R2,示例代码如下:
    void T1(void *parg)
    {
    	while(1)
    	{
    		(1)等待事件发生
    		
    		(2)请求互斥锁M1
    		
    		(3)访问共享资源R1
    		
        		:
        		:
    		(4)-------   中断!
        		:
        		:
    
    		(8)请求互斥锁M2
    		
    		(9)访问共享资源R2			
    	}
    }
    
    
    void T2(void *parg)
    {
    	while(1)
    	{
    		等待事件发生
    		
    		(5)请求互斥锁M2
    		
    		(6)访问共享资源R2
    		
        		:
        		:
    
    		(7)请求互斥锁M1
    		
    		访问共享资源R1			
    	}
    }
     
    (1)假设任务T1具有最高优先级,且其等待的事件发生了,所以任务1开始运行。
    (2)任务T1运行并请求获得互斥锁M1
    (3)任务T1获得M1并访问共享资源R1
    (4)一个中断发生了,导致具有比任务T1更高优先级的T2获得了CPU的使用权。
    (5)该中断是任务T2等待的事件,故任务T2继续运行。
    (6)任务T2继续运行,请请求获得互斥锁M2以访问共享资源R2。
    (7)任务T2想要获得互斥锁M1,但此时UCOSIII知道此时M1被任务T1占用着。
    (8)任务T2无法继续运行,UCOSIII做任务切换转而运行任务T1.
    (9)任务T1想要获取互斥锁M2,但M2却被任务T2占有了。此时两个任务便死锁了,谁也无法继续运行,因为谁也无法获取对方的资源。
     
    避免出现死锁的方法,让每个任务都:
    • 先得到全部需要的资源,再做下一个动作
    • 用相同的顺序申请多个资源
    • 在调用请求互斥锁的函数时设定超时时间
     
    以相同的顺序先得到全部需要的资源来避免死锁的问题,示例代码1如下:
    void T1(void *parg)
    {
    	while(1)
    	{
    		等待事件发生
    		
    		请求互斥锁M1
    		
    		请求互斥锁M2	
    		
    		访问共享资源R1
    
    		访问共享资源R2	
      
      		释放互斥锁M1
    		
    		释放互斥锁M2	  		
    	}
    }
    
    
    void T2(void *parg)
    {
    	while(1)
    	{
    		等待事件发生
    		
    		请求互斥锁M1
    		
    		请求互斥锁M2	
    		
    		访问共享资源R1
    
    		访问共享资源R2	
      
    		释放互斥锁M1
    		
    		释放互斥锁M2		
    	}
    }
    以相同的顺序获取资源来避免死锁的问题,
     
    示例代码2如下:
    void T1(void *parg)
    {
    	while(1)
    	{
    		等待事件发生
    		
    		请求互斥锁M1
    		
    		访问共享资源R1
    		
     		释放互斥锁M1
        
    		请求互斥锁M2	
    
    		访问共享资源R2	
      
     		释放互斥锁M2   		
    	}
    }
    
    void T2(void *parg)
    {
    	while(1)
    	{
    		等待事件发生
    		
    		请求互斥锁M1
    		
    		访问共享资源R1
    		
     		释放互斥锁M1
        
    		请求互斥锁M2	
    
    		访问共享资源R2	
      
     		释放互斥锁M2  		
    	}
    } 

    四、实例例程

    1、死锁

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "includes.h"
    
    //任务1控制块
    OS_TCB Task1_TCB;
    
    void task1(void *parg);
    
    CPU_STK task1_stk[128];			//任务1的任务堆栈,大小为128字,也就是512字节
    
    
    
    //任务2控制块
    OS_TCB Task2_TCB;
    
    void task2(void *parg);
    
    CPU_STK task2_stk[128];			//任务2的任务堆栈,大小为128字,也就是512字节
    
    
    OS_MUTEX	g_mutex_1;			//互斥锁1
    
    OS_MUTEX	g_mutex_2;			//互斥锁1
    
    
    void res1(void)
    {
    	volatile uint32_t i=0x50;
    	
    	while(i--)
    	{
    		delay_ms(10);
    	}
    }
    
    
    void res2(void)
    {
    	volatile uint32_t i=0x50;
    	
    	while(i--)
    	{
    		delay_ms(10);
    	}
    }
    
    //主函数
    int main(void)
    {
    	OS_ERR err;
    
    	systick_init();  													//时钟初始化
    	
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);						//中断分组配置
    	
    	usart_init(9600);  				 									//串口初始化
    	
    	LED_Init();         												//LED初始化	
    
    
    	//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
    	OSInit(&err);
    
    
    	//创建任务1
    	OSTaskCreate(	(OS_TCB *)&Task1_TCB,									//任务控制块,等同于线程id
    					(CPU_CHAR *)"Task1",									//任务的名字,名字可以自定义的
    					(OS_TASK_PTR)task1,										//任务函数,等同于线程函数
    					(void *)0,												//传递参数,等同于线程的传递参数
    					(OS_PRIO)6,											 	//任务的优先级6		
    					(CPU_STK *)task1_stk,									//任务堆栈基地址
    					(CPU_STK_SIZE)128/10,									//任务堆栈深度限位,用到这个位置,任务不能再继续使用
    					(CPU_STK_SIZE)128,										//任务堆栈大小			
    					(OS_MSG_QTY)0,											//禁止任务消息队列
    					(OS_TICK)0,												//默认时间片长度																
    					(void  *)0,												//不需要补充用户存储区
    					(OS_OPT)OS_OPT_TASK_NONE,								//没有任何选项
    					&err													//返回的错误码
    				);
    					
    	if(err!=OS_ERR_NONE)
    	{
    		printf("task 1 create fail
    ");
    		
    		while(1);
    	
    	}
    
    
    	//创建任务2
    	OSTaskCreate(	(OS_TCB *)&Task2_TCB,									//任务控制块
    					(CPU_CHAR *)"Task2",									//任务的名字
    					(OS_TASK_PTR)task2,										//任务函数
    					(void *)0,												//传递参数
    					(OS_PRIO)6,											 	//任务的优先级6		
    					(CPU_STK *)task2_stk,									//任务堆栈基地址
    					(CPU_STK_SIZE)128/10,									//任务堆栈深度限位,用到这个位置,任务不能再继续使用
    					(CPU_STK_SIZE)128,										//任务堆栈大小			
    					(OS_MSG_QTY)0,											//禁止任务消息队列
    					(OS_TICK)0,												//默认时间片长度																
    					(void  *)0,												//不需要补充用户存储区
    					(OS_OPT)OS_OPT_TASK_NONE,								//没有任何选项
    					&err													//返回的错误码
    				);
    					
    	if(err!=OS_ERR_NONE)
    	{
    		printf("task 2 create fail
    ");
    		
    		while(1);
    	
    	}
    	
    	//创建互斥锁1
    	OSMutexCreate(&g_mutex_1,"g_mutex_1",&err);
    	
    	OSMutexCreate(&g_mutex_2,"g_mutex_2",&err);
    	//启动OS,进行任务调度
    	OSStart(&err);
    					
    					
    	printf(".......
    ");
    					
    	while(1);
    	
    }
    
    
    void task1(void *parg)
    {
    
    	OS_ERR err;
    
    	printf("task1 is create ok
    ");
    
    	while(1)
    	{
    
    		OSMutexPend(&g_mutex_1,0,OS_OPT_PEND_BLOCKING,NULL,&err);
    		
    		printf("[task1]access res1 begin
    ");
    		res1();
    		printf("[task1]access res1 end
    ");
    		
    		OSMutexPend(&g_mutex_2,0,OS_OPT_PEND_BLOCKING,NULL,&err);		
    		printf("[task1]access res2 begin
    ");
    		res2();
    		printf("[task1]access res2 end
    ");		
    
    		OSMutexPost(&g_mutex_1,OS_OPT_POST_NONE,&err);
    		OSMutexPost(&g_mutex_2,OS_OPT_POST_NONE,&err);		
    	}
    }
    
    
    void task2(void *parg)
    {
    	OS_ERR err;
    	
    	printf("task2 is create ok
    ");
    
    	while(1)
    	{
    		OSMutexPend(&g_mutex_2,0,OS_OPT_PEND_BLOCKING,NULL,&err);
    		
    		printf("[task2]access res2 begin
    ");
    		res2();
    		printf("[task2]access res2 end
    ");
    		
    		OSMutexPend(&g_mutex_1,0,OS_OPT_PEND_BLOCKING,NULL,&err);	
    		
    		printf("[task2]access res1 begin
    ");
    		res1();
    		printf("[task2]access res1 end
    ");		
    
    		OSMutexPost(&g_mutex_1,OS_OPT_POST_NONE,&err);
    		OSMutexPost(&g_mutex_2,OS_OPT_POST_NONE,&err);
    	}
    } 
     
    执行结果:
    由打印信息可知,程序处于死锁状态。
     

    2、避免死锁

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "includes.h"
    
    //任务1控制块
    OS_TCB Task1_TCB;
    
    void task1(void *parg);
    
    CPU_STK task1_stk[128];			//任务1的任务堆栈,大小为128字,也就是512字节
    
    
    
    //任务2控制块
    OS_TCB Task2_TCB;
    
    void task2(void *parg);
    
    CPU_STK task2_stk[128];			//任务2的任务堆栈,大小为128字,也就是512字节
    
    
    OS_MUTEX	g_mutex_1;			//互斥锁1
    
    OS_MUTEX	g_mutex_2;			//互斥锁1
    
    
    void res1(void)
    {
    	volatile uint32_t i=0x50;
    	
    	while(i--)
    	{
    		delay_ms(10);
    	}
    }
    
    
    void res2(void)
    {
    	volatile uint32_t i=0x50;
    	
    	while(i--)
    	{
    		delay_ms(10);
    	}
    }
    
    //主函数
    int main(void)
    {
    	OS_ERR err;
    
    	systick_init();  													//时钟初始化
    	
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);						//中断分组配置
    	
    	usart_init(9600);  				 									//串口初始化
    	
    	LED_Init();         												//LED初始化	
    
    
    	//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
    	OSInit(&err);
    
    
    	//创建任务1
    	OSTaskCreate(	(OS_TCB *)&Task1_TCB,									//任务控制块,等同于线程id
    					(CPU_CHAR *)"Task1",									//任务的名字,名字可以自定义的
    					(OS_TASK_PTR)task1,										//任务函数,等同于线程函数
    					(void *)0,												//传递参数,等同于线程的传递参数
    					(OS_PRIO)6,											 	//任务的优先级6		
    					(CPU_STK *)task1_stk,									//任务堆栈基地址
    					(CPU_STK_SIZE)128/10,									//任务堆栈深度限位,用到这个位置,任务不能再继续使用
    					(CPU_STK_SIZE)128,										//任务堆栈大小			
    					(OS_MSG_QTY)0,											//禁止任务消息队列
    					(OS_TICK)0,												//默认时间片长度																
    					(void  *)0,												//不需要补充用户存储区
    					(OS_OPT)OS_OPT_TASK_NONE,								//没有任何选项
    					&err													//返回的错误码
    				);
    					
    	if(err!=OS_ERR_NONE)
    	{
    		printf("task 1 create fail
    ");
    		
    		while(1);
    	
    	}
    
    
    	//创建任务2
    	OSTaskCreate(	(OS_TCB *)&Task2_TCB,									//任务控制块
    					(CPU_CHAR *)"Task2",									//任务的名字
    					(OS_TASK_PTR)task2,										//任务函数
    					(void *)0,												//传递参数
    					(OS_PRIO)6,											 	//任务的优先级6		
    					(CPU_STK *)task2_stk,									//任务堆栈基地址
    					(CPU_STK_SIZE)128/10,									//任务堆栈深度限位,用到这个位置,任务不能再继续使用
    					(CPU_STK_SIZE)128,										//任务堆栈大小			
    					(OS_MSG_QTY)0,											//禁止任务消息队列
    					(OS_TICK)0,												//默认时间片长度																
    					(void  *)0,												//不需要补充用户存储区
    					(OS_OPT)OS_OPT_TASK_NONE,								//没有任何选项
    					&err													//返回的错误码
    				);
    					
    	if(err!=OS_ERR_NONE)
    	{
    		printf("task 2 create fail
    ");
    		
    		while(1);
    	
    	}
    	
    	//创建互斥锁1
    	OSMutexCreate(&g_mutex_1,"g_mutex_1",&err);
    	
    	OSMutexCreate(&g_mutex_2,"g_mutex_2",&err);
    	//启动OS,进行任务调度
    	OSStart(&err);
    					
    					
    	printf(".......
    ");
    					
    	while(1);
    	
    }
    
    
    void task1(void *parg)
    {
    
    	OS_ERR err;
    
    	printf("task1 is create ok
    ");
    
    	while(1)
    	{
    
    		OSMutexPend(&g_mutex_1,0,OS_OPT_PEND_BLOCKING,NULL,&err);
    		OSMutexPend(&g_mutex_2,0,OS_OPT_PEND_BLOCKING,NULL,&err);	
    		
    		printf("[task1]access res1 begin
    ");
    		res1();
    		printf("[task1]access res1 end
    ");
    		
    			
    		printf("[task1]access res2 begin
    ");
    		res2();
    		printf("[task1]access res2 end
    ");		
    
    		OSMutexPost(&g_mutex_1,OS_OPT_POST_NONE,&err);
    		OSMutexPost(&g_mutex_2,OS_OPT_POST_NONE,&err);		
    	}
    }
    
    
    void task2(void *parg)
    {
    	OS_ERR err;
    	
    	printf("task2 is create ok
    ");
    
    	while(1)
    	{
    		
    		OSMutexPend(&g_mutex_1,0,OS_OPT_PEND_BLOCKING,NULL,&err);
    		OSMutexPend(&g_mutex_2,0,OS_OPT_PEND_BLOCKING,NULL,&err);
    		printf("[task2]access res2 begin
    ");
    		res2();
    		printf("[task2]access res2 end
    ");
    		
    	
    		
    		printf("[task2]access res1 begin
    ");
    		res1();
    		printf("[task2]access res1 end
    ");		
    
    		OSMutexPost(&g_mutex_1,OS_OPT_POST_NONE,&err);
    		OSMutexPost(&g_mutex_2,OS_OPT_POST_NONE,&err);
    	}
    }
    

    执行结果

    task1执行完毕,task2执行,如此循环..。

     

     

     
     
     
     
     
  • 相关阅读:
    在MVC3 中给HtmlHelper 添加扩展(Extension)来消除魔鬼代码
    NHibernate中使用IQueryOver时,如何添加或(OR)条件
    用@Html.EditorFor在MVC3中封装带表单(Form)提交的分页控件(通用代码)
    .NET MVC3使用CheckBox List(复选框列表)的简单方法
    Castle Windsor的MVC3的例子在最新版本(3.0Beta)上编译不过去的解决办法
    初级编程:编程巧妙注释【附图讲解】
    布同:如何循序渐进学习Python语言
    <转>:写给初学者的话《学习程序之路》
    初级编程:一层循环如何依次遍历二维数组【附代码】
    赛班S60的Python平台的源代码
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15313955.html
Copyright © 2020-2023  润新知