UCOSIII---信号量

2023-05-16

UCOSIII---信号量

  • 概述
  • PV原语
  • 函数接口
    • 创建信号量
    • 等待信号量
    • 释放信号量
  • 例程
    • 注意
  • 优先级反转
    • 概述
    • 解决方案
    • 注意事项

概述

信号量常用于任务的同步,通过该信号,就能够控制某个任务的执行,这个信号具有计数值,因此,可以称为计数信号量。
计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目。访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个任务访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个任务访问,那么第 4 个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务 1)释放掉该资源的时候,第 4 个任务才能获取到信号量从而进行资源的访问,其运作的机制具体见下图。
在这里插入图片描述
生活中常见的例子,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这里插入图片描述
在这个停车场系统中,车位是公共资源,每辆车好比一个任务(线程),看门人起的就是控制信号量的作用,描述车位数。


PV原语

1965年,荷兰学者Dijkstra提出了利用信号量机制解决进程同步问题,信号量正式成为有效的进程同步工具,现在信号量机制被广泛的用于单处理机和多处理机系统以及计算机网络中。
信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用临界区的进程数。
Dijkstra同时提出了对信号量操作的PV原语。
P原语操作的动作是:
  (1)S减1;
  (2)若S减1后仍大于或等于零,则进程继续执行;
  (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。

V原语操作的动作是:
  (1)S加1;
  (2)若相加结果大于零,则进程继续执行;
  (3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。

PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。
信号量的P、V操作,P表示申请一个资源,每次P操作使信号量减1,V是释放一个资源,每次V操作使信号量加1。信号量表示的是当前可用的资源个数,当信号量为负时,申请资源的进程(任务)就只能等待了。所以,信号量是负的多少,就表明有多少个进程(任务)申请了资源但无资源可用,只能处于等待状态。
除了访问共享资源外,亦可中断/任务控制某任务的执行,称之为“单向同步”。


函数接口

创建信号量

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

参数:
p_sem,信号量对象
p_name,信号量名字
cnt,信号量的初值
p_err,返回错误码,没有错误的就返回OS_ERR_NONE

等待信号量

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,阻塞等待
p_ts,用于记录等待信号量花了多长时间,默认写NULL,不记录。
p_err,返回错误码,没有错误的就返回OS_ERR_NONE

释放信号量

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

参数:
p_sem,信号量对象
opt,信号量接收方
.OS_OPT_POST_1,只释放信号量给最高优先级且就绪的任务,类似于UDP的点播
.OS_OPT_POST_ALL,释放给所有等待信号量的任务,类似于UDP的广播

p_err,返回错误码,没有错误的就返回OS_ERR_NONE

返回值:
当前信号量计数值。

若多个任务等待信号量,等待信号量的任务最好是优先级是一样。

例程

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "includes.h"


//任务函数
void Task1_task(void *parg);
//任务优先级
#define	Task1_TASK_PRIO	5
//任务堆栈大小
#define	Task1_TASK_SIZE	128
//任务堆栈空间
CPU_STK	Task1_TASK_STK[Task1_TASK_SIZE];
//任务控制块
OS_TCB	Task1_TCB;

//任务函数
void Task2_task(void *parg);
//任务优先级
#define	Task2_TASK_PRIO	6
//任务堆栈大小
#define	Task2_TASK_SIZE	128
//任务堆栈空间
CPU_STK	Task2_TASK_STK[Task1_TASK_SIZE];
//任务控制块
OS_TCB	Task2_TCB;


OS_SEM	g_sem;


int main(void)
{
	//UCOS系统的变量定义
	OS_ERR err;
	
	delay_init();       //延时初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
	uart_init(115200);    //串口波特率设置
	LED_Init();         //LED初始化

	//初始化UCOSIII
	OSInit(&err);
	
	//创建任务1
	OSTaskCreate((OS_TCB 	* )&Task1_TCB,		//任务控制块
				 (CPU_CHAR	* )"Task1_task", 		//任务名字
                 (OS_TASK_PTR )Task1_task, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )Task1_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&Task1_TASK_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)Task1_TASK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)Task1_TASK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_NONE, //任务选项
                 (OS_ERR 	* )&err);				//存放该函数错误时的返回值

	//创建任务2
	OSTaskCreate((OS_TCB 	* )&Task2_TCB,		//任务控制块
				 (CPU_CHAR	* )"Task2_task", 		//任务名字
                 (OS_TASK_PTR )Task2_task, 			//任务函数
                 (void		* )0,					//传递给任务函数的参数
                 (OS_PRIO	  )Task2_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&Task2_TASK_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)Task2_TASK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)Task2_TASK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,					//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,					//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,					//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_NONE, //任务选项
                 (OS_ERR 	* )&err);				//存放该函数错误时的返回值		 
	
	//创建信号量
	OSSemCreate (&g_sem,"g_sem",0,&err);
	
	//开启UCOSIII
	OSStart(&err);  
				 
	printf("OS run error.......\r\n");
	while(1);
}

