精华内容
下载资源
问答
  • ucos 互斥信号量

    2018-08-16 11:58:11
    ucosIII示例-互斥信号量,初始化和使用方法,可直接运行
  • 使用方法 内核信号量的构成 信号量的API 初始化 PV操作 获取信号量(P) 释放内核信号量(V) 补充 内核信号量的使用例程 场景1 场景2 读-写信号量 概念 Linux内核的信号量在概念和原理上和用户态的...

    semaphore信号量:一个简单的示例程序》用户态程序

     

    目录

    概念

    应用场景

    使用方法

    内核信号量的构成

    信号量的API

    初始化

    PV操作

    获取信号量(P)

    释放内核信号量(V)

    补充

    内核信号量的使用例程

    场景1

    场景2

    读-写信号量


     

    概念


    Linux内核的信号量在概念和原理上和用户态的System V的IPC机制信号量是相同的,不过他绝不可能在内核之外使用,因此他和System V的IPC机制信号量毫不相干。

    如果有一个任务想要获得已经被占用的信号量时,信号量会将其放入一个等待队列(它不是站在外面痴痴地等待而是将自己的名字写在任务队列中)然后让其睡眠。

    当持有信号量的进程将信号释放后,处于等待队列中的一个任务将被唤醒(因为队列中可能不止一个任务),并让其获得信号量。这一点与自旋锁不同,处理器可以去执行其它代码。

     

    应用场景


    由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况;

    相反,锁被短时间持有时,使用信号量就不太适宜了,因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁占用的全部时间表还要长。

    举2个生活中的例子:

    1. 我们坐火车从南京到新疆需要2天的时间,这个'任务'特别的耗时,只能坐在车上等着车到站,但是我们没有必要一直睁着眼睛等,理想的情况就是我们上车就直接睡觉,醒来就到站(看过《异形》的读者会深有体会),这样从人(用户)的角度来说,体验是最好的,对比于进程,程序在等待一个耗时事件的时候,没有必须要一直占用CPU,可以暂停当前任务使其进入休眠状态,当等待的事件发生之后再由其他任务唤醒,类似于这种场景采用信号量比较合适。
    2. 我们有时候会等待电梯、洗手间,这种场景需要等待的时间并不是很多,如果我们还要找个地方睡一觉,然后等电梯到了或者洗手间可以用了再醒来,那很显然这也没有必要,我们只需要排好队,刷一刷抖音就可以了,对比于计算机程序,比如驱动在进入中断例程,在等待某个寄存器被置位,这种场景需要等待的时间往往很短暂,系统开销甚至远小于进入休眠的开销,所以这种场景采用自旋锁比较合适。

    关于信号量和自旋锁,以及死锁问题,我们后面会再详细讨论。

     

    使用方法


    一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在 该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示能获得信号量,因而能即时访问被该信号量保护的共享资源。

    当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加1实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此他也唤醒所有等待该信号量的任务。

     

    内核信号量的构成


    内核信号量类似于自旋锁,因为当锁关闭着时,它不允许内核控制路径继续进行。然而,当内核控制路径试图获取内核信号量锁保护的忙资源时,相应的进程就被挂起。只有在资源被释放时,进程才再次变为可运行。

    只有可以睡眠的函数才能获取内核信号量;中断处理程序和可延迟函数都不能使用内核信号量。

    内核信号量是struct semaphore类型的对象,在内核源码中位于include\linux\semaphore.h文件

    struct semaphore{
        raw_spinlock_t        lock;
        unsigned int        count;
        struct list_head    wait_list;
    }
    
    成员 描述
    lock 在2.6.33之后的版本,内核加入了raw_spin_lock系列,使用方法和spin_lock系列一模一样,只是参数spinlock_t变为了raw_spinlock_t
    count 相当于信号量的值,大于0,资源空闲;等于0,资源忙,但没有进程等待这个保护的资源;小于0,资源不可用,并至少有一个进程等待资源
    wait_list 内核链表,当前获得信号量的任务会与该成员一起注册到等待的链表中

     

    信号量的API


    初始化

    DECLARE_MUTEX(name)

    该宏声明一个信号量name并初始化他的值为1,即声明一个互斥锁。

    DECLARE_MUTEX_LOCKED(name)

    该宏声明一个互斥锁name,但把他的初始值设置为0,即锁在创建时就处在已锁状态。因此对于这种锁,一般是先释放后获得。

    void sema_init (struct semaphore *sem, int val);

    该函用于数初始化设置信号量的初值,他设置信号量sem的值为val。

    注意:val设置为1说明只有一个持有者,这种信号量叫二值信号量或者叫互斥信号量。

    我们还允许信号量可以有多个持有者,这种信号量叫计数信号量,在初始化时要说明最多允许有多少个持有者也可以把信号量中的val初始化为任意的正数值n,在这种情况下,最多有n个进程可以并发地访问这个资源。

    void init_MUTEX (struct semaphore *sem);

    该函数用于初始化一个互斥锁,即他把信号量sem的值设置为1。

    void init_MUTEX_LOCKED (struct semaphore *sem);

    该函数也用于初始化一个互斥锁,但他把信号量sem的值设置为0,即一开始就处在已锁状态。

     

    PV操作


    获取信号量(P)


    void down(struct semaphore * sem);

    该函数用于获得信号量sem,他会导致调用该函数的进程睡眠,因此不能在中断上下文(包括IRQ上下文和softirq上下文)使用该函数。该函数将把sem的值减1,如果信号量sem的值非负,就直接返回,否则调用者将被挂起,直到别的任务释放该信号量才能继续运行。

    int down_interruptible(struct semaphore * sem);

    该函数功能和down类似,不同之处为,down不会被信号(signal)打断,但down_interruptible能被信号打断,因此该函数有返回值来区分是正常返回还是被信号中断,如果返回0,表示获得信号量正常返回,如果被信号打断,返回-EINTR。

    int down_trylock(struct semaphore * sem);

    该函数试着获得信号量sem,如果能够即时获得,他就获得该信号量并返回0,否则,表示不能获得信号量sem,返回值为非0值。因此,他不会导致调用者睡眠,能在中断上下文使用。

    int down_killable(struct semaphore *sem);
    int down_timeout(struct semaphore *sem, long jiffies);
    int down_timeout_interruptible(struct semaphore *sem, long jiffies);

     

    释放内核信号量(V)


    void up(struct semaphore * sem);

    该函数释放信号量sem,即把sem的值加1,如果sem的值为非正数,表明有任务等待该信号量,因此唤醒这些等待者。

     

    补充

    int down_interruptible(struct semaphore *sem)

    这个函数的功能就是获得信号量,如果得不到信号量就睡眠,此时没有信号打断,那么进入睡眠。但是在睡眠过程中可能被信号打断,打断之后返回-EINTR,主要用来进程间的互斥同步。

    下面是该函数的注释:

    /**
    * down_interruptible - acquire the semaphore unless interrupted
    * @sem: the semaphore to be acquired
    *
    * Attempts to acquire the semaphore. If no more tasks are allowed to
    * acquire the semaphore, calling this function will put the task to sleep.
    * If the sleep is interrupted by a signal, this function will return -EINTR.
    * If the semaphore is successfully acquired, this function returns 0.
    */

    一个进程在调用down_interruptible()之后,如果sem<0,那么就进入到可中断的睡眠状态并调度其它进程运行, 但是一旦该进程收到信号,那么就会从down_interruptible函数中返回。并标记错误号为:-EINTR。

    一个形象的比喻:传入的信号量为1好比天亮,如果当前信号量为0,进程睡眠,直到(信号量为1)天亮才醒,但是可能中途有个闹铃(信号)把你闹醒。

    又如:小强下午放学回家,回家了就要开始吃饭嘛,这时就会有两种情况:情况一:饭做好了,可以开始吃;情况二:当他到厨房去的时候发现妈妈还在做, 妈妈就对他说:“你先去睡会,待会做好了叫你。” 小强就答应去睡会,不过又说了一句:“睡的这段时间要是小红来找我玩,你可以叫醒我。” 小强就是down_interruptible,想吃饭就是获取信号量,睡觉对应这里的休眠,而小红来找我玩就是中断休眠。

    使用可被中断的信号量版本的意思是,万一出现了semaphore的死锁,还有机会用ctrl+c发出软中断,让等待这个内核驱动返回的用户态进程退出。而不是把整个系统都锁住了。在休眠时,能被中断信号终止,这个进程是可以接受中断信号的!

    比如你在命令行中输入# sleep 10000,按下ctrl + c,就给上面的进程发送了进程终止信号。信号发送给用户空间,然后通过系统调用,会把这个信号传给递给驱动。信号只能发送给用户空间,无权直接发送给内核的,那1G的内核空间,我们是无法直接去操作的。

     

    内核信号量的使用例程


    场景1


    在驱动程序中,当多个线程同时访问相同的资源时(驱动中的全局变量时一种典型的共享资源),可能会引发“竞态“,因此我们必须对共享资源进行并发控制。Linux内核中

    解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

     

    场景2


    有时候我们希望设备只能被一个进程打开,当设备被占用的时候,其他设备必须进入休眠。

    信号处理示意图

    如上图:

    1. 进程A首先通过open()打开设备文件,调用到内核的hello_open(),并调用down_interruptible(),因为此时信号量没有被占用,所以进程A可以获得信号量;
    2. 进程A获得信号量之后继续处理原有任务,此时进程B也要通过open()打开设备文件,同样调用内核函数hello_open(),但此时信号量获取不到,于是进程B被阻塞;
    3. 进程A任务执行完毕,关闭设备文件,并通过up()释放信号量,于是进程B被唤醒,并得以继续执行剩下的任务,
    4. 进程B执行完任务,释放设备文件,通过up()释放信号量

    代码如下:

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/semaphore.h>
    
    static int major = 250;
    static int minor = 0;
    static dev_t devno;
    static struct cdev cdev;
    
    
    static struct class *cls;
    static struct device *test_device;
    
    static struct semaphore sem;
    static int hello_open (struct inode *inode, struct file *filep)
    {
        
        if(down_interruptible(&sem))//p
        {
            return -ERESTARTSYS;
        }
          return 0;
    }
    static int hello_release (struct inode *inode, struct file *filep)
    {
        up(&sem);//v
        return 0;
    }
    static struct file_operations hello_ops =
    {
        .open = hello_open,
        .release = hello_release,
    };
    static int hello_init(void)
    {
        int result;
        int error;    
        printk("hello_init \n");
        result = register_chrdev( major, "hello", &hello_ops);
        if(result < 0)
        {
            printk("register_chrdev fail \n");
            return result;
        }
        devno = MKDEV(major,minor);
        cls = class_create(THIS_MODULE,"helloclass");
        if(IS_ERR(cls))
        {
            unregister_chrdev(major,"hello");
            return result;
        }
        test_device = device_create(cls,NULL,devno,NULL,"test");
        if(IS_ERR(test_device ))
        {
            class_destroy(cls);
            unregister_chrdev(major,"hello");
            return result;
        }
        sem_init(&sem,1);
        return 0;
    }
    static void hello_exit(void)
    {
        printk("hello_exit \n");
        device_destroy(cls,devno);    
        class_destroy(cls);
        unregister_chrdev(major,"hello");
        return;
    }
    module_init(hello_init);
    module_exit(hello_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("daniel.peng");

    测试程序 test.c

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    main()
    {
        int fd;
        
        printf("before open\n ");    
        fd = open("/dev/test",O_RDWR);  //原子变量  0
        if(fd<0)
        {
            perror("open fail \n");
            return;
        }
        printf("open ok ,sleep......\n ");    
        sleep(20);
        printf("wake up from sleep!\n ");        
        close(fd);   //加为1
    }

    编译步骤

    make 生成 hello.ko
    gcc test.c -o a
    gcc test.c -o b

    测试步骤

    • 安装驱动

    insmod hello.ko
    • 先运行进程A,在运行进程B

    可见进程A成功打开设备,在进程A sleep期间会一直占有该字符设备,进程B由于无法获得信号量,进入休闲,结合代码可知,进程B被阻塞在函数open()中。

    • 进程A 结束了sleep,并释放字符设备以及信号量,进程B被唤醒获得信号量,并成功打开了字符设备。

    • 进程B执行完sleep函数后退出,并释放字符设备和信号量。

     

    读-写信号量


    跟自旋锁一样,信号量也有区分读-写信号量之分。

    如果一个读写信号量当前没有被写者拥有并且也没有写者等待读者释放信号量,那么任何读者都可以成功获得该读写信号量;否则,读者必须被挂起直到写者释放该信号量。如果一个读写信号量当前没有被读者或写者拥有并且也没有写者等待该信号量,那么一个写者可以成功获得该读写信号量,否则写者将被挂起,直到没有任何访问者。因此,写者是排他性的,独占性的。

    读写信号量有两种实现,一种是通用的,不依赖于硬件架构,因此,增加新的架构不需要重新实现它,但缺点是性能低,获得和释放读写信号量的开销大;另一种是架构相关的,因此性能高,获取和释放读写信号量的开销小,但增加新的架构需要重新实现。在内核配置时,可以通过选项去控制使用哪一种实现。

    读写信号量的相关API:

    DECLARE_RWSEM(name)

    该宏声明一个读写信号量name并对其进行初始化。

    void init_rwsem(struct rw_semaphore *sem);

    该函数对读写信号量sem进行初始化。

    void down_read(struct rw_semaphore *sem);

    读者调用该函数来得到读写信号量sem。该函数会导致调用者睡眠,因此只能在进程上下文使用。

    int down_read_trylock(struct rw_semaphore *sem);

    该函数类似于down_read,只是它不会导致调用者睡眠。它尽力得到读写信号量sem,如果能够立即得到,它就得到该读写信号量,并且返回1,否则表示不能立刻得到该信号量,返回0。因此,它也可以在中断上下文使用。

    void down_write(struct rw_semaphore *sem);

    写者使用该函数来得到读写信号量sem,它也会导致调用者睡眠,因此只能在进程上下文使用。

    int down_write_trylock(struct rw_semaphore *sem);

    该函数类似于down_write,只是它不会导致调用者睡眠。该函数尽力得到读写信号量,如果能够立刻获得,就获得该读写信号量并且返回1,否则表示无法立刻获得,返回0。它可以在中断上下文使用。

    void up_read(struct rw_semaphore *sem);

    读者使用该函数释放读写信号量sem。它与down_read或down_read_trylock配对使用。

    如果down_read_trylock返回0,不需要调用up_read来释放读写信号量,因为根本就没有获得信号量。

    void up_write(struct rw_semaphore *sem);

    写者调用该函数释放信号量sem。它与down_write或down_write_trylock配对使用。如果down_write_trylock返回0,不需要调用up_write,因为返回0表示没有获得该读写信号量。

    void downgrade_write(struct rw_semaphore *sem);

    该函数用于把写者降级为读者,这有时是必要的。因为写者是排他性的,因此在写者保持读写信号量期间,任何读者或写者都将无法访问该读写信号量保护的共享资源,对于那些当前条件下不需要写访问的写者,降级为读者将,使得等待访问的读者能够立刻访问,从而增加了并发性,提高了效率。

    读写信号量适于在读多写少的情况下使用,在linux内核中对进程的内存映像描述结构的访问就使用了读写信号量进行保护。


    https://mp.weixin.qq.com/s/79-Sgh6G7x4cpJmSXWz47g

    展开全文
  • FreeRTOS-互斥信号量

    千次阅读 2017-10-08 11:41:05
    0.前言  在嵌入式操作系统中互斥信号量是任务间资源保护的重要手段。下面结合一个具体例子说明FreeRTOS中的互斥信号量... 互斥信号量使用方法如图1所示。在多数情况下,互斥信号量和二值型信号非常

    原文地址:http://blog.csdn.net/xukai871105/article/details/43456985


    0.前言

        在嵌入式操作系统中互斥型信号量是任务间资源保护的重要手段。下面结合一个具体例子说明FreeRTOS中的互斥型信号量如何使用。


    1.基本说明
        互斥型信号量的使用方法如图1所示。在多数情况下,互斥型信号量和二值型信号非常相似,但是从功能上二值型信号量用于同步,而互斥型信号量用于资源保护。互斥型信号量和二值型信号量还有一个最大的区别,互斥型信号量可以有效解决优先级反转现象。

    图1 互斥型信号量使用方法

    (1)互斥信号量的简介
    在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源.
    (2)优先级继承
    当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级的任务的优先级提升到与自己相同优先级,这个过程就是优先级继承。
    优先级继承尽可能降低了高优先级任务处于阻塞态的时间,并将已经出现的“优先级翻转的影响降低到最低.
    (3)互斥信号量不能用在中断服务函数中,原因如下:
    互斥信号量具有优先级继承机制,所以只能用在任务中,不能用在中断服务函数中。
    中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
    (4)二值信号量 与 互斥信号量的区别
    互斥信号量拥有优先级继承机制,二值信号量没有优先级继承机制
    二值信号量更加适合用于任务同步,互斥信号量更加适合用于互斥访问

    2.参考代码

        本例具有两个任务,两个任务都试图通过串口打印内容,此时串口就好比一个“资源”,某个任务使用串口资源时必须保护该资源,使用完串口之后在释放资源。保护和释放动作便对应互斥型信号量的两个基本操作,xSemaphoreTake和xSemaphoreGive。
        【代码】
    /* Standard includes. */
    #include <stdio.h>
    #include <string.h>
    
    /* Scheduler includes. */
    #include "FreeRTOS.h"
    #include "task.h"
    #include "queue.h"
    #include "semphr.h"
    
    /* Library includes. */
    #include "stm32f10x.h"
    
    #define LED0_ON()   GPIO_SetBits(GPIOB,GPIO_Pin_5);
    #define LED0_OFF()  GPIO_ResetBits(GPIOB,GPIO_Pin_5);
    
    static void Setup(void);
    void TaskA( void *pvParameters );
    void TaskB( void *pvParameters );
    
    void LedInit(void);
    void UART1Init(void);
    
    /* 互斥信号量句柄 */
    SemaphoreHandle_t xSemaphore = NULL;
    
    int main(void)
    {
        /* 初始化硬件平台 */
        Setup();
        /* 创建互斥信号量 */
        xSemaphore = xSemaphoreCreateMutex();
        /* 建立任务 */
        xTaskCreate( TaskA, "TaskA", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+3, NULL );
        xTaskCreate( TaskB, "TaskB", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+4, NULL );
        /* 启动OS */
        vTaskStartScheduler();
        
        return 0;
    }
    
    void TaskA( void *pvParameters )
    {
        for( ;; )
        {
            xSemaphoreTake( xSemaphore, portMAX_DELAY );
            {
                printf("Task A\r\n");
            }
            xSemaphoreGive( xSemaphore );
            vTaskDelay( 2000/portTICK_RATE_MS );
        }
    }
    
    void TaskB( void *pvParameters )
    {
        for( ;; )
        {
            xSemaphoreTake( xSemaphore, portMAX_DELAY );
            {
                printf("Task B\r\n");
            }
            xSemaphoreGive( xSemaphore );
            vTaskDelay( 1000/portTICK_RATE_MS );
        }
    }
    
    static void Setup( void )
    {
        LedInit();
        UART1Init();
    }
    
    void LedInit( void )
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
        /*LED0 @ GPIOB.5*/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_Init( GPIOB, &GPIO_InitStructure );    
    }
    
    void UART1Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        
        /* 第1步:打开GPIO和USART时钟 */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
        
        /* 第2步:将USART1 Tx@PA9的GPIO配置为推挽复用模式 */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        
        /* 第3步:将USART1 Rx@PA10的GPIO配置为浮空输入模式 */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        
        /* 第4步:配置USART1参数
        波特率   = 9600
        数据长度 = 8
        停止位   = 1
        校验位   = No
        禁止硬件流控(即禁止RTS和CTS)
        使能接收和发送
        */
        USART_InitStructure.USART_BaudRate = 9600;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        USART_Init(USART1, &USART_InitStructure);
        
        /* 第5步:使能 USART1, 配置完毕 */
        USART_Cmd(USART1, ENABLE);
        
        /* 清除发送完成标志 */
        USART_ClearFlag(USART1, USART_FLAG_TC);
        
        /* 使能USART1发送中断和接收中断,并设置优先级 */
        NVIC_InitTypeDef NVIC_InitStructure;
        // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        /* 设定USART1 中断优先级 */
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configLIBRARY_KERNEL_INTERRUPT_PRIORITY; 
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
        /* 使能接收中断 */
        // USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 
    }
    
    int fputc(int ch, FILE *f)
    {
        /* 写一个字节到USART1 */
        USART_SendData(USART1, (uint8_t) ch);
        /* 等待发送结束 */
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
        {}
        return ch;
    }

    3.简单说明
    SemaphoreHandle_t xSemaphore = NULL;
        申明互斥型信号量,在FreeRTOS中二值型信号量和互斥型信号量类型完全相同。
    xSemaphore = xSemaphoreCreateMutex();
        创建互斥型信号量。
    xSemaphoreTake( xSemaphore, portMAX_DELAY );
        获得资源的使用权,此处的等待时间为portMAX_DELAY(挂起最大时间),如果任务无法获得资源的使用权,任务会处于挂起状态。
     xSemaphoreGive( xSemaphore );
        释放资源的使用权。

    4.总结
        互斥型信号量和二值型信号量使用方法相似,但二值型信号量用于同步而互斥型信号量用于资源保护。

    展开全文
  • UCOSIII---互斥信号量

    2021-05-13 21:46:22
    介绍互斥信号量之前,首先给大家简单的说一下优先级反转,优先级反转是实时操作系统中常要面对的一个问题,...有一种方法可以避免这一现象的出现:在UCOSIII中有一种特殊的信号量可以胜任这一任务,那就是互斥信号量

    介绍互斥信号量之前,首先给大家简单的说一下优先级反转,优先级反转是实时操作系统中常要面对的一个问题,而且它只出现在使用基于优先级的可剥夺型内核时。

    优先级反转栗子
    在这里插入图片描述
    在这里插入图片描述
    在这种情况下,任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待直到任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权,使得任务H的情况更加恶化,这样就相当于任务M的优先级高于任务H,导致优先级反转。
    有一种方法可以避免这一现象的出现:在UCOSIII中有一种特殊的信号量可以胜任这一任务,那就是互斥信号量。

    互斥信号量

    UCOSIII支持一种特殊的二进制信号量,其被称作互斥信号量。用它可以解决无界优先级反转的问题
    在这里插入图片描述
    在这里插入图片描述
    注意!只有任务才能使用互斥信号量(中断服务程序则不可以),UCOSIII允许用户嵌套使用互斥型信号量,一旦一个任务获得了一个互斥型信号量,则该任务最多可以对该互斥型信号量嵌套使用250次,当然该任务只有释放相同的次数才能真正释放这个互斥型信号量。

    至于互斥信号量的思想:将拥有信号量的任务优先级暂时提升至目前正在等待该信号量的任务中的最高优先级

    互斥信号量API函数

    在这里插入图片描述
    与普通信号量一样,在本节中只讨论创建OSMutexCreate(),等待OSMutexPend(),发布OSMutexPost()。

    如何创建互斥信号量

    创建互斥信号量使用函数OSMutexCreate(),函数如下:

    void  OSMutexCreate (OS_MUTEX  *p_mutex,                        //指向互斥型信号量控制块
                         CPU_CHAR  *p_name,                            //互斥信号量的名字
                         OS_ERR    *p_err)
    {
        CPU_SR_ALLOC();
     
        OS_CRITICAL_ENTER();
        p_mutex->Type              =  OS_OBJ_TYPE_MUTEX;        /* Mark the data structure as a mutex                     */
        p_mutex->NamePtr           =  p_name;
        p_mutex->OwnerTCBPtr       = (OS_TCB       *)0;
        p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)0;         /* Mutex is available                                     */
        p_mutex->TS                = (CPU_TS        )0;
        p_mutex->OwnerOriginalPrio =  OS_CFG_PRIO_MAX;
        OS_PendListInit(&p_mutex->PendList);                    /* Initialize the waiting list                            */
     
        OSMutexQty++;
     
        OS_CRITICAL_EXIT_NO_SCHED();
       *p_err = OS_ERR_NONE;
    }
    

    可以得知互斥信号量是一个二进制信号量。

    请求互斥型信号量

    当一个任务需要对资源进行独占式访问的时候就可以使用函数OSMutexPend(),如果该互斥信号量正在被其他的任务使用,那么UCOSIII就会将请求这个互斥信号量的任务放置在这个互斥信号量的等待表中。任务会一直等待,直到这个互斥信号量被释放掉,或者设定的超时时间到达为止。如果在设定的超时时间到达之前信号量被释放,UCOSIII将会恢复所有等待这个信号量的任务中优先级最高的任务

    void  OSMutexPend (OS_MUTEX  *p_mutex,                        //指向互斥信号量
                       OS_TICK    timeout,                        //指定等待互斥信号量的超时时间(时钟节拍数)
                       OS_OPT     opt,                           //用于选择是否使用阻塞模式
                       CPU_TS    *p_ts,                            //指向一个时间戳
                       OS_ERR    *p_err)
    {
        OS_PEND_DATA  pend_data;
        OS_TCB       *p_tcb;
        CPU_SR_ALLOC();
     
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = (CPU_TS  )0;                                /* Initialize the returned timestamp                      */
        }
     
        CPU_CRITICAL_ENTER();
        if (p_mutex->OwnerNestingCtr == (OS_NESTING_CTR)0) {    /* Resource available?                                    */
            p_mutex->OwnerTCBPtr       =  OSTCBCurPtr;          /* Yes, caller may proceed                                */
            p_mutex->OwnerOriginalPrio =  OSTCBCurPtr->Prio;
            p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)1;
            if (p_ts != (CPU_TS *)0) {
               *p_ts  = p_mutex->TS;
            }
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_NONE;
            return;
        }
     
        if (OSTCBCurPtr == p_mutex->OwnerTCBPtr) {              /* See if current task is already the owner of the mutex  */
            p_mutex->OwnerNestingCtr++;
            if (p_ts != (CPU_TS *)0) {
               *p_ts  = p_mutex->TS;
            }
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_MUTEX_OWNER;                         /* Indicate that current task already owns the mutex      */
            return;
        }
     
        if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
            return;
        } else {
            if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
                CPU_CRITICAL_EXIT();
               *p_err = OS_ERR_SCHED_LOCKED;
                return;
            }
        }
     
        OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
        p_tcb = p_mutex->OwnerTCBPtr;                           /* Point to the TCB of the Mutex owner                    */
        if (p_tcb->Prio > OSTCBCurPtr->Prio) {                  /* See if mutex owner has a lower priority than current   */
            switch (p_tcb->TaskState) {
                case OS_TASK_STATE_RDY:
                     OS_RdyListRemove(p_tcb);                   /* Remove from ready list at current priority             */
                     p_tcb->Prio = OSTCBCurPtr->Prio;           /* Raise owner's priority                                 */
                     OS_PrioInsert(p_tcb->Prio);
                     OS_RdyListInsertHead(p_tcb);               /* Insert in ready list at new priority                   */
                     break;
     
                case OS_TASK_STATE_DLY:
                case OS_TASK_STATE_DLY_SUSPENDED:
                case OS_TASK_STATE_SUSPENDED:
                     p_tcb->Prio = OSTCBCurPtr->Prio;           /* Only need to raise the owner's priority                */
                     break;
     
                case OS_TASK_STATE_PEND:                        /* Change the position of the task in the wait list       */
                case OS_TASK_STATE_PEND_TIMEOUT:
                case OS_TASK_STATE_PEND_SUSPENDED:
                case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
                     OS_PendListChangePrio(p_tcb,
                                           OSTCBCurPtr->Prio);
                     break;
     
                default:
                     OS_CRITICAL_EXIT();
                    *p_err = OS_ERR_STATE_INVALID;
                     return;
            }
        }
     
        OS_Pend(&pend_data,                                     /* Block task pending on Mutex                            */
                (OS_PEND_OBJ *)((void *)p_mutex),
                 OS_TASK_PEND_ON_MUTEX,
                 timeout);
     
        OS_CRITICAL_EXIT_NO_SCHED();
     
        OSSched();                                              /* Find the next highest priority task ready to run       */
     
        CPU_CRITICAL_ENTER();
        switch (OSTCBCurPtr->PendStatus) {
            case OS_STATUS_PEND_OK:                             /* We got the mutex                                       */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                *p_err = OS_ERR_NONE;
                 break;
     
            case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                *p_err = OS_ERR_PEND_ABORT;
                 break;
     
            case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get mutex within timeout       */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = (CPU_TS  )0;
                 }
                *p_err = OS_ERR_TIMEOUT;
                 break;
     
            case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
                 if (p_ts != (CPU_TS *)0) {
                    *p_ts  = OSTCBCurPtr->TS;
                 }
                *p_err = OS_ERR_OBJ_DEL;
                 break;
     
            default:
                *p_err = OS_ERR_STATUS_INVALID;
                 break;
        }
        CPU_CRITICAL_EXIT();
    }
    

    发送互斥信号量

    我们可以通过调用函数OSMutexPost()来释放互斥型信号量,只有之前调用过函数OSMutexPend()获取互斥信号量,才需要调用OSMutexPost()函数来释放这个互斥信号量

    void  OSMutexPost (OS_MUTEX  *p_mutex,                    //指向互斥信号量
                       OS_OPT     opt,                    //用来指定是否进行任务调度操作
                       OS_ERR    *p_err)
    {
        OS_PEND_LIST  *p_pend_list;
        OS_TCB        *p_tcb;
        CPU_TS         ts;
        CPU_SR_ALLOC();
     
        CPU_CRITICAL_ENTER();
        if (OSTCBCurPtr != p_mutex->OwnerTCBPtr) {              /* Make sure the mutex owner is releasing the mutex       */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_MUTEX_NOT_OWNER;
            return;
        }
     
        OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
        ts          = OS_TS_GET();                              /* Get timestamp                                          */
        p_mutex->TS = ts;
        p_mutex->OwnerNestingCtr--;                             /* Decrement owner's nesting counter                      */
        if (p_mutex->OwnerNestingCtr > (OS_NESTING_CTR)0) {     /* Are we done with all nestings?                         */
            OS_CRITICAL_EXIT();                                 /* No                                                     */
           *p_err = OS_ERR_MUTEX_NESTING;
            return;
        }
     
        p_pend_list = &p_mutex->PendList;
        if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) {         /* Any task waiting on mutex?                             */
            p_mutex->OwnerTCBPtr     = (OS_TCB       *)0;       /* No                                                     */
            p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)0;
            OS_CRITICAL_EXIT();
           *p_err = OS_ERR_NONE;
            return;
        }
                                                                /* Yes                                                    */
        if (OSTCBCurPtr->Prio != p_mutex->OwnerOriginalPrio) {
            OS_RdyListRemove(OSTCBCurPtr);
            OSTCBCurPtr->Prio = p_mutex->OwnerOriginalPrio;     /* Lower owner's priority back to its original one        */
            OS_PrioInsert(OSTCBCurPtr->Prio);
            OS_RdyListInsertTail(OSTCBCurPtr);                  /* Insert owner in ready list at new priority             */
            OSPrioCur         = OSTCBCurPtr->Prio;
        }
                                                                /* Get TCB from head of pend list                         */
        p_tcb                      = p_pend_list->HeadPtr->TCBPtr;
        p_mutex->OwnerTCBPtr       = p_tcb;                     /* Give mutex to new owner                                */
        p_mutex->OwnerOriginalPrio = p_tcb->Prio;
        p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)1;
                                                                /* Post to mutex                                          */
        OS_Post((OS_PEND_OBJ *)((void *)p_mutex),
                (OS_TCB      *)p_tcb,
                (void        *)0,
                (OS_MSG_SIZE  )0,
                (CPU_TS       )ts);
     
        OS_CRITICAL_EXIT_NO_SCHED();
     
        if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
            OSSched();                                          /* Run the scheduler                                      */
        }
     
       *p_err = OS_ERR_NONE;
    }
    

    何时可以用普通信号量代替互斥信号量

    如果没有任务对共享资源的访问有截至时间,那么普通信号量就可以代替互斥信号量,反之则必须使用互斥信号量。因为前者会造成无界优先级反转,而后者却不会。

    展开全文
  • uc/os-ii 互斥信号量及mutex.c源码分析

    千次阅读 2018-04-26 19:56:30
    互斥信号量互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。 ...

    互斥信号量:


    • 互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。
      解决优先级反转:当高优先级任务需要使用某个共享资源,而恰巧该共享资源又被一个低优先级任务占用时,优先级反转问题就会发生。为了降解优先级反转,内核就必须支持优先级继承,将低优先级任务的优先级提升到高于高优先级任务的优先级,直到低优先级任务处理完毕共享资源。

    OS_MUTEX.C中µC/OS-Ⅱ提供对互斥信号量进行管理的函数

    这里写图片描述

    • 事件控制块ECB结构
    typedef struct {
        INT8U   OSEventType;                      /* 事件类型  */
        INT8U   OSEventGrp;                    /*事件等待组*/
        INT16U  OSEventCnt;             /* 计数器(当事件是信号量时) */
        void   *OSEventPtr;             /*空事件块时用来链接空事件链表
                                   事件请求成功后指向占用任务的TCB指针*/
        INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; /*事件等待表*/
    } OS_EVENT;

    这里写图片描述

    1. OSEventTbl[]任务组 和 OSEventGrp任务表 很像前面讲到的OSRdyTbl[]和OSRdyGrp,前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
    2. OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。(EventWaitListInit()来初始化)
    3. OSEventCnt被分成了低8位和高8位两部分:
      低8位用来存放信号值(该值为((INT16U)0x00FFu)时,信号为有效,否则信号为无效)
      高8位用来存放为了减少出现优先级反转现象而要提升的优先级别prio。

    mutex中涉及的TCB

    这里写图片描述

    • OSTCBCur-指向“当前任务控制块”的指针;
      (当前占用cpu的任务)
      OSTCBFreeList-“空任务控制块”链表的表头指针;
      OSTCBHighRdy -指向“将要运行最高优先级任务控制块”的指针;
      OSTCBList-“已使用任务控制块”链表的表头指针;
    • OSTCBTbl[]-任务控制块数组,所有的任务控制块都保存在这个数组中。
    • 具体做法为:系统在调用函数OSInit()对ucos进行初始化时,就现在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],然后把各个元素连接成链表,从而形成一个空的任务块链表。
      这里写图片描述
      这里写图片描述
    • OSTCBPrioTbl[]-任务控制块优先级表,按任务的优先级别将这些指针存放在数组的各个元素里,专门用来存放指向各任务控制块的指针。访问任务控制块时,不需要遍历任务控制块链表。
    • OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* 放置一个非空指针防止被 其他人占用(设置OSTCKPrioTbl为有效)

    建立互斥信号量OSMutexCreate()

    • 1.创建一个互斥型信号量
      a.首先进行运行条件检查(函数执行参数检查,校验优先级,中断状态)
      b.检查PIP优先级是否被占用(该优先级是否有任务)
      c.检查是否有可用的空闲事件控制块

      a.b.c 都满足则建立互斥信号量
      d.取走了一个空闲ECB,调整空闲ECB链表指针
      e.紧接着将ECB设置成Mutex类型
      f.将事件控制块的计数器Cnt高8位设置为优先级,低8位全为1表示互斥信号量可用
      g.任务列表初始化完毕后,返回ECB指针。

    这里写图片描述

    *建立一个互斥型信号量 
    *描述:建立一个互斥型信号量 
    *参数:prio:当存取(访问)互斥型信号量时它的优先级。换言之,当任务需要信号量, 
    *                     而另一优先级更高的任务想得到信号量,就改变当前任务的优先级,变为更高 
    *                     假定你改变的优先级值小于任务竞争这个信号量的任务的值(即优先级更高) 
    *      Perr:指向返回应用程序的错误代码的指针: 
    *                               OS_ERR_NONE          如果调用成功 
    *                               OS_ERR_CREATE_ISR   如果想从ISR中建立互斥
    *                               OS_PRIO_EXIST       如果优先级继承优先级的优先级已经存在(优先级继承算法或优先级天花板算法) 
    *                               OS_ERR_PEVENT_NULL  没有事件控制块可用 
    *                               OS_PRIO_INVALID    如果你指定的优先级大于最大值(无效优先级) 
            INT8U:8位无符号char型变量
    *********************************************************************************************************
    */
    
    OS_EVENT  *OSMutexCreate (INT8U   prio,                             //prio是取得信号量的任务需要提升到的优先级
                              INT8U  *perr)                             //用于输出错误信息 
    {
        OS_EVENT  *pevent;                                          //指向事件控制块的指针(创建信号量的句柄)
    #if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
        OS_CPU_SR  cpu_sr = 0;                                  //得到当前处理器状态字的值,并将其保存在C函数局部变量之中
    #endif
    
    
    
    #if OS_ARG_CHK_EN > 0                                       //对μC/OS-III的大部分函数执行参数检查(>0)
        if (perr == (INT8U *)0) {                              /*校验 'perr'                          */
            return ((OS_EVENT *)0);
        }
        if (prio >= OS_LOWEST_PRIO) {                          /* 校验优先级,如果优先级无效                            */
            *perr = OS_ERR_PRIO_INVALID;                        //错误指向:无效优先级
            return ((OS_EVENT *)0);
        }
    #endif
        if (OSIntNesting > 0) {                                /* 如果在中断中(调度器加锁)             */
            *perr = OS_ERR_CREATE_ISR;                         /* 错误指向:不允许在中断中建立互斥      */
            return ((OS_EVENT *)0);
        }
        OS_ENTER_CRITICAL();                                    /*进入临界区,关中断(保护临界区)
        if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* 判断需要提升到的优先级上是否有建立任务    */
            OS_EXIT_CRITICAL();                                /* 任务已优先存在,开中断,退出临界区*/
            *perr = OS_ERR_PRIO_EXIST;                         /* 错误指向:需要提升优先级已经有任务         */
            return ((OS_EVENT *)0);
        }//通过任务优先级号快速找到当前任务在任务控制块中的首地址(即OSTCBStkPtr地址)
        OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* 放置一个非空指针防止被其他人占用(设置OSTCKPrioTbl为有效)                  */
        pevent             = OSEventFreeList;                  /* 获取下一个空闲事件控制块       */
        if (pevent == (OS_EVENT *)0) {                         /* 如果ECB是可获取的,是可用的              */
            OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* 释放占有的优先级 ,释放表项         */
            OS_EXIT_CRITICAL();                                 //退出临界区
            *perr              = OS_ERR_PEVENT_NULL;           /* 错误指向:没有事件控制块可用             */
            return (pevent);
        }
        OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* 取走了一个空闲ECB,调整空闲ECB链表指针       */
        OS_EXIT_CRITICAL();                                  /*开中断*/
        pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;        /*设置ECB数据结构的事件类型为互斥信号量*/
        pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8) | OS_MUTEX_AVAILABLE; /* 将优先级数值存在事件计数值OSEventCnt的高8位 */
                                                /*AVAILABLE的值存储在低8位,其值为0x00FF。若低八位全为1,表示该互斥信号量可以用,否则就是不可用*/
                                                /*低8位在没有任务占用时为0xFF,有任务占用时,用于保存占用任务的优先级*/
        pevent->OSEventPtr     = (void *)0;                   /*只有在所定义的事件是邮箱或者消息队列时才使用(指向下一个ECB的指针),断开与空闲链表的联系*/
    #if OS_EVENT_NAME_EN > 0
        pevent->OSEventName    = (INT8U *)"?";                  /*事件名称*/
    #endif
        OS_EventWaitListInit(pevent);                       /*初始化ECB的任务等待表表,全为0,没有任务在等待事件 */
        *perr                  = OS_ERR_NONE;               /*指向:调用成功(互斥信号量创建成功)
        return (pevent);                                    
    }
    
    /*$PAGE*/

    等待(申请)互斥信号量OSMutexPend()

    • 描述: pevent :是一个指向所需的相关联的事件控制块的指针
      timeout:等待超时时限
      如果非0,您的任务将等待资源达到该参数指定的时间量。
      如果为0,任务将永远在指定的位置等待互斥信号量,直到资源可用为止。
      perr : 指向错误代码 OS_ERR_NONE 调用成功,任务拥有互斥锁。
      OS_ERR_TIMEOUT 互斥锁在指定的“超时”内不可用。
      OS_ERR_PEND_ABORT 互斥锁上的等待被异常中止。.
      OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量
      OS_ERR_PEVENT_NULL ‘pevent’ 是空指针
      OS_ERR_PEND_ISR ISR调用这个函数时错误.
      OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级(互斥机制失效)
      OS_ERR_PEND_LOCKED 调度器上锁不能等待信号量
    • 操作流程:
      a.进行运行条件检查
      b.足运行条件后,首先检查是否有Mutex可用,如果有可用的Mutex,则占用这个Mutex,调用者获得共享资源的使用权;
      c.如果没有可用的Mutex,则需要检查Mutex的占用者是否需要提升优先级;(从ECB中取得占用的TCB指针和优先级,判断只有当占用ECB的优先级比升级优先级和当前请求优先级都低的时候才进行升级,)
      d.对需要提升优先级的占用者进行一系列处理;
      对占用任务判定是否就绪,就绪就更新新优先级的就绪组和就绪表,未就绪则更新ECB的等待数和等待表
      f.挂起调用者,并调用调度函数切换最高优先级任务;
      g.当调用者再次运行时,需要检查调用者是因为超时还是得到Mutex而运行的,并作相应处理。
    #define  OS_MUTEX_KEEP_LOWER_8   ((INT16U)0x00FFu)   /*设置低8位全为1*/
    #define  OS_MUTEX_KEEP_UPPER_8   ((INT16U)0xFF00u)   /*设置高8位全为1*/
    #define  OS_MUTEX_AVAILABLE      ((INT16U)0x00FFu)
        INT8U      pip;                                              /* 优先级继承优先级 (PIP)      */
    
          注:Pip变量是保存在OSEventCnt中的,当我们创建信号量的时候,就会给定这个值,  这个值也就是系统能够将等待该互斥信号量的任务提升的最高优先级
    
    
        INT8U      mprio;                                       /* 占用信号量的任务自己的优先级         */
        BOOLEAN    rdy;                                          /* Flag indicating task was ready 任务已就绪标志    */
        OS_TCB    *ptcb;                                             /*任务控制块结构体(占用互斥信号量的控制块)*/
        OS_EVENT  *pevent2;                                     /*指向任务未就绪时等待事件的指针*/
        INT8U      y;               /*优先级高3位对应的数值*/
    
    
    #if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
        OS_CPU_SR  cpu_sr = 0;
    #endif
    
    
    /*进行运行条件检查*/
    #if OS_ARG_CHK_EN > 0                                       /*对μC/OS-III的大部分函数执行参数检查(>0)
        if (perr == (INT8U *)0) {                               /* 校验 'perr'                          */
            return;
        }
        if (pevent == (OS_EVENT *)0) {                          /* 校验 'pevent'                        */
            *perr = OS_ERR_PEVENT_NULL;                         /*错误指向:空指针
            return;
        }
    #endif
        if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {       /* 校验事件控制块(互斥信号量)                */
            *perr = OS_ERR_EVENT_TYPE;                          /*错误指向:不是互斥信号量类型
            return;
        }
        if (OSIntNesting > 0) {                                /* 如果在中断               */
            *perr = OS_ERR_PEND_ISR;                           /* 错误指向:ISR调用这个函数和结果将导致阻塞               */
            return;
        }
        if (OSLockNesting > 0) {                               /*如果调度程序锁定   */
            *perr = OS_ERR_PEND_LOCKED;                        /*错误指向:调度任务锁住阻塞              */
            return;
        }
    /*$PAGE*/
       OS_ENTER_CRITICAL();             /*进入临界区*/
        pip = (INT8U)(pevent->OSEventCnt >> 8);                  /* Get PIP from mutex 事件计数器高8位保存PIP */
                                                                         /* Is Mutex available?                      */
        if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
        {           /*提取低8位,看是否全为1,来判断互斥信号量是否可用。*/
            pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;      
                             /* 修改高8位的数据,这个时候高8位数据不变,
                            还是表示继承优先级,低8位清零。               */
            pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;        
                            /* 将申请任务的优先级保存在低8位之中。低八位不可能全                        部都为零,所以依旧可以表示信号量是否被占用。*/
    
      pevent->OSEventPtr  = (void *)OSTCBCur;                  /* OSEventPtr指针指向当前占用信号的任务控制块*/
                                        /*获取mutex*/
            if (OSTCBCur->OSTCBPrio <= pip) {                 
                              /*申请信号量任务的优先级高于继承优先级(数值上小于)*/
                OS_EXIT_CRITICAL();                                                                        /*退出临界区 */
                *perr = OS_ERR_PIP_LOWER;                /*错误指向:申请优先级高于继承优先级的优先级*/  
                        /*不需要提高优先级*/ 
    
          } else {                    /*申请任务的优先级高于继承优先级的优先级*/
                OS_EXIT_CRITICAL();
                *perr = OS_ERR_NONE;                                           /*指向:调用成功*/
            }
            return;
        }
    
        mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
                           /*取得互斥信号量任务的优先级(因为被占用,所以低8位为优先级)*/
        ptcb  = (OS_TCB *)(pevent->OSEventPtr);                         /*从信号量中获得占用该信号量的任务*/
    
        if (ptcb->OSTCBPrio > pip) {                    /*占用的任务优先级低于最高继承优先级*/                        
    
            if (mprio > OSTCBCur->OSTCBPrio) {  
                                   /*且原先占用该mutex的优先级比提出申请该mutex的任务                          的优先级低则提升原任务的优先级至PIP*/
                y = ptcb->OSTCBY;           /*优先级高3位对应的数值(=prio>>3)*/
                if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0) {      /* 看取得信号量的任务是否就绪 (对应就绪表的值)  */
                    OSRdyTbl[y] &= ~ptcb->OSTCBBitX;                                        /*   若原任务已就绪,则将其从就                                  绪表中删除,并置就绪标志rdy*/ 
                    if (OSRdyTbl[y] == 0) {                                                           
                        OSRdyGrp &= ~ptcb->OSTCBBitY;
                    }
                    rdy = OS_TRUE;
                    } else {//若没有就绪,在等待一个事件
    
      pevent2 = ptcb->OSTCBEventPtr;                    /*指向未就绪任务等待的事件*/
                  if (pevent2 != (OS_EVENT *)0) {                        /* 任务存在等待事件发生       */
                        if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
                            pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;           /*清除对应的等待表和等待组*/
                        }
                    }
                    rdy = OS_FALSE;                                           /* 标记任务未就绪  */
         }
                ptcb->OSTCBPrio = pip;                                          /* 提升占用信号的优先级至pip         */
                 /*根据新取得的优先级,修改优先级,更新任务控制块的相关数据。*/
    
    /*OSRdyGrp |= OSMapTbl[prio >> 3];  原先的查找表设置
    OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];*/
    //不查表,直接把0x01左移动 OSRdyTbl[]元素即可
    
    #if OS_LOWEST_PRIO <= 63    /*改进*/
                ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3);            /*优先级的高三位*/
                ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07); /*优先级的低三位*/
                ptcb->OSTCBBitY = (INT8U)(1u << ptcb->OSTCBY);            /*优先级在就绪组的位置*/
                ptcb->OSTCBBitX = (INT8U)(1u << ptcb->OSTCBX);      /*优先级在就绪表的位置*/
    #else
                ptcb->OSTCBY    = (INT8U)((ptcb->OSTCBPrio >> 4) & 0xFF);
                ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x0F);
                ptcb->OSTCBBitY = (INT16U)(1u << ptcb->OSTCBY);
                ptcb->OSTCBBitX = (INT16U)(1u << ptcb->OSTCBX);
    #endif
    if (rdy == OS_TRUE) {                      /* 如果取得信号量的任务为就绪状态,则继续让新的优先级就绪*/
                    OSRdyGrp               |= ptcb->OSTCBBitY; /* 更新就绪数组和就绪表(任务的新优先级)    */
                    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                } else {//如果不是就绪
                    pevent2 = ptcb->OSTCBEventPtr;
                    if (pevent2 != (OS_EVENT *)0) {            /* 将其更新ECB的等待数组和等待表(Add to event                                         wait list) */
                        pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                        pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    }
                }
                OSTCBPrioTbl[pip] = ptcb;           /*优先级数组指针指向取得的任务控制块*/
            }
        }
        //到这说明前面已经把占用信号量任务的优先级提高
            //当前任务TCB域处理:
    
        OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;    /*#define  OS_STAT_MUTEX ox10u */    
                            /* 将当前任务的状态设为等待互斥信号量*/
        OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;/* 事件等待标志:事件发生,等待结                                               束*/
        OSTCBCur->OSTCBDly       = timeout;                   /* 在任务控制块设置延时时间*/
    
        OS_EventTaskWait(pevent);                         /* 取消申请任务的的就绪状态,设置等待事件                          阻塞,直到时间超时或者是事件发生  */
            OS_EXIT_CRITICAL();
        OS_Sched();                                             /* 执行调度,运行最高优先级的就绪任务
                如果原来低优先级的任务优先级被抬高了,则该任务将被执行 */
        OS_ENTER_CRITICAL();
        //原先占用mutex的任务执行完成释放了mutex,并唤醒了等待该mutex的最高优先级的任务
        switch (OSTCBCur->OSTCBStatPend) {                /* 检查调用者是因为超时还是得到Mutex而运行*/
            case OS_STAT_PEND_OK:   /*事件发生,正确等待目标信号量的到来*/               
                 *perr = OS_ERR_NONE;               /*正常返回*/
                 break;
    
            case OS_STAT_PEND_ABORT:    /*异常终止,其它任务把目标信号量删除,任务异常终止*/
                 *perr = OS_ERR_PEND_ABORT;                    /* 异常终止      */
                 break;
    
            case OS_STAT_PEND_TO:   /*等待超时*/
            default:
                 OS_EventTaskRemove(OSTCBCur, pevent);      /*将该任务从该等待事件中取消*/
                 *perr = OS_ERR_TIMEOUT;                            /* 时间超时没有得到互斥信号量   */
                 break;
        }
        OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      /*   任务当前状态               */
                             /*#define OS_STAT_RDY  ox00u */
        OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  /*等待事件发生,等待结束*/
    
        OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;           /* 清除事件控制块指针*/
                            /*表示当前任务没有事件阻塞*/
    /*EVENT_MULTI_EN表示等待多个事件*/
    #if (OS_EVENT_MULTI_EN > 0)
    
        OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;      /*清除指向多事件控制块指针*/
    
    #endif
    
        OS_EXIT_CRITICAL();
    }

    恢复任务的优先级RdyAtPrio ()

    • 描述:
      当释放信号量的任务的优先级为等于互斥信号量最高可提升优先级的时候,
      需要通过OSMutex_RdyAtPrio函数来将任务的优先级恢复

           功能是将ptcb指向的任务控制块的任务的优先级改为prio并且就绪 
      
    • 操作流程:
      a.让升级了的任务优先级恢复原优先级并使之就绪,首先清0这个任务在就绪表、组,然后获取TCB中的得到相关域。
      b.就绪表、组标记为原优先级的就绪状态,优先级指针表存TCB *。
      参数: INT8U y;
    static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb, 
                                     INT8U    prio)
    {
        y            =  ptcb->OSTCBY;                       /* 获取任务就绪表OSRdyGrp高三位*/   
       /* 将提升到高优先级从就绪表和就绪数组中清除   */
        OSRdyTbl[y] &= ~ptcb->OSTCBBitX;   /*清除就绪表*/       
        if (OSRdyTbl[y] == 0) {//为0对应       /*清除就绪组*/
            OSRdyGrp &= ~ptcb->OSTCBBitY;
        }
        ptcb->OSTCBPrio         = prio;     /*将任务控制块的任务优先级恢复到prio*/
    /*更新TCB任务控制块的跟优先级相关的数据成员*/
    #if OS_LOWEST_PRIO <= 63
    
        ptcb->OSTCBY            = (INT8U)((prio >> (INT8U)3) & (INT8U)0x07);
        ptcb->OSTCBX            = (INT8U) (prio & (INT8U)0x07);
        ptcb->OSTCBBitY         = (INT8U)(1u << ptcb->OSTCBY);
        ptcb->OSTCBBitX         = (INT8U)(1u << ptcb->OSTCBX);
    #else
        ptcb->OSTCBY            = (INT8U)((prio >> (INT8U)4) & (INT8U)0x0F);
        ptcb->OSTCBX            = (INT8U) (prio & (INT8U)0x0F);
        ptcb->OSTCBBitY         = (INT16U)(1u << ptcb->OSTCBY);
        ptcb->OSTCBBitX         = (INT16U)(1u << ptcb->OSTCBX);
    
    
    #endif
        OSRdyGrp               |= ptcb->OSTCBBitY;               /* 更新就绪组     */
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;              
                                    /*更新就绪表*/
        OSTCBPrioTbl[prio]      = ptcb;                         
                    /*将对应优先级的数组指正指向该任务控制块*/
        }
    
    
    #endif                                                     /* OS_MUTEX_EN                              */

    无等待的获取互斥信号量(非阻塞OSMutexAccept ()

    • 参数: pevent 指向任务控制块的指针
      perr 一个指向一个错误代码将返回到您的应用程序:
      os_err_none 如果调用成功。
      os_err_event_type ‘pevent’不是一个指向互斥信号量
      os_err_pevent_null ‘pevent’是一个空指针
      os_err_pend_isr 如果从ISR调用这个函数
      os_err_pip_lower 申请任务的优先级比继承优先级小
    • Returns : == OS_TRUE 如果信号量可用,互斥信号量可采集
      == OS_FALSE 如果信号量不可用

    • 操作流程:
      a.进行运行条件检查(函数执行参数检查,验证事件块类型,中断状态)
      b.关中段保护临界区
      c.获取事件计数器高8位存放pip
      d.判断事件计数器低8位数值看信号量是否可用
      f.如果可用,到8位保存pip,低8位保存任务的优先级
      g.任务获得信号量

    #if OS_MUTEX_ACCEPT_EN > 0
    BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,                    //非阻塞的获取互斥型信号量函数
                            INT8U     *perr)
    {
         INT8U      pip;                                           /* 优先级继承优先级 (PIP)          */
    #if OS_CRITICAL_METHOD == 3                            /* 为CPU状态寄存器分配存储      */
        OS_CPU_SR  cpu_sr = 0;
    #endif
    
    #if OS_ARG_CHK_EN > 0       /*对μC/OS-III的大部分函数执行参数检查(>0)*/
    
        if (perr == (INT8U *)0) {                             /* 验证 'perr'  */
            return (OS_FALSE);          /*perr为null返回false*/
        }
    
        if (pevent == (OS_EVENT *)0) {                     /* 验证 'pevent' */
            *perr = OS_ERR_PEVENT_NULL;     /*错误指向:'pevent'是一个空指针*/
            return (OS_FALSE);
        }
    #endif
        if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {  
                            /* 验证事件块类型(不为互斥信号类型)*/
    
            *perr = OS_ERR_EVENT_TYPE;      /*错误指向:perr 不是一个指向互斥信号量*/
            return (OS_FALSE);
        }
    
        if (OSIntNesting > 0) {                                    /*确保它不是从ISR调用的    */
            *perr = OS_ERR_PEND_ISR;            /*错误指向:从ISR调用这个函数*/
            return (OS_FALSE);  
        }
        OS_ENTER_CRITICAL();                                      /* Get value (0 or 1) of Mutex  关中断(保护临界区*/
    
        pip = (INT8U)(pevent->OSEventCnt >> 8);            /* Get PIP from mutex    事件计数器高8位保存PIP  */
        if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
                                /*提取低8位,看是否全为1,来判断互斥信号量是否可用*/
    
           pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  
                             /*  修改高8位的数据,这个时候高8位数据不变,
                                还是表示继承优先级,低8位清零。        */
    
            pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;    
                                 /* 将申请任务的优先级保存在低8位之中。
                      低八位不可能全部都为零,所以依旧可以表示信号量是否被占用。    */
            pevent->OSEventPtr  = (void *)OSTCBCur;       
                                 /* OSEventPtr指针指向任务控制块         */
                                    /*把任务占用信号量*/
                                        if (OSTCBCur->OSTCBPrio <= pip) {              /* 申请任务优先级高于继承优先级的数值。*/
                OS_EXIT_CRITICAL();                               /*   开中断                */
                *perr = OS_ERR_PIP_LOWER;       /*指向信息: 申请任务优先级高于继承优先级的                     数值,无需提高优先级*/
            } else {//反之    
                OS_EXIT_CRITICAL();                         
                *perr = OS_ERR_NONE;                     /*无等待的获取互斥信号量成功*/
            }
            return (OS_TRUE);                       /*资源可用,互斥信号量可采集*/
        }
        OS_EXIT_CRITICAL();
    
    
        *perr = OS_ERR_NONE;                    /*无等待的获取互斥信号量成功*/
    
         return (OS_FALSE);
         }
    #endif

    查询互斥信号量信息 OSMutexQuery ()

    • 功能:查询互斥信号量信息
      参数: pevent 是一个指向所需的互斥体相关的事件控制块。
      p_mutex_data 是一个指向一个数据结构,包含了互斥信息返回:

      OS_ERR_NONE 调用成功
      OS_ERR_QUERY_ISR If you called this function from an ISR
      OS_ERR_PEVENT_NULL If ‘pevent’ is a NULL pointer
      OS_ERR_PDATA_NULL ‘p_mutex_data’ 空指针
      OS_ERR_EVENT_TYPE 非互斥信号量类型获取数据错误

    • 操作流程:
      a.进行运行条件检查()
      b.p_mutex_data结构来存放互斥信号量的信息
      c.获取高八位优先继承优先和低八位任务优先级
      d.拷贝任务等待信号量表到状态数据结构中
    #if OS_MUTEX_QUERY_EN > 0
    INT8U  OSMutexQuery (OS_EVENT       *pevent,                    /*一个指向所需互斥体相关的事件控制块*/
                         OS_MUTEX_DATA  *p_mutex_data)              /*包换互斥信息的结构体
    {
        INT8U      i;
    #if OS_LOWEST_PRIO <= 63                                    /*优先级*/
        INT8U     *psrc;                                /*定义8位pevent->OSEventTbl[0]的地址指针*/
        INT8U     *pdest;                               /*定义8位pdata->OSEventTbl[0]的地址指针*/
    #else
        INT16U    *psrc;
        INT16U    *pdest;
    #endif
    #if OS_CRITICAL_METHOD == 3                      /* 为CPU状态寄存器分配存储         */
        OS_CPU_SR  cpu_sr = 0;
    #endif
    
    
    
        if (OSIntNesting > 0) {                                /* 如果在中断中             */
            return (OS_ERR_QUERY_ISR);                         /* 不能在中断中获取互斥信号量的信息      */
        }
    #if OS_ARG_CHK_EN > 0                                       /*对μC/OS-III的大部分函数执行参数检查(>0)
        if (pevent == (OS_EVENT *)0) {                         /* 校验 'pevent'                        */
            return (OS_ERR_PEVENT_NULL);
        }
        if (p_mutex_data == (OS_MUTEX_DATA *)0) {              /* 校验 'p_mutex_data'                  */
            return (OS_ERR_PDATA_NULL);                         /*错误指向:p_mutex_data 为空
        }
    #endif
        if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* 校验事件块类型            */
            return (OS_ERR_EVENT_TYPE);                         /*返回错误:不是互斥信号量类型
        }   
        OS_ENTER_CRITICAL();                                    /*关中断(保护临界区)
        p_mutex_data->OSMutexPIP  = (INT8U)(pevent->OSEventCnt >> 8); /*高8位的数据,即继承优先级的数值*/
        p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);/*低八位的数据,信号量拥有的优先级
        if (p_mutex_data->OSOwnerPrio == 0xFF) {                /*若低八位都为1   
            p_mutex_data->OSValue = OS_TRUE;                /*则表示信号量可用(未被占用)*/
        } else {
            p_mutex_data->OSValue = OS_FALSE;                   /*否则被占用*/
        }
        p_mutex_data->OSEventGrp  = pevent->OSEventGrp;       /* 拷贝任务等待信号量表到状态数据结构中*/
       /*指针遍历逐一赋值*/
        psrc = &pevent->OSEventTbl[0];  /*指向任务等待表*/     
        pdest = &p_mutex_data->OSEventTbl[0];/*指向存放等待表的地址*/
        for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
            *pdest++ = *psrc++;                                 /*地址指针下移一个类型地址,获取互斥信号量的值*/
        }
        OS_EXIT_CRITICAL();                                     /*开中断*/
        return (OS_ERR_NONE);
    }
    #endif                                                     /* OS_MUTEX_QUERY_EN*/

    释放互斥信号量OSMutexPost ()

    • 参数 : pevent 是一个指向所需的相关联的事件控制块的指针

    返回 : OS_ERR_NONE 调用成功
    OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量类型的指针
    OS_ERR_PEVENT_NULL ‘pevent’是一个空指针
    OS_ERR_POST_ISR 中断时不能释放互斥信号量
    OS_ERR_NOT_MUTEX_OWNER 完成该任务的任务不是互斥信号量的所有者
    OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级

    • 操作流程:

    a.进行运行条件检查(中断检测,校验’pevent’,校验事件块类型 )
    b.如果任务不是互斥信号量的所有者任务,则报错
    c.高优先级任务想得到Mutex时,如果Mutex占用者的优先级已经被升高,那么调用OSMutex_RdyAtPrio()函数使优先级升高了的任务恢复原来的优先级。

    d.如果有多个任务在等待一个Mutex,那么其中优先级最高的任务获得Mutex,设为就绪
    e.如果没有任务等待,低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用,OSEventPtr=0,占用的任务为空。

    INT8U  OSMutexPost (OS_EVENT *pevent)
    {
        INT8U      pip;                                   /* 优先级继承优先级                */
        INT8U      prio;            
    #if OS_CRITICAL_METHOD == 3                           /* 为CPU状态寄存器分配存储      */
        OS_CPU_SR  cpu_sr = 0;
    #endif
    
        if (OSIntNesting > 0) {                                     /* 如果在中断中 */
            return (OS_ERR_POST_ISR);                     /* 错误返回:不能在中断中释放互斥信号量 */
        }
    #if OS_ARG_CHK_EN > 0
        if (pevent == (OS_EVENT *)0) {                             /* 校验'pevent  */
            return (OS_ERR_PEVENT_NULL);                  /*错误指向:'pevent'是个空指针
        }
    #endif
        if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件块类型 (不是互斥信号量类型)                   */
            return (OS_ERR_EVENT_TYPE);                 /*错误指向:不是互斥信号量类型
        }
    
        OS_ENTER_CRITICAL();                    /*关中断(保护临界区)
        pip  = (INT8U)(pevent->OSEventCnt >> 8);          /* 事件计数器高八位取得优先级的继承优先级数值(PIP)*/
        prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
                               /* 提取低8位,取得信号量任务的原先优先级     */
        if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {         
                              /* 判断是提交信号量的任务是不是取得信号量的任务  */
            OS_EXIT_CRITICAL();                 /*开中断*/
            return (OS_ERR_NOT_MUTEX_OWNER);    /*错误指向:该任务不是互斥信号量的所有者任务*/
        }
        if (OSTCBCur->OSTCBPrio == pip) {                                      /* 如果取得信号量的任务的优先级等于继承优先级 */
                                /*判定已经优先级反转过*/
            OSMutex_RdyAtPrio(OSTCBCur, prio);                             /* 恢复取得信号量之前的优先级          */
        }
        OSTCBPrioTbl[pip] = OS_TCB_RESERVED;             /* 将pip优先级对应的任务优先级数组指针赋值                           为OS_TCB_RESERVED (占用)*/           
        if (pevent->OSEventGrp != 0) {                          /* 有等待任务时               */
             prio= OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
                                                                                       /*将等待信号量的最高优先级任务的设为就绪*/
            pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  /* 高8位保存 pip(继承优先级)      */
            pevent->OSEventCnt |= prio;             /*低八位保存刚刚转为就绪任务的优先级*/
            pevent->OSEventPtr  = OSTCBPrioTbl[prio];       
                                               /*对应优先级的任务控制块获得互斥信号*/
            if (prio <= pip) {                            /* 刚刚转为就绪任务的优先级高于继承优先级,mutex机制已经不起作用       */
                OS_EXIT_CRITICAL();                   /*开中断     
                OS_Sched();                                 /*  调度指向优先级最高的任务 */
                return (OS_ERR_PIP_LOWER);      /*返回指向:任务优先级高于pip,mutex机制已经不起作用,会发生                 优先级反转*/
            } 
            else {/*内定的最高优先级pip务必大于所有能访问互斥资源的进程优先级*/
                OS_EXIT_CRITICAL();
                OS_Sched();                               /*  调度指向优先级最高的任务  */
                return (OS_ERR_NONE);    /*指向:调用成功        }
        }
        pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;      
                    /* 如果没有任务等待信号量,则将低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用  */                               
        pevent->OSEventPtr  = (void *)0;                /*设置互斥信号量没有被任务占用*/
        OS_EXIT_CRITICAL();                  /*开中断*/
        return (OS_ERR_NONE);           /*指向:调用成功       */  
    }

    删除互斥信号量 OSMutexDel ()

    • 描述:该函数删除一个互斥信号量和准备所有的任务在它之前。
      参数: pervent: 是一个指向所需的互斥体相关的事件控制块。

      opt:
      opt == OS_DEL_NO_PEND 当没有任务等待时才删除互斥项。
      opt == OS_DEL_ALWAYS 即使任务正在等待,也会删除互斥体。

      perr :
                   os_err_none        调用是成功和互斥锁被删除
                   os_err_del_isr     从ISR删除互斥错误
                   os_err_invalid_opt 无效选项指定
                   os_err_task_waiting一个或多个任务等待互斥
                   os_err_event_type  'pevent'不是一个指向互斥信号量
                   os_err_pevent_null  '事件'是一个空指针。
      
    • 操作流程:

      a.首先进行运行条件检查,若不满足,则返回空指针和错误代码(函数执行参数检查,校验事件块类型,中断状态)
      b.查看是否有等待互斥信号量的任务(判断并标记是否有任务因该信号量阻塞),设置对应标志(OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。
      c.按删除条件选择参数opt进行删除
      (1)只有在没有任务等待时才删除互斥锁
      a.初始化mutex
      b.Mutex的ECB指针交还给空闲ECB链表
      (2)总是删除互斥锁
      b.初始化mutex
      a.如果发生了提升优先级,通过OSMutex_RdyAtPro(ptcb,prio)恢复为prio优先级
      c.阻塞的任务让所有因该事件被阻塞的任务全部就绪
      d升级过优先级的OSTCBPrioTbl[]的TCB指针清0,然后设置ECB的域,返回 给ECB空闲链表,调用OSSched()
      (3)无效选项指定(报错break)

    #if OS_MUTEX_DEL_EN
    OS_EVENT  *OSMutexDel (OS_EVENT  *pevent,               //指向事件控制块的指针(创建信号量的句柄)
                           INT8U      opt,                  //删除条件选择参数opt
                           INT8U     *perr)                 //返回应用程序的错误代码的指针
    {
    BOOLEAN    tasks_waiting;                                                              /*标记是否有等待互斥信号量的任务*/
    OS_EVENT  *pevent_return;                                                       /*标记互斥信号量删除标志*/
        INT8U      pip;                                                          /* 优先级继承优先级            */
        INT8U      prio;                                                         /*还没优先级反转前的优先级(原优先级)*/
    OS_TCB    *ptcb;                /*任务控制块*/
    
    
    
    #if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
        OS_CPU_SR  cpu_sr = 0;                                  //得到当前处理器状态字的值,并将其保存在C函数局部变量之中
    #endif
    #if OS_ARG_CHK_EN > 0               /*对μC/OS-III的大部分函数执行参数检查(>0)
        if (perr == (INT8U *)0) {                                   /* 校验'perr'                          */
            return (pevent);
        }
        if (pevent == (OS_EVENT *)0) {                                    /* 校验 'pevent'  */
            *perr = OS_ERR_PEVENT_NULL;         /*错误指向:‘事件’是一个空指针*/
            return (pevent);
        }
    #endif
        if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {           /* 校验事件块类型 */
            *perr = OS_ERR_EVENT_TYPE;               /*'pevent'不是一个指向互斥信号量*/
            return (pevent);
        }
        if (OSIntNesting > 0) {                                         /* 如果在中断 */
            *perr = OS_ERR_DEL_ISR;                                     /* 错误指向:从ISR删除互斥错误            */
            return (pevent);
        }
        OS_ENTER_CRITICAL();            /*进入临界区,关中断(保护临界区)*/                                                
        if (pevent->OSEventGrp != 0) {                         /* 查看是否有等待互斥信号的任务      */
                            /*OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。OSEventGrp,来表示等待任务表中的任务组。*/
    
            tasks_waiting = OS_TRUE;                       /* Yes      标记是有等待互斥信号量的任务*/               
        } else {
            tasks_waiting = OS_FALSE;                      /* No      标记是无等待互斥信号量的任务*/                                
        }
            switch (opt) {                              /*删除的参数条件*/
            case OS_DEL_NO_PEND:                                        /* 只有在没有任务等待时才删除互斥锁 --- */
                 if (tasks_waiting == OS_FALSE) {
    #if OS_EVENT_NAME_EN > 0
                     pevent->OSEventName = (INT8U *)"?";                        /*事件名称*/
    #endif
                     pip                 = (INT8U)(pevent->OSEventCnt >> 8);        /*事件计数器高8位,取继承优先级的数值*/
                     OSTCBPrioTbl[pip]   = (OS_TCB *)0;        /*根据继承优先级查找到任务控制表的首地址,设置OSTCKPrioTbl*/                           pevent->OSEventType = OS_EVENT_TYPE_UNUSED;     /*设置ECB数据结构的事件类型为未设置0*/
                     pevent->OSEventPtr  = OSEventFreeList;            /*指向下一个ECB为空事件控制链表 */
                     pevent->OSEventCnt  = 0;                   /*初始化ECB计数器*/
                     OSEventFreeList     = pevent;              /*把该事件控制块返回给空事件控制链表*/
                     OS_EXIT_CRITICAL();
                     *perr               = OS_ERR_NONE;         /*调用成功,删除互斥信号量*/
                     pevent_return       = (OS_EVENT *)0;               /* 标志互斥信号量删除                  */
                 } else {
                     OS_EXIT_CRITICAL();
                     *perr               = OS_ERR_TASK_WAITING;         /*一个或多个任务等待互斥*/
                     pevent_return       = pevent;              /*删除失败返回互斥信号量句柄*/
                 }
                 break;
                 case OS_DEL_ALWAYS:                                            /* 总是删除互斥锁 ---------------- */
                 pip  = (INT8U)(pevent->OSEventCnt >> 8);                     /* 从互斥信号量中获取优先继承优先的数值*/
                 prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  /* 获取任务的最初优先级,获取取得信号量优先级的任务 */
                 ptcb = (OS_TCB *)pevent->OSEventPtr;           /*获取占用该信号的TCB控制块*/
                 if (ptcb != (OS_TCB *)0) {                     /* 查看是否有任何任务拥有互斥锁,看是否ECB的任务控制块指针是否为空          */
                     if (ptcb->OSTCBPrio == pip) {                       /*如果提升过优先级        */
                         OSMutex_RdyAtPrio(ptcb, prio);                    /*恢复之前任务的优先级   */
                     }
                 }
                 while (pevent->OSEventGrp != 0) {                        /* 遍历互斥信号量的所有等待任务        */
                     (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
                        /*选择pevent中优先级最高的任务离开等待队列,进入就绪队列*/
                 }
    #if OS_EVENT_NAME_EN > 0
                 pevent->OSEventName = (INT8U *)"?";
    #endif
        pip       = (INT8U)(pevent->OSEventCnt >> 8);
                 OSTCBPrioTbl[pip]   = (OS_TCB *)0;                 /*对应优先级 TCB指针清空,清除占用 */
                 pevent->OSEventType = OS_EVENT_TYPE_UNUSED;    /*设置ECB数据结构的事件类型为未设置*/
                 pevent->OSEventPtr  = OSEventFreeList;          /*把该事件控制块返回给空事件控制链表 */
                 pevent->OSEventCnt  = 0;           /*初始化ECB计数器*/
                 OSEventFreeList     = pevent;                      
                 OS_EXIT_CRITICAL();
                 if (tasks_waiting == OS_TRUE) {                        /* 有任务等待调度  */
                     OS_Sched();                                            /* 最高优先级任务调度  */
                 }
                 *perr         = OS_ERR_NONE;                   /*调用成功*/
                 pevent_return = (OS_EVENT *)0;                            /* 设置互斥锁删除标志                  */
                 break;
                 default:
                 OS_EXIT_CRITICAL();                            /*开中断,退出临界区*/
                 *perr         = OS_ERR_INVALID_OPT;                    /*无效选项指定*/
                 pevent_return = pevent;        
                 break;
        }
        return (pevent_return);                     /*返回该事件控制块的指针*/
    }
    展开全文
  • 文章目录STM32之FreeRTOS(五):互斥型信号量用法第1步:创建互斥信号量第2步:获取互斥信号量第3步:释放互斥信号量总结: 介绍一下计数型信号量的常规使用方法 一般使用步骤是: 1、创建互斥型信号量 2、获取...
  • 实验三:线程的互斥 一、实验目的 (1)熟练掌握Windows系统环境...(4)分别两种方法--使用临界区对象和互斥对象完成实验:线程的互斥。 二、实验准备 LPCRITICAL_SECTION hCriticalSection; ----定义指向临界区对象...
  • 互斥信号量使用方法如图1所示。在多数情况下,互斥信号量和二值型信号非常相似,但是从功能上二值型信号量用于同步,而互斥信号量用于资源保护。互斥信号量和二值型信号量还有一个最大的区别,互斥型信号....
  • Linux 信号量使用方法

    2020-01-14 16:02:14
    信号量互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。 不多做解释,要使用信号量同步,需要包含头文件semaphore.h。 主要用到的函数: int sem_init(sem_t *sem,...
  • 文章目录STM32之FreeRTOS(五):递归互斥型信号量用法第1步:创建递归互斥信号量第2步:获取递归互斥信号量第3步:释放递归互斥信号量总结 介绍一下递归型信号量的常规使用方法 注意:递归互斥型信号量与互斥型...
  • 进程间同步(互斥量、信号量

    千次阅读 2019-04-23 21:45:41
    进程间同步可以使用互斥量mutex(互斥锁)、信号量和文件锁。 进程间同步使用信号量: int sem_init(sem_t *sem, int pshared, unsigned int value); 用于进程间同步此时第二个参数不能取0了,取非0值用于进程间...
  • 信号量(semaphore)是操作系统用来解决并发中的互斥和同步问题的一种方法信号量是一个与队列有关的整型变量,你可以把它想象成一个数后面拖着一条排队的队列,如图: 那信号量上面值n代表什么意思呢? n>0:...
  • 信号量主要用作线程间的同步及互斥信号量的获取不能在ISR中调用,会导致中断挂起,系统不能有效的进行线程切换及运行。信号量分为动态创建信号量和静态创建信号量,当创建信号量时系统会初始化IPC以及与semaphone...
  • 本文章为方法2,使用条件变量 + 系统互斥量mutex实现信号量semaphore,代码如下所示: //semaphore.h #ifndef _SEMAPHORE_2_H__ #define _SEMAPHORE_2_H__ #include using namespace std; typedef unsigned i
  • 读者可以通过实验进一步理解进程间同步与互斥、临界区与临界资源的概念与含义,并学会Linux信号量的基本使用方法。 二、实验环境 硬件环境:计算机一台,局域网环境; 软件环境:Linux Ubuntu操作系统,gcc编译器。 ...
  • 信号量互斥

    2018-04-11 20:14:31
    号量(semaphore)信号量是E.W.Dijkstra提出的方法,它使用一个整型变量来累计唤醒次数,供以后使用。一个信号量的取值可以为0,或者为正值。 信号量有两种操作:P(wait):检查其值是否大于0,若大于0,则将其值...
  • 是用于保护临界区的一种常用方法,它的使用方式和自旋锁类似。与自旋锁相同的是只有得到信号量的进程才能执行临界区代码。 与自旋锁不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。 linux中...
  • (2) 掌握信号量使用方法; (3) 掌握PV操作的定义; (4) 掌握PV操作实现互斥的方法。 【实验原理/实验基础知识】 一、 互斥 多进程不能同时访问共享的临界资源的现象称为互斥。 二、 Linux信号量结构 每一个...
  • 互斥量、临界区、信号量和时间的作用与区别 不管是辅助线程还是用户接口线程,在存取共享资源时,都需要保护共享资源,以免引起冲突,造成错误。处理方法类似于Win32 API函数的使用,但MFC为我们提供了几个同步...
  • 信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程和进程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要...
  • 也是操作系统中用于控制进程同步互斥的量,下面我们来看看Java 信号量 Semaphore使用方法在用Semaphore 信号量的时候,感觉对公平调度比较有用,可以控制多线程争夺资源时候,最大可以几个在执行,随手写了代码测试下,...
  • 3、信号量:为控制一个具有有限数量用户资源而设计,只能在进程上下文中使用,适合长时间访问共享资源的情况 4、自旋锁:适合短时间访问共享资源的情况,如果锁被长时间持有,等待线程会消耗...
  • 临界区,内核事件,互斥量,信号量,都能完成线程的同步,在这里把他们各自的函数调用,结构定义,以及适用情况做一个总结。临界区:适用范围:它只能同步一个进程中的线程,不能跨进程同步。一般用它来做单个进程内...
  • 上一篇介绍了进程同步、互斥的基本概念跟方法https://blog.csdn.net/ddazz0621/article/details/112463238;这一篇在上篇的基础上得以延伸,介绍一下相对复杂的进程互斥、同步的案例。 问题描述 系统中有一组生产...
  • 对于某个临界资源的访问,读操作和写操作是要区别对待的。读操作可以多个线程同时进行,写操作必须互斥进行。 读写锁:当已经被加了读锁时,其他的读模式锁请求仍然可以访问,但是写模式锁不能访问;...3、用互斥量
  • 而条件变量通过允许线程阻塞和等待另一个线程发送信号方法弥补了互斥锁的不足,他常和互斥锁一起使用使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦...
  • 书接上一回,在上一篇文章:Linux多线程——使用信号量同步线程中,我们留下了一个如何使用互斥量来进行线程同步的问题,本文将会给出互斥量的详细解说,并用一个互斥量解决上一篇文章中,要使用两个信号量才能解决...
  • 而条件变量通过允许线程阻塞和等待另一个线程发送信号方法弥补了互斥锁的不足,他常和互斥锁一起使用使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。用...
  • 四种进程或线程同步互斥的控制方法1、... 3、信号量:为控制一个具有有限数量用户资源而设计。 4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 一 临界区临界区的使用在线程同步中应该算是比较简单

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 811
精华内容 324
关键字:

互斥信号量使用方法