精华内容
下载资源
问答
  • FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API函数,而像 RTX,uCOS-II 和 III 是仅提供...

    本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量。 二值信号量是计数信号量的一种特殊形式,即共享资源为 1 的情况。

    FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数

    信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API

    函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信

    号量和计数信号量。 当然,FreeRTOS 使用计数信号量也能够实现同样的效果。 另外,为什么叫二值信号

    量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0

    和 1 两种情况的信号量称之为二值信号量。

    函数 xSemaphoreCreateBinary

    函数原型:

    SemaphoreHandle_t xSemaphoreCreateBinary(void)

    函数描述:

    函数 xSemaphoreCreateBinary 用于创建二值信号量。

     返回值,如果创建成功会返回二值信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不

    足,无法为此二值信号量提供所需的空间会返回 NULL。

    FreeRTOS 重要的资源共享机制---互斥信号量(Mutex,即 Mutual Exclusion 的缩写)。

    注意,建议初学者学习完前两个信号量后再学习本章节的互斥信号量。

    互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互

    斥信号量与二值信号量有区别。 下面我们先举一个通过二值信号量实现资源独享,即互斥访问的例子,让

    大家有一个形象的认识,进而引出要讲解的互斥信号量。

    运行条件:

     让两个任务 Task1 和 Task2 都运行串口打印函数 printf,这里我们就通过二值信号量实现对函数

    printf 的互斥访问。 如果不对函数 printf 进行互斥访问,串口打印容易出现乱码。

     用计数信号量实现二值信号量只需将计数信号量的初始值设置为 1 即可。

    代码实现:

    有了上面二值信号量的认识之后,互斥信号量与二值信号量又有什么区别呢?互斥信号量可以防止优

    先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。

    运行条件:

     创建 3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。 也就是 Task1 的优先级最高。

     任务 Task1 和 Task3 互斥访问串口打印 printf,采用二值信号实现互斥访问。

     起初 Task3 通过二值信号量正在调用 printf,被任务 Task1 抢占,开始执行任务 Task1,也就是上图的起始位置。

    运行过程描述如下:

     任务 Task1 运行的过程需要调用函数 printf,发现任务 Task3 正在调用,任务 Task1 会被挂起,等

    待 Task3 释放函数 printf。

     在调度器的作用下,任务 Task3 得到运行,Task3 运行的过程中,由于任务 Task2 就绪,抢占了 Task3

    的运行。 优先级翻转问题就出在这里了,从任务执行的现象上看,任务 Task1 需要等待 Task2 执行

    完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级

    任务的执行,这里成了高优先级任务 Task1 等待低优先级任务 Task2 完成。 所以这种情况被称之为

    优先级翻转问题。

     任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,

    从而可以继续执行。FreeRTOS 互斥信号量的实现FreeRTOS 互斥信号量是怎么实现的呢?其实相对于二值信号量,互斥信号量就是解决了一下优先级

    翻转的问题。 下面我们通过如下的框图来说明一下 FreeRTOS 互斥信号量的实现,让大家有一个形象的认识。

    运行条件:

     创建 2 个任务 Task1 和 Task2,优先级分别为 1 和 3,也就是任务 Task2 的优先级最高。

     任务 Task1 和 Task2 互斥访问串口打印 printf。

     使用 FreeRTOS 的互斥信号量实现串口打印 printf 的互斥访问。

    运行过程描述如下: 低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。 此时任务 Task2 抢占了任务 Task1

    的执行,任务 Task1 被挂起。 任务 Task2 得到执行。

     任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优

    先级会被提升到与 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority

    inheritance),这样就有效地防止了优先级翻转问题。 任务 Task2 被挂起,任务 Task1 有新的优先

    级继续执行。

     任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。 由于互斥资源可以使用,任务

    Task2 获得互斥资源后开始执行。FreeRTOS 中断方式互斥信号量的实现

    互斥信号量仅支持用在 FreeRTOS 的任务中,中断函数中不可使用。互斥信号量 API 函数函数 xSemaphoreCreateMutex函数原型:

    SemaphoreHandle_t xSemaphoreCreateMutex( void )

    函数描述:

    函数 xSemaphoreCreateMutex 用于创建互斥信号量。

     返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不

    足,无法为此互斥信号量提供所需的空间会返回 NULL。使用这个函数要注意以下问题:

    1. 此函数是基于函数 xQueueCreateMutex 实现的:

    #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

    函数 xQueueCreateMutex 的实现是基于消息队列函数 xQueueGenericCreate 实现的。

    2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:

    #define configUSE_MUTEXES 1

    互斥信号量, xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用

    应用举例:

    经过测试,互斥信号量是可以被其他任务释放的,但是我们最好不要这么做,因为官方推荐的就是在同一个任务中接收和释放。如果在其他任务释放,不仅仅会让代码整体逻辑变得复杂,还会给使用和维护这套API的人带来困难。遵守规范,总是好的。

    裸机编程的时候,我经常想一个问题,就是怎么做到当一个标志位触发的时候,立即执行某个操作,如同实现标志中断一样,在os编程之后,我们就可以让一个优先级最高任务一直等待某个信号量,如果获得信号量,就执行某个操作,实现类似标志位中断的作用(当然,要想正真做到中断效果,那就需要屏蔽所有可屏蔽中断,而临界区就可以做到)。

    再说一下递归互斥信号量:递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量

    eg:

    static void vTaskMsgPro(void *pvParameters)

    {

    TickType_t xLastWakeTime;const TickType_t xFrequency = 1500;/*获取当前的系统时间*/xLastWakeTime=xTaskGetTickCount();while(1)

    {/*递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

    {/*--------------------------------------*/

    //假如这里是被保护的资源,第1层被保护的资源,用户可以在这里添加被保护资源

    /*----------------------------------------------------------------------------*/printf("任务vTaskMsgPro在运行,第1层被保护的资源,用户可以在这里添加被保护资源\r\n");/*第1层被保护的资源里面嵌套被保护的资源*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

    {/*------------------------------------------------------------------------*/

    //假如这里是被保护的资源,第2层被保护的资源,用户可以在这里添加被保护资源

    /*------------------------------------------------------------------------*/printf("任务vTaskMsgPro在运行,第2层被保护的资源,用户可以在这里添加被保护资源\r\n");/*第2层被保护的资源里面嵌套被保护的资源*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

    {

    printf("任务vTaskMsgPro在运行,第3层被保护的资源,用户可以在这里添加被保护资源\r\n");

    bsp_LedToggle(1);

    bsp_LedToggle(4);

    }

    xSemaphoreGiveRecursive(xRecursiveMutex);

    }

    xSemaphoreGiveRecursive(xRecursiveMutex);

    }

    xSemaphoreGiveRecursive(xRecursiveMutex);/*vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/vTaskDelayUntil(&xLastWakeTime, xFrequency);

    }

    }

    可以前面的那个官方文档那样用if判断传递共享量:

    也可以用我们递归互斥信号量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE时,会一直等待信号量,直到有信号量来才执行后面的语句。

    展开全文
  • 信号量用于线程同步,互斥量用户保护资源的互斥访问。信号量互斥量的区别互斥量用于线程的互斥,信号线用于线程的同步。互斥量值只能为0/1,信号量值可以为非负整数。信号量可以实现多个同类资源的多线程互斥和...

    信号量用于线程同步,互斥量用户保护资源的互斥访问。

    信号量与互斥量的区别

    互斥量用于线程的互斥,信号线用于线程的同步。

    互斥量值只能为0/1,信号量值可以为非负整数。信号量可以实现多个同类资源的多线程互斥和同步。

    互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

    信号量Semaphore

    信号量是在多线程环境中,线程间传递信号的一种方式。

    简单的Semaphore实现

    public class Semaphore {

    private boolean signal = false; //使用signal可以避免信号丢失

    public synchronized void take() {

    this.signal = true;

    this.notify();

    }

    public synchronized void release() throws InterruptedException{

    while(!this.signal) //使用while避免假唤醒

    wait();

    this.signal = false;

    }

    }

    使用场景

    Semaphore semaphore = new Semaphore();

    SendingThread sender = new SendingThread(semaphore);

    ReceivingThread receiver = new ReceivingThread(semaphore);

    receiver.start();

    sender.start();

    public class SendingThread {

    Semaphore semaphore = null;

    public SendingThread(Semaphore semaphore){

    this.semaphore = semaphore;

    }

    public void run(){

    while(true){

    //do something, then signal

    this.semaphore.take();

    }

    }

    }

    public class RecevingThread {

    Semaphore semaphore = null;

    public ReceivingThread(Semaphore semaphore){

    this.semaphore = semaphore;

    }

    public void run(){

    while(true){

    this.semaphore.release();

    //receive signal, then do something...

    }

    }

    }

    可计数的Semaphore

    上面提到的Semaphore的简单实现并没有计算通过调用take方法所产生信号的数量。可以把它改造成具有计数功能的Semaphore。

    public class CountingSemaphore {

    private int signals = 0;

    public synchronized void take() {

    this.signals++;

    this.notify();

    }

    public synchronized void release() throws InterruptedException{

    while(this.signals == 0)

    wait();

    this.signals--;

    }

    }

    有上限的Semaphore

    可以将上面的CountingSemaphore改造成一个信号数量有上限的BoundedSemaphore

    public class BoundedSemaphore {

    private int signals = 0;

    private int bound = 0;

    public BoundedSemaphore(int upperBound){

    this.bound = upperBound;

    }

    public synchronized void take() throws InterruptedException{

    while(this.signals == bound)

    wait();

    this.signals++;

    this.notify();

    }

    public synchronized void release() throws InterruptedException{

    while(this.signals == 0)

    wait();

    this.signals--;

    this.notify();

    }

    }

    在BoundedSemaphore中,当已经产生的信号数量达到了上限,take方法将阻塞新的信号产生请求,直到某个线程调用release方法后,被阻塞于take方法的线程才能传递自己的信号。

    Java内置的Semaphore

    java.util.concurrent包中有Semaphore的实现,可以设置参数,控制同时访问的个数。

    下面的Demo中申明了一个只有5个许可的Semaphore,而有20个线程要访问这个资源,通过acquire()和release()获取和释放访问许可。

    final Semaphore semp = new Semaphore(5);

    ExecutorService exec = Executors.newCachedThreadPool();

    for (int index = 0; index < 20; index++) {

    final int NO = index;

    Runnable run = new Runnable() {

    public void run() {

    try {

    // 获取许可

    semp.acquire();

    System.out.println("Accessing: " + NO);

    Thread.sleep((long) (Math.random() * 10000));

    // 访问完后,释放

    semp.release();

    System.out.println("-----------------" + semp.availablePermits());

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    };

    exec.execute(run);

    }

    exec.shutdown();

    互斥量Mutex

    互斥量:提供对资源的独占访问,只能为0/1,如果某一个资源同时只能允许一个访问者对其访问,可以使用互斥量控制线程对其访问。

    互斥量实现:

    public class Mutex {

    private boolean isLocked = false;

    public synchronized void lock() {

    while(this.isLocked) //使用while可以避免线程 假唤醒

    wait();

    this.isLocked= true;

    }

    }

    public synchronized void unlock() throws InterruptedException{

    this.isLocked= false;

    this.notify();

    }

    }

    在Mutex中,我们添加了一个signal用于保存信号。

    将互斥量当作锁来使用:

    Mutex mutex = new Mutex();

    mutex.lock();

    ...

    //临界区

    mutex.unlock();

    互斥量的加锁和解锁必须由同一个线程分别对应使用。

    参考

    展开全文
  • 互斥互斥量可以理解为二值信号量,它可以避免上述优先级反转的问题,方法是当高优先级任务申请访问资源但资源被低优先级任务时,将低优先级任务的优先级提高到高优先级同级别,就避免了中优先级任务就绪时剥夺低...

    1. 信号量

    信号量有两种:计数性信号量和二值信号量,计数性信号量可以被获取多次,二值信号量只有0和1两种状态,只能被获取一次。

    信号量可以用来对资源进行保护,防止多个任务同时访问某个资源。为资源创建一个专属的二值信号量,任务在申请访问一个资源之前,先申请获取信号量,如果当前没有任务正在访问资源,则获取信号量可以成功,可以继续访问资源;如果当前资源正在被某个任务访问,则获取信号量会失败,任务进入挂起状态,等待其它任务访问资源完成并释放信号量后再访问资源。

    如果某个资源允许多个任务同时访问,可以采用计数性信号量,每次有任务申请获取信号量则信号量计数减一,直到减为0后不再允许其他任务再申请获取,即可以通过信号量的初始值来控制同时访问资源的最大任务数量。

    2. 优先级反转

    使用信号量可能会导致优先级反转的问题,详细的说明见下图,截取自RT-Thread编程指南,简单来说就是当一个高优先级任务申请访问资源时,资源正在被低优先级任务占用,所以高优先级任务被挂起,此时一个中优先级任务就绪,剥夺低优先级任务的CPU使用权,开始执行,就造成了该任务先于高优先级任务开始执行。
    在这里插入图片描述

    3. 互斥量

    互斥量可以理解为二值信号量,它可以避免上述优先级反转的问题,方法是当高优先级任务申请访问资源但资源被低优先级任务时,将低优先级任务的优先级提高到高优先级同级别,就避免了中优先级任务就绪时剥夺低优先级任务的CPU使用权。详细的说明见下图。任务在申请获取互斥量时,系统会判断当前占有该互斥量的任务的优先级和申请任务的优先级,如果申请任务优先级更高,系统会把这个更高的优先级赋给占有互斥量的任务,当任务释放互斥量时再恢复其原有的优先级。
    在这里插入图片描述
    此外,互斥量还支持重入,即多次获取,这时互斥量被获取了多少次就需要释放多少次,才能完全释放。且互斥量是归属于某个任务的,哪个任务获取互斥量就要由哪个任务释放互斥量,其它任务不能释放。

    4. 死锁

    死锁也称抱死,是指两个任务无限制地互相等待对方正在占用的资源。详细说明如下,截取自《嵌入式实时操作系统uC/OS-III》:
    在这里插入图片描述
    避免死锁有以下三个方法:

    • (1)不同任务中用相同的顺序申请多个资源;
    • (2)任务中需要用到多个资源时,先获取所有资源,再做下一步工作;
    • (3)在申请获取互斥量或信号量时设置超时时间,避免永远等待。

    最有效的是方法(1),按相同的顺序访问资源,就可以避免两个任务和两个资源间的交叉访问,如果任务中使用资源的顺序固定,那么可以先获取所有资源的访问权限,再访问资源,这样就能避免死锁的问题。方法(3)只是能暂时避免死锁,但会导致系统报错,不推荐。

    5. 邮箱和消息队列

    5.1 uCOS

    uCOS-II有邮箱和消息队列两个概念,都是Event的一种,对于邮箱来说,Event控制块中有一个指针直接指向数据地址,所以每次只能传递一个数据指针。而对消息队列来说,Event控制块中的指针指向一个消息队列,消息队列中的每一个节点包含一个数据地址,所以能同时传递多个数据地址。队列大小为1的消息队列与邮箱的作用是一样的。

    uCOS-II中有一个静态的全局结构体数组OSQTbl[OS_MAX_QS],包含了系统中所有可用的消息队列,每创建一个消息队列,就从这个数组中取一个节点出来。

    在uCOS-III中,取消了邮箱的概念,只有消息队列。在代码中,定义了一个消息池:

    OS_MSG         OSCfg_MsgPool       [OS_CFG_MSG_POOL_SIZE];
    

    所有消息队列在发送消息的时候,都从消息池里取一个节点出来,挂到消息队列的消息链表上,消息队列控制块有指针指向消息链表的头和尾,还会记录消息节点的数量。

    消息队列控制块的定义如下:

    struct  os_msg_q {                                          /* OS_MSG_Q                                               */
        OS_MSG              *InPtr;                             /* Pointer to next OS_MSG to be inserted  in   the queue  */
        OS_MSG              *OutPtr;                            /* Pointer to next OS_MSG to be extracted from the queue  */
        OS_MSG_QTY           NbrEntriesSize;                    /* Maximum allowable number of entries in the queue       */
        OS_MSG_QTY           NbrEntries;                        /* Current number of entries in the queue                 */
        OS_MSG_QTY           NbrEntriesMax;                     /* Peak number of entries in the queue                    */
    };
    

    5.2 rt-thread

    RTT中有邮箱和消息队列两个概念,它的邮箱和uCOS-II的邮箱概念不同。RTT的邮箱是基于一个数组来实现的,数组的成员是4字节变量,可以是数据地址或变量,这个数组就是该邮箱的消息池,在创建邮箱的时候固定进行分配。所以RTT的邮箱在传递数据时每次固定传输一个4字节的数据,每次发送邮件的时候从消息池中取一个4字节变量出来,接收邮件的时候释放一个4字节变量。邮箱控制块中存储着当前邮箱发送邮件(in_offset)和接收邮件(out_offset)的数据地址,以及当前邮箱中存在的邮件数量(entry)。

    邮箱控制块的定义如下:

    struct rt_mailbox
    {
        struct rt_ipc_object parent;                        /**< inherit from ipc_object */
    
        rt_ubase_t          *msg_pool;                      /**< start address of message buffer */
    
        rt_uint16_t          size;                          /**< size of message pool */
    
        rt_uint16_t          entry;                         /**< index of messages in msg_pool */
        rt_uint16_t          in_offset;                     /**< input offset of the message buffer */
        rt_uint16_t          out_offset;                    /**< output offset of the message buffer */
    
        rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this mailbox */
    };
    

    RTT消息队列的实现和uCOS相似。但RTT不再是所有消息队列共用一个消息池,而是每个消息队列单独有一个消息池,分别管理。

    RTT的消息队列控制块定义如下:

    struct rt_messagequeue
    {
        struct rt_ipc_object parent;                        /**< inherit from ipc_object */
    
        void                *msg_pool;                      /**< start address of message queue */
    
        rt_uint16_t          msg_size;                      /**< message size of each message */
        rt_uint16_t          max_msgs;                      /**< max number of messages */
    
        rt_uint16_t          entry;                         /**< index of messages in the queue */
    
        void                *msg_queue_head;                /**< list head */
        void                *msg_queue_tail;                /**< list tail */
        void                *msg_queue_free;                /**< pointer indicated the free node of queue */
    
        rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this message queue */
    };
    
    展开全文
  • 信号量用来控制对共享资源的保护,但是现在基本用来做任务同步用 要想获取资源的任务必须执行 “等待” 操作,如果该资源对应的信号量有效值大于1,则任务可以获得该资源,任务继续运行。如果该信号量的有效值为0,...

    1、UCOSIII信号量

    1.1、信号量简介:

    信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。

    信号量用来控制对共享资源的保护,但是现在基本用来做任务同步用

    要想获取资源的任务必须执行 “等待” 操作,如果该资源对应的信号量有效值大于1,则任务可以获得该资源,任务继续运行。如果该信号量的有效值为0,则任务加入等待信号量的任务表中。如果等待时间超过某一个设定值,该信号量仍然没有被释放掉,则等待信号量的任务就进入就绪态,如果将等待时间设置为0的话任务就将一直等待该信号量

    信号量通常分为两种:二进制信号量和计数型信号量

    • 二进制信号量只能取0和1两个值,计数型信号量的信号量值大于1,计数型信号量的范围由 0S_SEM_STR决定,OS_SEM_CTR可以为8位,16位和32位,取值范围分别为:0~255、0~65535和0~4294967295
    • 二值信号量用于那些一次只能一个任务使用的资源,比如 I/O 设备,打印机计,数型信号量用于某些资源可以同时被几个任务所使用,比如一个缓存池有10个缓存块,那么同时最多可以支持10个任务来使用内存池。

    1.2、信号量API函数:

    函数名作用
    OSSemCreate()建立一个信号量
    OSSemDel()删除一个信号量
    OSSemPend()等待一个信号量
    OSSemPendAbrot()取消等待
    OSSemPost()释放或者发出一个信号量
    OSSemSet()强制设置一个信号量的值

    OSSemCreate 创建信号量:

    void  OSSemCreate (OS_SEM      *p_sem,
                       CPU_CHAR    *p_name,
                       OS_SEM_CTR   cnt,
                       OS_ERR      *p_err)
    
    • OS_SEM 信号量:

      typedef  struct  os_sem              OS_SEM;
      
    • CPU_CHAR 信号量命名:

      typedef            char        CPU_CHAR;                        /*  8-bit character                                     */
      
    • OS_SEM_CTR 信号量初始值:

      typedef   CPU_INT32U      OS_SEM_CTR;                  /* Semaphore value                                     16/<32> */
      
    • OS_ERR 信号量返回错误信息:

      typedef  enum  os_err {
          OS_ERR_NONE                      =     0u,
      
          OS_ERR_A                         = 10000u,
          OS_ERR_ACCEPT_ISR                = 10001u,
      
          OS_ERR_B                         = 11000u,
      
          OS_ERR_C                         = 12000u,
          OS_ERR_CREATE_ISR                = 12001u,
      
          OS_ERR_D                         = 13000u,
          OS_ERR_DEL_ISR                   = 13001u,
      
          OS_ERR_E                         = 14000u,
      
          OS_ERR_F                         = 15000u,
          OS_ERR_FATAL_RETURN              = 15001u,
      
          OS_ERR_FLAG_GRP_DEPLETED         = 15101u,
          OS_ERR_FLAG_NOT_RDY              = 15102u,
          OS_ERR_FLAG_PEND_OPT             = 15103u,
          OS_ERR_FLUSH_ISR                 = 15104u,
      
          OS_ERR_G                         = 16000u,
      
          OS_ERR_H                         = 17000u,
      
          OS_ERR_I                         = 18000u,
          OS_ERR_ILLEGAL_CREATE_RUN_TIME   = 18001u,
          OS_ERR_INT_Q                     = 18002u,
          OS_ERR_INT_Q_FULL                = 18003u,
          OS_ERR_INT_Q_SIZE                = 18004u,
          OS_ERR_INT_Q_STK_INVALID         = 18005u,
          OS_ERR_INT_Q_STK_SIZE_INVALID    = 18006u,
      
          OS_ERR_J                         = 19000u,
      
          OS_ERR_K                         = 20000u,
      
          OS_ERR_L                         = 21000u,
          OS_ERR_LOCK_NESTING_OVF          = 21001u,
      
          OS_ERR_M                         = 22000u,
      
          OS_ERR_MEM_CREATE_ISR            = 22201u,
          OS_ERR_MEM_FULL                  = 22202u,
          OS_ERR_MEM_INVALID_P_ADDR        = 22203u,
          OS_ERR_MEM_INVALID_BLKS          = 22204u,
          OS_ERR_MEM_INVALID_PART          = 22205u,
          OS_ERR_MEM_INVALID_P_BLK         = 22206u,
          OS_ERR_MEM_INVALID_P_MEM         = 22207u,
          OS_ERR_MEM_INVALID_P_DATA        = 22208u,
          OS_ERR_MEM_INVALID_SIZE          = 22209u,
          OS_ERR_MEM_NO_FREE_BLKS          = 22210u,
      
          OS_ERR_MSG_POOL_EMPTY            = 22301u,
          OS_ERR_MSG_POOL_NULL_PTR         = 22302u,
      
          OS_ERR_MUTEX_NOT_OWNER           = 22401u,
          OS_ERR_MUTEX_OWNER               = 22402u,
          OS_ERR_MUTEX_NESTING             = 22403u,
      
          OS_ERR_N                         = 23000u,
          OS_ERR_NAME                      = 23001u,
          OS_ERR_NO_MORE_ID_AVAIL          = 23002u,
      
          OS_ERR_O                         = 24000u,
          OS_ERR_OBJ_CREATED               = 24001u,
          OS_ERR_OBJ_DEL                   = 24002u,
          OS_ERR_OBJ_PTR_NULL              = 24003u,
          OS_ERR_OBJ_TYPE                  = 24004u,
      
          OS_ERR_OPT_INVALID               = 24101u,
      
          OS_ERR_OS_NOT_RUNNING            = 24201u,
          OS_ERR_OS_RUNNING                = 24202u,
      
          OS_ERR_P                         = 25000u,
          OS_ERR_PEND_ABORT                = 25001u,
          OS_ERR_PEND_ABORT_ISR            = 25002u,
          OS_ERR_PEND_ABORT_NONE           = 25003u,
          OS_ERR_PEND_ABORT_SELF           = 25004u,
          OS_ERR_PEND_DEL                  = 25005u,
          OS_ERR_PEND_ISR                  = 25006u,
          OS_ERR_PEND_LOCKED               = 25007u,
          OS_ERR_PEND_WOULD_BLOCK          = 25008u,
      
          OS_ERR_POST_NULL_PTR             = 25101u,
          OS_ERR_POST_ISR                  = 25102u,
      
          OS_ERR_PRIO_EXIST                = 25201u,
          OS_ERR_PRIO                      = 25202u,
          OS_ERR_PRIO_INVALID              = 25203u,
      
          OS_ERR_PTR_INVALID               = 25301u,
      
          OS_ERR_Q                         = 26000u,
          OS_ERR_Q_FULL                    = 26001u,
          OS_ERR_Q_EMPTY                   = 26002u,
          OS_ERR_Q_MAX                     = 26003u,
          OS_ERR_Q_SIZE                    = 26004u,
      
          OS_ERR_R                         = 27000u,
          OS_ERR_REG_ID_INVALID            = 27001u,
          OS_ERR_ROUND_ROBIN_1             = 27002u,
          OS_ERR_ROUND_ROBIN_DISABLED      = 27003u,
      
          OS_ERR_S                         = 28000u,
          OS_ERR_SCHED_INVALID_TIME_SLICE  = 28001u,
          OS_ERR_SCHED_LOCK_ISR            = 28002u,
          OS_ERR_SCHED_LOCKED              = 28003u,
          OS_ERR_SCHED_NOT_LOCKED          = 28004u,
          OS_ERR_SCHED_UNLOCK_ISR          = 28005u,
      
          OS_ERR_SEM_OVF                   = 28101u,
          OS_ERR_SET_ISR                   = 28102u,
      
          OS_ERR_STAT_RESET_ISR            = 28201u,
          OS_ERR_STAT_PRIO_INVALID         = 28202u,
          OS_ERR_STAT_STK_INVALID          = 28203u,
          OS_ERR_STAT_STK_SIZE_INVALID     = 28204u,
          OS_ERR_STATE_INVALID             = 28205u,
          OS_ERR_STATUS_INVALID            = 28206u,
          OS_ERR_STK_INVALID               = 28207u,
          OS_ERR_STK_SIZE_INVALID          = 28208u,
          OS_ERR_STK_LIMIT_INVALID         = 28209u,
      
          OS_ERR_T                         = 29000u,
          OS_ERR_TASK_CHANGE_PRIO_ISR      = 29001u,
          OS_ERR_TASK_CREATE_ISR           = 29002u,
          OS_ERR_TASK_DEL                  = 29003u,
          OS_ERR_TASK_DEL_IDLE             = 29004u,
          OS_ERR_TASK_DEL_INVALID          = 29005u,
          OS_ERR_TASK_DEL_ISR              = 29006u,
          OS_ERR_TASK_INVALID              = 29007u,
          OS_ERR_TASK_NO_MORE_TCB          = 29008u,
          OS_ERR_TASK_NOT_DLY              = 29009u,
          OS_ERR_TASK_NOT_EXIST            = 29010u,
          OS_ERR_TASK_NOT_SUSPENDED        = 29011u,
          OS_ERR_TASK_OPT                  = 29012u,
          OS_ERR_TASK_RESUME_ISR           = 29013u,
          OS_ERR_TASK_RESUME_PRIO          = 29014u,
          OS_ERR_TASK_RESUME_SELF          = 29015u,
          OS_ERR_TASK_RUNNING              = 29016u,
          OS_ERR_TASK_STK_CHK_ISR          = 29017u,
          OS_ERR_TASK_SUSPENDED            = 29018u,
          OS_ERR_TASK_SUSPEND_IDLE         = 29019u,
          OS_ERR_TASK_SUSPEND_INT_HANDLER  = 29020u,
          OS_ERR_TASK_SUSPEND_ISR          = 29021u,
          OS_ERR_TASK_SUSPEND_PRIO         = 29022u,
          OS_ERR_TASK_WAITING              = 29023u,
      
          OS_ERR_TCB_INVALID               = 29101u,
      
          OS_ERR_TLS_ID_INVALID            = 29120u,
          OS_ERR_TLS_ISR                   = 29121u,
          OS_ERR_TLS_NO_MORE_AVAIL         = 29122u,
          OS_ERR_TLS_NOT_EN                = 29123u,
          OS_ERR_TLS_DESTRUCT_ASSIGNED     = 29124u,
      
          OS_ERR_TICK_PRIO_INVALID         = 29201u,
          OS_ERR_TICK_STK_INVALID          = 29202u,
          OS_ERR_TICK_STK_SIZE_INVALID     = 29203u,
          OS_ERR_TICK_WHEEL_SIZE           = 29204u,
      
          OS_ERR_TIME_DLY_ISR              = 29301u,
          OS_ERR_TIME_DLY_RESUME_ISR       = 29302u,
          OS_ERR_TIME_GET_ISR              = 29303u,
          OS_ERR_TIME_INVALID_HOURS        = 29304u,
          OS_ERR_TIME_INVALID_MINUTES      = 29305u,
          OS_ERR_TIME_INVALID_SECONDS      = 29306u,
          OS_ERR_TIME_INVALID_MILLISECONDS = 29307u,
          OS_ERR_TIME_NOT_DLY              = 29308u,
          OS_ERR_TIME_SET_ISR              = 29309u,
          OS_ERR_TIME_ZERO_DLY             = 29310u,
      
          OS_ERR_TIMEOUT                   = 29401u,
      
          OS_ERR_TMR_INACTIVE              = 29501u,
          OS_ERR_TMR_INVALID_DEST          = 29502u,
          OS_ERR_TMR_INVALID_DLY           = 29503u,
          OS_ERR_TMR_INVALID_PERIOD        = 29504u,
          OS_ERR_TMR_INVALID_STATE         = 29505u,
          OS_ERR_TMR_INVALID               = 29506u,
          OS_ERR_TMR_ISR                   = 29507u,
          OS_ERR_TMR_NO_CALLBACK           = 29508u,
          OS_ERR_TMR_NON_AVAIL             = 29509u,
          OS_ERR_TMR_PRIO_INVALID          = 29510u,
          OS_ERR_TMR_STK_INVALID           = 29511u,
          OS_ERR_TMR_STK_SIZE_INVALID      = 29512u,
          OS_ERR_TMR_STOPPED               = 29513u,
      
          OS_ERR_U                         = 30000u,
      
          OS_ERR_V                         = 31000u,
      
          OS_ERR_W                         = 32000u,
      
          OS_ERR_X                         = 33000u,
      
          OS_ERR_Y                         = 34000u,
          OS_ERR_YIELD_ISR                 = 34001u,
      
          OS_ERR_Z                         = 35000u
      } OS_ERR;
      

    OSSemPend 请求信号量:

    OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,
                           OS_TICK   timeout,
                           OS_OPT    opt,
                           CPU_TS   *p_ts,
                           OS_ERR   *p_err)
    

    2、UCOSIII互斥信号量

    2.1、优先级反转:

    优先级反转在可剥夺反转内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果
    在这里插入图片描述
    任务优先级:任务H > 任务M > 任务L

    时间线从左往右

    2.2、互斥信号量:

    为了避免优先级反转这个问题,UCOSIII支持一种特殊的二进制信号量:互斥信号量,用它可以解决优先级反转问题。
    在这里插入图片描述

    2.2、互斥信号量API函数:

    函数名作用
    OSMutexCreate()建立一个互斥信号量
    OSMutexDel()删除一个互斥信号量
    OSMutexPend()等待一个互斥信号量
    OSMutexPendAbrot()取消等待
    OSMutexPost()释放或者发布一个互斥信号量

    3、UCOSIII任务内嵌信号量

    在UCOSIII中每个任务都有自己的内嵌的信号量,这种功能不仅能够简化代码,而且比使用独立的信号量更有效。任务信号量是直接内嵌在UCOSIII中的,任务信号量相关代码在 os_task.c 中

    函数名作用
    OSTaskSemPend()等待一个任务信号量
    OSTaskSemPendAbrot()取消等待任务信号量
    OSTaskSemPost()发布任务信号量
    OSTaskSemSet()强行设置任务信号量计数
    展开全文
  • 学习互斥量的时候涉及到优先级反转和优先级继承两个概念。下面是我的理解。 1、阐明下两者的概念: ...任务1首先执行(首先不断获取二值信号量,由于之前没有释放二值信号量的操作,所以任务1首先释放一次二值信号量
  • 如上百度百科所示,它是一个经典的多线程同步问题,然后解决方案就用我们标题所说的信号量互斥锁共同解决: 如上图所示,由主线程main创建子线程生产者以及消费者,生产者向缓冲区中写入数据,消费者向缓冲区读取...
  • (1)互斥量用于线程的互斥信号量用于线程的同步。 互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。 同步:是指在互斥的...
  • 信号量互斥量之间的区别 1 互斥量用于线程的互斥信号量用于线程的同步 互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排他性。 但互斥无法限制访问者对资源的访问顺序,即访问是无序的 同步...
  • 一、信号量简介 相比于自旋锁,信号量可以使线程进入休眠状态,比如A与B、C合租了一套房子,这个房子只有一个厕所,一次只能一个人使用。某一天早上A去上厕所了,过了一会B也想用厕所,因为A在厕所里面,所以B只能...
  • 1. 理解如下,言简意赅的说,信号量解决同步,互斥解决竞争。信号量用于同步,主要任务间和中断间同步;互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁。互斥量具有优先级继承,信号量...
  • 前天,水木上又有人问到了信号量互斥锁的问题,我想还是对它们的区别与实现总结一下。 首先了解一些信号量和线程互斥锁的语义上的区别: 援引CU上一篇帖子的内容: “信号...
  • 首先说明一下什么叫做PV信号量机制: 信号量,接下来用Semaphore或者S来表示.其意义是代表当前临界资源的数量.正数表示目前空闲的临界资源数量.0表示目前没有临界资源空闲,也没有进程等待临界资源.负数表示目前有多少...
  • 实验5 用信号量实现进程互斥

    千次阅读 2020-12-18 18:29:38
    实验5 利用信号量实现进程互斥 【实验目的】 (1) 理解互斥概念、信号量机制及信号量结构; (2) 掌握信号量的使用方法; (3) 掌握PV操作的定义; (4) 掌握PV操作实现互斥方法。 【实验原理/实验基础知识】...
  • 信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个...
  • 先添加 OS_EVENT *str_Q;//主任务的声明 ... //发送信号量(控制下一个线程) 在另一个线程里的while中添加 OSSemPend(str_Q,0,&err); //请求信号量(上一个线程发送了才执行下面的语句) ...
  • 互斥信号量 1.有优先级继承。 2.尽量不要在中断中调用 3.xSemaphoreCreateMutex创建后,可以直接take使用。 二值信号量 1.无优先级继承。 2.允许在中断中调用。 3.可以当做标志位来使用。 4....
  • 信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个...
  • 一、互斥信号量简介 ...互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先
  • 信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。一个任务要想访问共享...
  • 互斥信号量

    千次阅读 2021-05-08 16:40:32
    为使多个进程能互斥的访问某临界资源,只需为该资源设置一互斥信号量mutex,并设初始值为1,然后将各进程访问该资源的临界区置于wait(mutex)和signal(mutex)操作之间即可。 互斥信号量值的描述: 初值为1,取值范围...
  • 互斥锁和信号量

    2021-08-14 18:17:26
    POSIX标准中进程和线程同步和互斥方法,主要有信号量互斥锁两种方式。 同步:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。 同步就是在互斥的基础上有顺序 二、互斥锁 2.1 互斥锁...
  • 在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII引入了互斥信号量这个概念。本章我们就来...
  • 线程同步和互斥的概念 线程同步就是把同一进程环境下的一组并发线程,因直接制约而互相发送消息而进行互相合作、互相等待,使得各线程按一定的速度执行的过程。互斥是指不允许两个以上的共享该资源的并发线程同时...
  • 1.同步和互斥: 同步(直接制约关系):指的是完成同一任务的伙伴进程间,因需要协调它们的工作而等待、传递信息等。...(1)信号量S是一个整型变量(信号量可以看做是资源)。 (2) P操作就是当..
  • FreeRTOS有二值信号量、计数信号量互斥锁和递归互斥锁。 信号量实现总0.前言[在嵌入式操作系统中二值型信号量是任务间、任务与中断间同步的重要手段。FreeRTOS的二值型信号量简单易用,下面结合一个具体例子说明...
  • 问题什么是Java中的互斥信号量?主要区别是什么?#1 热门回答(127 赞)不幸的是,每个人都错过了信号量互斥量之间最重要的区别; "所有权"的概念。信号量没有所有权的概念,这意味着任何线程都可以释放信号量(这...
  • posix信号量实现哲学家就餐问题 1.哲学家就餐问题描述(请参考请参考其他博主) 2.如何实现就餐场景 a.5个信号量代表5支筷子 b.5五个线程代表5个哲学家 c.主线程负责创建筷子们和哲学家们 d.sem_wait函数 模拟拿筷子...
  • 常见的同步方式有:互斥锁、条件变量、读写锁、信号量。另外,对于进程间的同步,也可以通过进程间通信的方式进行同步,包括管道(无名管道、有名管道)、信号量、消息队列、共享内存、远程过程调用,当然也可以通过...
  • 1.信号量 信号量是用来协调不同进程间的数据对象的,而最主要的应用是共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。 一般说来,为了获得共享资源,进程...
  • * sem_id : 信号量键值, 一个唯一的非零整数, 不同的进程可以通过它访问同一个信号量。 * num_sems : 信号量数目, 它几乎总是取值为1. * sem_flags: 它低端的9个比特是该信号量的权限,其作用类.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 95,094
精华内容 38,037
关键字:

信号量解决互斥的基本方法