void Task1_task(void *parg)
{
	OS_ERR err;
	printf("task1 is create ok\r\n");

	while(1)
	{

		printf("task1 is running ...\r\n");
		//释放信号量
		OSSemPost (&g_sem,OS_OPT_POST_1,&err);
		delay_ms(1000);
	}
}

void Task2_task(void *parg)
{
	OS_ERR err;	
	printf("task2 is create ok\r\n");

	while(1)
	{
		//等待信号量,阻塞等待信号,不记录等待时间
		OSSemPend(&g_sem,0,OS_OPT_PEND_BLOCKING,NULL,&err);
		printf("task2 is running ...\r\n");
		
		delay_ms(1000);
	}
}



任务1释放信号量,任务2等待信号量
在这里插入图片描述

注意

若有3个任务,任务1发送信号量,任务2 任务3等待信号量打印信息
如果释放信号量参数选择OS_OPT_POST_1任务2优先级高于任务3 打印顺序为
任务1》任务2》任务1》任务2》任务1》任务2… … …
如果释放信号量参数选择OS_OPT_POST_1任务2和任务3优先级相同 打印顺序为
任务1》任务2》任务1》任务3》任务1》任务2》任务1》任务3… … …

如果释放信号量参数选择OS_OPT_POST_ALL任务2和任务3优先级相同 打印顺序为
任务1》任务3》任务2》任务1》任务2》任务3》任务1》任务3》任务2… … …
广播形式的信号量,任务2和任务3都会执行

如果释放信号量参数选择OS_OPT_POST_ALL任务2优先级高于任务3 打印顺序为
任务1》任务2》任务3》任务1》任务2》任务3》任务1》任务2》任务3… … …

优先级反转

概述

