• 嵌入式实时操作系统的设计与开发(信号量学习)


    信号量

    除了临界点机制、互斥量机制可实现临界资源的互斥访问外,信号量(Semaphore)是另一选择。

    信号量与互斥量的区别

    • 对于互斥量来说,主要应用于临界资源的互斥访问,并且能够有效地避免优先级反转问题。
    • 对于信号量而言,它虽然也能用于临界资源的互斥访问,但是不能处理优先级反转问题。

    也正因为信号量没有考虑优先级反转问题,所以相对于互斥量来说是一种轻量级的实现方式,比互斥量耗费更少的CPU资源。
    此外,信号量除了用于互斥,还可以用于处理不同线程之间的同步问题,而互斥量却不行。

    针对上述情况,有三种类型的信号量,按照功能来分,可以分为线程对临界资源互斥访问的互斥信号量、用于线程间同步的信号量、控制系统中临界资源的多个实例使用的计数信号量。

    用于同步的信号量其初始值在创建信号量时设置为0,表示同步事件尚未发生。
    临界资源互斥的信号量初始值为1,表明当前没有任务获取该信号量。
    用于控制系统中临界资源的多个实例使用的计数信号量其初始值为n,表明需要管理的实例个数最大数为n,这样的信号量也称为计数信号量。

    以下例子,通过一个计数信号量和互斥信号量实现对一个有界缓冲使用的控制,这就是“生产者与消费者”问题。

    • 计数信号量FULL表示已经被填充了的数据项目。
    • 计数信号量EMPTY表示空闲数据项数目。
      以上的取值范围均为(0,n-1),其初始值分别为0,n-1。
      由于有界缓冲区是共享资源,还需要一个互斥信号量MUTEX控制生产者线程与消费者线程对它的互斥访问,其初始值为1。

    创建信号量

    acoral_evt_t *acoral_sem_create(unsigned int semNum)
    {
    	acoral_evt_t *evt;
    	evt = acoral_alloc_evt();
    	if (NULL == evt)
    	{
    		return NULL;
    	}
    	semNum = 1 - semNum;
    	evt->count = semNum;
    	evt->type = ACORAL_EVENT_SEM;
    	evt->data = NULL;
    	acoral_evt_init(evt);
    	return evt;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    初始化信号量。当静态定义信号量而不是采用指针形式定义时,内存空间已经在定义时分配,此时应当调用初始化函数acoral_sem_init()对定义过的信号量进行初始化。

    aCoralSemRetValEnum acoral_sem_init(acoral_evt_t *evt,unsigned int semNum)
    {
    	if(NULL == evt)
    	{
    		return SEM_ERR_NULL;
    	}
    	semNum = 1 - semNum;
    	evt->count = semNum;
    	evt->type = ACORAL_EVENT_SEM;
    	evt->data = NULL;
    	acoral_evt_init(evt);
    	return SEM_SUCCED;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    互斥量初始化类似,就是为acoral_evt_t各个成员赋值。
    这里需要提及的是count初始化,从传入的参数semNum可知,该变量用来表示当前信号量所控制的临界资源的实例的数量,但在具体实现时,并不是和大家想象的数字一样,如1代表有1个资源,2代表有2个资源…
    在实现时,实例数量是用“1-semNum”来表示的,此时0代表有1个资源,-1代表有两个资源,1代表已经没有资源,且有1个线程在等待该资源实例。

    申请信号量。申请信号量时需要传入两个参数:先前创建的信号量的地址,超时处理的时间。

    acoralSemRetValEnum acoral_sem_pend(acoral_evt_t *evt, unsigned int timeout)
    {
    	acoral_thread_t *cur = acoral_cur_thread;
    	if(acoral_intr_nesting)
    	{
    		return SEM_ERR_INTR;
    	}
    	if(NULL == evt)
    	{
    		return SEM_ERR_NULL;
    	}
    	if(ACORAL_EVENT_SEM != evt->type)
    	{
    		return SEM_ERR_TYPE;
    	}
    
    	//计算信号量处理
    	acoral_enter_critical();
    	/*判断是否还有可用资源,从前面的介绍可知,这里的SEM_RES_AVAI其实就是0,如果count数目小于等于0,代表有资源实例。如果count大于0,代表在等待的有多少个线程。如果有可用的资源实例,让count的数目加一后退出,表示成功申请信号量。*/
    	if((char)evt->count <= SEM_RES_AVAI)
    	{
    		evt->count++;
    		acoral_exit_critical();
    		return SEM_SUCCED;
    	}
    	//如果无可用的资源实例,让count的数目加一后,再将自身挂起,重新调度线程
    	evt->count++;
    	acoral_unrdy_thread(cur);
    	if(timeout > 0)
    	{
    		cur->delay = TIME_TO_TICKS(timeout);
    		timeout_queue_add(cur);
    	}
    	acoral_evt_queue_add(evt, cur);
    	acoral_exit_critical();
    	acoral_sched();
    	acoral_enter_critical();
    
    	//如果某个线程等待某个资源实例而又无法获取,它将被挂起,而若它希望被挂起的时间小于一个设定值timeout,还需将TCB的成员更新为timeout,并挂载到延迟队列中,如果延迟时间到,将进行相应处理
    	if(timeout > 0 && cur->Delay <= 0)
    	{
    		evt->count--;
    		acoral_evt_queue_Del(cur);
    		acoral_exit_critical();
    		return SEM_ERR_TIMEOUT;
    	}
    	timeout_queue_del(cur);
    	acoral_exit_critical();
    	return SEM_SUCCED;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    释放信号量

    acoralSemRetValEnum acoral_sem_post(acoral_evt_t *evt)
    {
    	acoral_thread_t *thread;
    
    	/* 参数检测*/
    	if (NULL == evt)
    	{
    		return SEM_ERR_NULL; /* error*/
    	}
    	if (ACORAL_EVENT_SEM != evt->type)
    	{
    		return SEM_ERR_TYPE;
    	}
    
    	acoral_enter_critical();
    	if((char)evt->count <= SEM_RES_NOVAI)
    	{
    		evt->count--;
    		acoral_exit_critical();
    		return SEM_SUCCED;
    	}
    	evt->count--;
    	thread = acoral_evt_high_thread(evt);
    	if(thread == NULL)
    	{
    		acoral_print("Err Sem post\n");
    		acoral_Exit_critical();
    		return SEM_ERR_UNDEF;
    	}
    	timeout_queue_del(thread);
    	acoral_evt_queue_del(thread);
    	acoral_rdy_thread(thread);
    	acoral_exit_critical();
    	acoral_sched();
    	return SEM_SUCCED;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    同步机制

    信号量机制不仅可以实现临界资源互斥访问,控制系统中临界资源多个实例的使用,还可以用于维护线程之间、线程和中断之间的同步。

    当信号量用来实现同步时,其初始值为0,如一个线程正等待某个I/O操作,当该I/O操作完成后,中断服务程序发出信号量,该线程得到信号量后才能继续往下执行。
    某个线程将一直处于等待状态,除非获取了其它线程发给它的信号量。

    用于互斥的信号量初始值在创建时设置为1,此时1-semNum=0,是小于等于0的,表明当前没有线程获取该信号量。
    而用于同步的信号量初始值在信号量创建时设置为0,此时1-semNum=1,是大于1的,表明同步尚未发生。

    同步信号量的实现和互斥信号量是一样的,只是创建时传入的参数决定了是用于同步还是用于互斥。

  • 相关阅读:
    命名空间提示“http://schemas.microsoft.com/xaml/behaviors”不存在Interation的解决办法
    VoLTE基础自学系列 | VoLTE短消息业务概述暨SMS over IP概述
    看一下链表结构
    尚硅谷-SpringBoot1.5.9(已过时,直接学2)
    LabVIEW编程开发天气监测系统
    调整C / C ++编译器以在多核应用程序中获得最佳并行性能:第二部分
    【飞控开发高级教程6】疯壳·开源编队无人机-AI语音控制
    自适应滤波器更新算法-EP1
    【FaceChain风格DIY手把手教程】无限场景风格LoRA与固定人物LoRA的融合(4Kstar!)
    论文写作 - CCF会议论文常用表述
  • 原文地址:https://blog.csdn.net/Caramel_biscuit/article/details/133999721