zoukankan      html  css  js  c++  java
  • ucos(四)共享资源保护

    一、共享资源

    典型的共享资源有:变量(静态或全局)、数据结构体、RAM中的表格、I/O设备中的寄存器、多个任务访问的函数等。
    共享资源的可靠访问,任务必须对数据具有独享权变得极其重要,否则将可能导致任务间的竞争与数据损坏。
    最常用的独占共享资源的方法有以下几种:
    • 关中断
    • 禁止任务调度
    • 使用信号量
    • 使用互斥型信号量(互斥锁)
    独占共享资源的方法,取决于代码访问共享资源的速度,即占用共享资源的时间(重点),具体使用说明如下表:

    1、关/开中断

    和CPU 相关的操作,其相关代码被放在与CPU相关的文件中(见CPU.H)。uC/OS-III 中与CPU相关的模块叫做uC/CPU。每种架构的CPU 都需要设置相适应的uC/CPU 文件。

     

    只有这种方法才能任务和中断程序共享资源。只要关中断的时间不比系统本身的关中断时间长,就不会影响到系统的中断延时。

    2、锁调度器

    如果任务不需要和ISR 共享资源,就可以通过锁调度器来访问共享资源。
    实际上就是禁止任务调度已达到资源的独占

     

    注意,只有调度器被锁,中断是使能的,如果在处理临界段时中断发生,ISR程序就会被执行。在ISR 的末尾,uC/OS-III会返回原任务(即使ISR中有高优先级任务被就绪)。

    支持嵌套250 级。当OSSchedUnlock()与OSSchedLock()被调用的次数相同时调度器才被锁调度器影响了抢占式内核的初衷,内核行为实际与不可剥夺的内核已经是一样的了。

    3、信号量

    一种上锁机制,代码需要获取对应的钥匙才能访问共享资源(继续执行),我的理解是实际上只是对访问共享资源区的代码段进行上锁,如果没有获取到钥匙(信号量)则等待无法继续执行,只有别的任务施放了信号量之后才能继续执行下去。

    二进制信号量和计数型信号量:

      二进制信号量只能取0和1两个值,计数型信号量的信号量值大于1,计数型信号量的范围由OS_SEM_CTR决定,OS_SEM_CTR可以为8位,16位和32位,取值范围分别为:0~255,0~65535和0~4294967295。

      二值信号量用于那些一次只能一个任务使用的资源,比如I/O设备,打印机,数型信号量用于某些资源可以同时被几个任务所使用,比如一个缓存池有10个缓存块,那么同时最多可以支持10个任务来使用内存池。应用中可以使用任意数量的信号量,但是一般用于IO保护,很多情况下,访问一个简短的共享资源时不推荐使用信号量,请求和释放信号量会消耗CPU时间。

     (1)定义一个信号量
    OS_SEM XXX 
    
    (2)创建一个信号量
    void  OSSemCreate (OS_SEM *p_sem, //信号量结构体,其中OS_SEM_CTR Ctr表示信号量的值
                       CPU_CHAR *p_name,//名字
                       OS_SEM_CTR cnt,   //信号量初始值
                       OS_ERR *p_err)
    
    (3)删除一个信号量
    OS_OBJ_QTY  OSSemDel(OS_SEM *p_sem,
                         OS_OPT opt, 
         //1.NO_PEND 仅当没有信号请求的时候才删除
         //2.ALWAYS 直接删除,不管有没有信号请求
                         OS_ERR *p_err)
    
    (4)请求/等待一个信号量  
    OS_SEM_CTR  OSSemPend(OS_SEM *p_sem, 
                          OS_TICK timeout,//等待的时间,如果为0:一直等待下去
                          OS_OPT opt,
             //1.暂时无效直接挂起 OS_OPT_PEND_BLOCKING
             //2.无效直接返回OS_OPT_PEND_NON_BLOCKING
                          CPU_TS *p_ts,//时间戳:记录接受信号量的时刻,0或者null则不需要时间戳。
                          OS_ERR *p_err)
    
    (5)取消等待
    OS_OBJ_QTY  OSSemPendAbort(OS_SEM *p_sem,
                               OS_OPT opt,
                 //1. 仅终止等待该信号量的最高优先级任务
                 //2. 中止所有的等待该信号量的任务
                 //3. 禁止任务调度
                               OS_ERR *p_err)
    
    (6)释放一个信号量
    OS_SEM_CTR OSSemPost(OS_SEM *p_sem,
                         OS_OPT opt,
                  //1. 仅发送给等待该信号量的最高优先级任务
                  //2. 发送给所有的等待该信号量的任务
                  //3. 禁止任务调度
                         OS_ERR *p_err)
    
    (7)步骤:
    • 先定义OS_SEM。
    • Create创建信号量.
    • OSSemPend等待信号量.
    • OSSemPost释放信号量.

    4、互斥信号量

      使用信号量访问共享资源会有优先级反转问题:高优先级A任务执行访问共享资源的时候被迫等待低优先级任务D释放信号量,此时会发生任务切换到低优先级。如果此时又有高一级的任务B中断了D的任务,虽然B任务比A任务优先级更低,也会发生任务调度。使得高优先级任务A实际上被拉到占用信号量的低优先级任务D同一优先级别,违背了实时RTOS。

    互斥信号量实际解决方法:将低优先级任务D的优先级临时提升至任务A同一优先级,等信号量释放后,再将任务D的优先级恢复,这样任务切换只在这两个任务之间执行(如果有比A更高的还是会切换)。

     (1)创建互斥信号量
    void  OSMutexCreate(OS_MUTEX  *p_mutex,//指向互斥信号量的控制块
                        CPU_CHAR  *p_name, //互斥信号量名字
                        OS_ERR    *p_err)//调用此函数后返回的错误码
    
    (2)请求/等待互斥信号量
    void  OSMutexPend(OS_MUTEX  *p_mutex, //指向互斥信号量的控制块
                      OS_TICK timeout,//指定等待互斥信号量的超时节拍,超时继续执行,0位一直等待
                      OS_OPT opt,//是否阻塞
                 //OS_OPT_PEND_BLOCKING无效挂起
                 //OS_OPT_PEND_NON_BLOCKING无效直接返回
                      CPU_TS *p_ts,//时间戳
                      OS_ERR *p_err)//错误码
    
    (3)释放互斥信号量
    void  OSMutexPost(OS_MUTEX *p_mutex,//指向互斥信号量
    
                      OS_OPT opt,//指定是否进行任务调度操作
        //OS_OPT_POST_NONE不指定特定的选项
        //OS_OPT_POST_NO_SCHED 禁止在本函数内执行任务调度
    
                      OS_ERR *p_err)//错误码
    
    (4)步骤:
    • 先定义OS_MUTEX xxxx
    • Create创建信号量
    • OSMutexPend等待信号量
    • OSMutexPost释放信号量

    5、死锁

    任务1需要的资源要等到任务2释放,任务2的资源需要等待任务1释放,造成死锁。

    (1)假定任务T1 所等待的事情发生,任务1被执行。

    (2)任务T1 申请M1。(mutex 1)

    (3)任务T1 访问资源R1。
    (4)中断发生,中断中使能了任务T2。由于任务T2 的优先级高于任务T1,CPU 切换到任务T2。
    (5)该中断即是任务T2 所等待的事件。
    6)任务T2 申请M2 并占用资源R2。

    (7)任务T2 申请M1,但是M1 已被任务T1 占用。任务T2被挂起。

    (8)uC/OS-III 切换到任务T1。

    (9)此时任务T1 申请M2,但是M2 已经被任务T2 占用。

    此时,两个任务互相等待,这就算死锁。

    用以下方式防止死锁:
    • 同一个时间不要申请多于一个mutex
    • 不要直接地申请mutex(该申请放到器件驱动中和可重入函数中)
    • 在处理之前先获得全部所需要的mutex
    • 任务间以同样的顺序申请资源

    当申请信号量或mutex 时允许设置时间期限,这样能防止死锁,但是同样的死锁可能稍后再次出现。

    二、选择题

    1.以下请问使用( A )方式保护最为合适。

    void T1(void *parg)
    {
    	while(1)
    	{ 
             ...........
    	        喂狗	
             ...........		
    	}
    }
    
    A. 关中断、开中断    B.给调度器上锁、解锁   C.信号量   D.互斥锁
     

    2.以下请问使用( B )方式保护最为合适。

    uint32 g=0;
    void T1(void *parg)
    {
    	while(1)
    	{ 
             ...........
    	        g++;	
             ...........		
    	}
    }
    
    void T2(void *parg)
    {
       uint32_t a=0;
    	while(1)
    	{ 
             ...........
    	        a = g;	
             ...........		
    	}
    }
    
    A. 关中断、开中断   B.给调度器上锁、解锁   C.信号量   D.互斥锁

    3.以下请问使用( A )方式保护最为合适。

    uint32 g=0;
    
    void T1(void *parg)
    {
    	while(1)
    	{ 
             ...........
    	        g++;	
             ...........		
    	}
    }
    
    void T2(void *parg)
    {
       uint32_t a=0;
    	while(1)
    	{ 
             ...........
    	        a = g;	
             ...........		
    	}
    }
    
    void USART1_IRQHandler(void)
    {
        
             ...........
    	        g = USART_ReceiveData(USART1);	
             ...........  
        
    }
    
    A. 关中断、开中断   B.给调度器上锁、解锁   C.信号量   D.互斥锁

    4.以下请问使用( C )方式保护最为合适。

    void T1(void *parg)
    {
    	while(1)
    	{ 
             ...........
             printf("hello teacher.wen
    ");	
             ...........		
    	}
    }
    
    void T2(void *parg)
    {
    
    	while(1)
    	{ 
             ...........
             printf("good bye teacher.wen
    ");	
             ...........		
    	}
    }
    

     

    A. 关中断、开中断   B.给调度器上锁、解锁   C.信号量   D.互斥锁

    5.以下请问使用( D )方式保护最为合适。

    void T1(void *parg)
    {
    	while(1)
    	{ 
             ...........
             printf("hello teacher.wen
    ");	
             ...........		
    	}
    }
    
    void T2(void *parg)
    {
    
    	while(1)
    	{ 
             ...........
             printf("good bye teacher.wen
    ");	
             ...........		
    	}
    }
    
    void T3(void *parg)
    {
    	while(1)
    	{ 
             ...........
             printf("he's teacher.wen
    ");	
             ...........		
    	}
    }
    

     

    A. 关中断、开中断   B.给调度器上锁、解锁   C.信号量   D.互斥锁

    三、辨析题

    1.请分析以下代码。

    void T1(void *parg)
    {
    
    	while(1)
    	{ 
                调度器上锁
                printf("hello teacher.wen
    ");
                delay_ms(1000)	
                调度器解锁		
    	}
    }
    
    void T2(void *parg)
    {
    	while(1)
    	{ 
                调度器上锁
                printf("good bye teacher.wen
    ");
                delay_ms(1000)	
                调度器解锁			
    	}
    }
    
    
     

    2.请分析以下代码。

    void T1(void *parg)
    {
    
    	while(1)
    	{ 
                等待互斥锁
                printf("hello teacher.wen
    ");
                OLED_ShowString(0,2,"this is task 1
    ");
                释放互斥锁		
    	}
    }
    
    void T2(void *parg)
    {
       uint8_t buf[5]={0};
    	while(1)
    	{ 
                等待互斥锁
                printf("good bye teacher.wen
    ");
                dht11_read(buf)
                ......
                释放互斥锁			
    	}
    }

    3.请分析以下代码。

    void T1(void *parg)
    {
    
    	while(1)
    	{ 
                等待互斥锁
                printf("hello teacher.wen
    ");
                OLED_ShowString(0,2,"this is task 1
    ");
                释放互斥锁		
    	}
    }
    
    void T2(void *parg)
    {
       uint8_t buf[5]={0};
    	while(1)
    	{ 
                等待互斥锁
                printf("good bye teacher.wen
    ");
                dht11_read(buf)
                ......
                释放互斥锁			
    	}
    }

    4.请分析以下代码。

    void app_task_init(void *parg)
    {
        创建信号量,初值为0
    }
    
    void T1(void *parg)
    {
    	while(1)
    	{ 
                等待信号量
                printf("hello teacher.wen
    ");
                释放信号量		
    	}
    }
    
    void T2(void *parg)
    {
    	while(1)
    	{ 
                等待信号量
                printf("good bye teacher.wen
    ");
                释放信号量			
    	}
    }  

     

     

  • 相关阅读:
    leetcode刷题-54螺旋矩阵
    leetcode刷题-53最大子序和
    leetcode刷题-52N皇后2
    leetcode刷题-51N皇后
    leetcode刷题-50Pow(x, n)
    leetcode刷题-37解数独
    leetcode刷题-49字母异位词分组
    leetcode刷题-48旋转图像
    数据结构—B树、B+树、B*树
    LeetCode–旋转数组的最小数字
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15143630.html
Copyright © 2011-2022 走看看