在实时系统中使用信号量有可能导致一个严重的问题——优先级反转,详见《嵌入式实时操作系统UCOSIII》章节13.3.5。
优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果,下图就是一个优先级反转的例子。
在这里插入图片描述
(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完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。

在这种情况下,任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待直到任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权,使得任务H的情况更加恶化,这样就相当于任务M的优先级高于任务H,导致优先级反转。
优先级反转。简单地说,就是高优先级任务必须等待低优先级任务的完成。
在这里插入图片描述

解决方案

为了避免优先级反转这个问题,UCOSIII支持一种特殊的二进制信号量:互斥信号量,即互斥锁,用它可以解决优先级反转问题。
目前解决优先级反转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承(priority inheritance);另一种被称作优先级天花板(priority ceilings)。
A. 优先级继承(priority inheritance) :优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级。当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会临时自动被提升,以使该任务不被其他任务所打断,从而能尽快的使用完共享资源并释放,再恢复该任务原来的优先级别。
B. 优先级天花板(priority ceilings): 优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级。(这个优先级称为该资源的优先级天花板) 。
A和B的区别:优先级继承,只有当占有资源的低优先级的任务被阻塞时,才会提高占有资源任务的优先级;而优先级天花板,不论是否发生阻塞,都提升。
在UCOSIII,默认使用方案A,详细如下图。
在这里插入图片描述

注意事项

程序中可以使用任意多的信号量访问各种资源。比如一个信号量来访问一个共享的显示设备,另一个则访问一个共享的打印机;再用一个信号量来共享一些数据结构,另一个信号量来保护缓冲池等。不过一般建议信号量来访问I/O设备,而不用它来访问存储单元。
信号量常被用过了头,用信号量来处理简单的共享变量更是小题大做。请求和释放信号量的过程相当耗费时间的。尽管关中断可能会带来一些间接成本,甚至导致那些并不访问共享资源的高优先级任务也无法在第一时间被执行,但用户仍可以通过关中断、开中断来处理简单的共享变量从而提高整体工作效率。
例如两个任务共享一个32位的整数变量,一个任务给这个任务变量加1,另一个任务给这个变量清0。如果要求不管哪种操作,CPU都能在极短时间内完成,显然就不会使用信号量来满足互斥访问的条件了。每个任务只需要在操作这个任务之前关中断,之后再开中断就可以了。然而,如果这个变量是浮点数,而相应的微处理器又没有硬件的浮点协处理器,则浮点运算的时间相当长,如此一来,关中断时间长了便会影响到中断延迟,这种情况下就有必要使用信号量了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UCOSIII---信号量 的相关文章

  • virtual method but non-virtual destructor

    有虚函数 xff08 纯虚函数 xff09 就应该有虚析构函数 class Base public virtual void func 61 0 virtual Base 注意 xff0c 有 实现 class Child public B
  • STM32使用C标准库time.h里面的时间时钟函数

    标准C里面已经定义好时间日期库time h xff0c 在嵌入式里面并没有被限制使用 下面我们看一下怎样在IAR中使用time h库 有四个与时间相关的类型 xff1a clock t time t size t 和 tm 类型 clock
  • ros 学习之计算机视觉资料总结

    ros 学习之计算机视觉 ros高效编程Visual ServoingAugmented Reality xff08 AR xff09 Perception and object recognitionvisual odometry 古月居
  • 数字信号处理 离散时间信号

    1 时域离散信号 xff1a 对模拟信号进行等间隔采样 xff0c 即得到时域离散信号 xff08 时间离散 xff0c 但是幅度没有量化 xff0c 离散 xff09 1 xff09 例如 对连续信号Xa t 以等间隔T对其进行采样 xf
  • 复指数函数

    复指数信号 百度百科 复指数信号是指数信号的指数因子是复数时 xff0c 称之为复指数信号 复指数信号在物理上是不可实现的 xff0c 但是它概括了多种情况 利用复指数信号可以表示常见的普通信号 xff0c 如直流信号 指数信号 正弦信号等
  • Make 详解

    1 windows的IDE中自带Makefile 但是unix和linux需要自己写 2 makefile除了指定文件的编译顺序和规则以外 xff0c 也可以执行操作系统的命令 3 make 是一个命令工具 xff0c 是一个解释makef
  • 四旋翼无人机飞行控制算法H∞控制

    PID控制之所以被广泛应用 xff0c 就是因为它是一个无模型控制器 也就是说 xff0c 不管控制对象是什么 xff0c PID控制器的公式都是现成的 xff0c 然后你去修改三个参数试凑就行了 这对理论基础较差的工程技术人员来说用起来是

随机推荐

  • 【无标题】

    linux常用的压缩格式为 xff1a zip tar gz tar tar bz2等 xff0c 不同的方式消耗的时间和压缩比差异比较大 1 zip zip r test zip test r 表示当前test是个目录 2 解压命令 xf
  • 【无标题】

    如下是读取16bit音频raw文件的程序 xff0c 其中的short就是为了16bit准备的 xff0c 一般的固定频率的单频 xff08 1khz之类 xff09 的音频文件是固定的循环 xff0c 比如1khz是每8个点循环一次 xf
  • select 的使用

    select是在指定时间内 xff08 最后一个参数指定的时间 xff09 轮询指定fd集合的接口 1 需要包含的头文件 include lt sys time h gt select是在指定时间内轮询 xff0c 所以有时间相关的参数 i
  • KEIL修改背景色

    本篇文章借鉴 于旺园先生的 平时在用keil软件比较多 xff0c 看白色背景比较多有点伤眼睛 xff0c 现在我们来设置一下keil的背景 xff0c 来保护我们的眼睛吧 效果图 xff1a 1 打开配置界面 color amp font
  • QT表格的实现

    本文章复制于别人的博客 xff0c 是因为我怕原创者把原著删掉 xff0c 所以就复制下来了 xff0c 如有冒犯 xff0c 请多见谅 xff01 一 简介 QTableWidget是QT对话框设计中常用的显示数据表格的控件 xff0c
  • QT 表格

    在公司公示Qt开发一段时间 xff0c 表格用到不少 xff0c 所以 xff0c 今天在这做个总结 xff0c 防止以后忘记 下面为个人模拟Windows资源管理器的一个表单 xff08 写的比較粗糙 xff0c 谅解一下 xff09 一
  • 格力空调红外编码

    格力空调遥控器红外编码透析 xff08 长码 xff09 2016年11月04日 13 13 19 阅读数 xff1a 5516 格力空调遥控器 xff08 YB0F2 xff09 红外码组成如下 xff0c 按解码顺序排列 起始码 xff
  • android相机Camera.Parameters所有的参数值

    最近两天一直在看android相机的参数设置 xff0c Camera Parameters中属性值非常的多 xff0c 我索性就把就调用的flatten 方法 xff0c 将设置的参数值都打印出来了 xff0c 方便以后查看 xff0c
  • 酒店管理项目

    数据库介绍 user表 xff1a 这是一个管理员的表 xff0c 用于存储可对后台进行操作的人员信息表 xff0c 其中重要的字段包括包括主键ID xff0c 与权限等级 xff0c 密码 room表 xff1a 作为存储房间信息的表 x
  • 无人机——电池、电机、螺旋桨搭配

    1 电机 1 xff09 电机KV值 KV值是每1V的电压下电机每分钟空转的转速 例如KV800 在1V的电压下空转转速是800转每分钟 那么10V的电压下是8000转每分钟的空转转速 KV值越小 xff0c 同等电压下转速越低 xff0c
  • 姿态和位置,四旋翼的控制流程

    xfeff xfeff 姿态和位置计算 xff1a EKF 位置控制 xff1a PID 姿态控制 xff1a 姿态环是直接P控制 xff0c 姿态率是PID控制 主要是滤波算法和姿态算法还有PID算法 滤波算法主要是将获取到的陀螺仪和加速
  • Jetson nano 通过 vnc 实现远程桌面控制(已在nano实现)

    目录 一 linux环境 xff1a Ubuntu 18 04 二 nano设置VNC服务 三 设置开机自启动 四 设置静态IP 一 linux环境 xff1a Ubuntu 18 04 jetson nano用的是ubuntu18 04的
  • make px4_sitl_default gazebo出错

    出现错误时候 xff0c 可以在固件文件夹下先更新下 make clean sudo apt get update sudo apt get upgrade 错误1 xff1a 编译make px4 sitl default gazebo
  • PX4 与 MAVROS 实现offboard

    目录 一 虚拟机仿真环境 1 创建工作空间 2 创建ROS节点功能包 3 运行PX4的gazebo仿真 4 启动PX4与Mavros之间的连接 二 真机控制 1 硬件连接 2 软件设置 3 出现问题 Ubuntu xff1a 20 04 x
  • AprilTag_ros的使用

    目录 前言 一 usb摄像头的安装和使用 二 AprilTag ros包的安装 三 单目摄像机的标定 四 AprilTag ros的使用 五 其他 前言 平台 xff1a VM虚拟机 ROS版本 xff1a noetic Ubuntu xf
  • make px4_sitl_default gazebo 建立PX4仿真环境的各种坑

    前言 xff1a 平台 xff1a VM 虚拟机 Ubuntu18 04 gazebo9 一 执行组件更新总是各种中断 git submodule update init recursive 众所周知这是墙墙的故事 xff0c 所以进行了机
  • Jetson nano刷机安装系统

    1 下载系统镜像 可以到官网上下载镜像 xff0c 英伟达官网 在上面选择合适的镜像文件下载 2 格式化内存卡 需要下载格式化tf卡的工具SD Memory Card Formatter xff0c 读者可以也自行下载 格式化时候就像下图
  • 继电器的使用

    继电器使用 继电器基本说明 继电器就相当于一个开关 xff0c 接在任意线上 xff0c 通过控制信号下控制通断 xff1b 一般是断开状态 xff0c 此时线就断开了 xff0c 没导通 xff1b 在控制信号作用下继电器闭合 xff0c
  • 在树莓派上使用火焰,声音,震动,光敏传感器

    作为一个软件工程专业的学生 xff0c 对传感器等硬件的使用一直不太顺手 xff0c 而在树莓派使用Python的RPi GPIO xff0c 进行传感器等硬件的使用却是非常方便 而且使用树莓派这个网络功能强大的控制中心 xff0c 其在物
  • UCOSIII---信号量

    UCOSIII 信号量 概述PV原语函数接口创建信号量等待信号量释放信号量 例程注意 优先级反转概述解决方案注意事项 概述 信号量常用于任务的同步 xff0c 通过该信号 xff0c 就能够控制某个任务的执行 xff0c 这个信号具有计数值