精华内容
下载资源
问答
  • 信号量的初始值

    万次阅读 多人点赞 2016-05-10 00:15:38
    互斥信号量的初始值一般是0,因为两个进程之间的缓冲区通常是一个。 对于同步信号量,前一个进程的同步信号量的初始值一般是n,后一个进程的同步信号量的初始值一般是0. 怎么判断同步信号量是属于哪个进程的 同步...

    互斥信号量的初始值一般是1,因为两个进程之间的缓冲区通常是一个。

    对于同步信号量,前一个进程的同步信号量的初始值一般是n,后一个进程的同步信号量的初始值一般是0.


    怎么判断同步信号量是属于哪个进程的

    同步信号量的P操作在哪一个进程前面,该信号量就是哪一个进程的。


     某企业生产流水线M共有两位生产者,生产者甲不断地将其工序上加工的半成品放入半成品箱,生产者乙从半成品箱取出继续加工。假设半成品箱可存放n件半成品,采用PV操作实现生产者甲和生产者乙的同步可以设置三个信号量S、Sl和S2,其同步模型如下图所示。


      信号量S是一个互斥信号量,初值为( );Sl、S2的初值分别为( )。
    A.0       B.1       C.n       D.任意正整数
    A.n、0      B.0、n      C.1、n      D.n、1


     
    试题答案:B,A
    试题来源:2011年下半年软件设计师考试试题

    解析:甲乙之间的那个半成品箱就只有一个,所以互斥信号量的初始值是1。



    企业的生产流水线上有2名工人P1和P2,1名检验员P3。P1将初步加工的半成品放入半成品箱B1; P2从半成品箱B1取出继续加工,加工好的产品放入成品箱B2;P3从成品箱B2去除产品校验。假设B1可存放n件半成品,B2可存放m件产品,并设置6 个信号量S1、S2、S3、S4、S5和S6,且S3和S6的初值都为0。采用PV操作实现P1、P2和P3的同步模型如下图所示,则信号量S1和 S5(23);S2、S4的初值分别为(24)。
         

          (23)A.分别为同步信号量和互斥信号量,初值分别为0和1
          B.都是同步信号量,其初值分别为0和0
          C.都是互斥信号量,其初值分别为1和1
          D.都是互斥信号量,其初值分别为0和1
          (24)A.n、0    B. m、0    C.m、n    D.n、m



    展开全文
  • 本章节讲解 FreeRTOS 任务间的同步和资源共享机制...FreeRTOS 分别提供了二信号量和计数信号量,其中二信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都...

    以下转载自安富莱电子: http://forum.armfly.com/forum.php

    本章节讲解 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时,会一直等待信号量,直到有信号量来才执行后面的语句。

     

    展开全文
  • 内核信号量的构成 信号量的API 初始化 PV操作 获取信号量(P) 释放内核信号量(V) 补充 内核信号量的使用例程 场景1 场景2 读-写信号量 概念 Linux内核的信号量在概念和原理上和用户态的System V的...

    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

    展开全文
  • 互斥信号量

    2020-10-26 21:01:00
    互斥信号量用于共享资源需要互斥访问的场合,可以理解为初始值为 TRUE 的带优先级天花板和优先级继承机制(意在解决优先级反转问题)的二进制信号量,只有拥有互斥信号量的线程才有权释放互斥信号量。 ...

    目录

    1、Creat

    2、Delete

    3、Wait

    4、Post

    5、Statu


    互斥信号量

    在介绍二进制信号量时,曾讨论到如果二进制信号量创建时设置参数 bInitValue 为TRUE,则可以用于互斥访问共享资源。实际上,SylixOS 的二进制信号量实现的互斥性是将一个变量初始化标记为 1,等待信号量(Wait)时将该变量减 1(此时等于 0),如果另一个线程再次等待该信号量将阻塞,直到该信号量被释放(变量加 1),这样就实现了共享资源的互斥访问。

    如果系统中只有两个线程,上面的过程是没有问题的。但是一旦有多个线程介入,上面过程将出现以下问题:

    一个高优先级的线程可能也要访问同一个共享资源(这是完全有可能的),此时只能阻塞等待,但是可能会有另一个中等优先级的线程将占有信号量的线程抢占。这个过程导致了高优先级线程很长时间得不到运行(这是 SylixOS 不允许出现的情况)。

    以上过程出现的问题就是经典的优先级反转问题。

    互斥信号量用于共享资源需要互斥访问的场合,可以理解为初始值为 TRUE 的带优先级天花板和优先级继承机制(意在解决优先级反转问题)的二进制信号量,只有拥有互斥信号量的线程才有权释放互斥信号量。

     

    1、Creat

    一个 SylixOS 互斥信号量必须要调用 Lw_SemaphoreM_Create 函数创建之后才能使用,如果创建成功,该函数将返回一个互斥信号量的句柄。

    #include <SylixOS.h>
    LW_HANDLE Lw_SemaphoreM_Create(CPCHAR pcName,
         UINT8 ucCeilingPriority,
         ULONG ulOption,
         LW_OBJECT_ID *pulId);

    函数 Lw_SemaphoreM_Create 原型分析:

    • 此函数成功返回互斥信号量的句柄,失败返回 NULL 并设置错误号;
    • 参数 pcName 是互斥信号量的名字;
    • 参数 ucCeilingPriority 在使用优先级天花板算法时有效,此参数为天花板优先级;
    • 参数 ulOption 是互斥信号量的创建选项;
    • 输出参数 pulId 返回互斥信号量的句柄(同返回值),可以为 NULL

    创建选项包含了二进制信号的创建选项,此外还可以使用,如下表所示的互斥信号量特有的创建选项。

    需要注意,LW_OPTION_INHERIT_PRIORITY 和 LW_OPTION_PRIORITY_CEILING只能二选一,同样 LW_OPTION_NORMAL LW_OPTION_ERRORCHECK 及LW_OPTION_RECURSIVE 只能三选一。

     

    2、Delete

    一个不再使用的互斥信号量,可以调用以下函数将其删除。删除后的信号量系统自动回收其占用的系统资源(试图使用被删除的互斥信号量将出现未知的错误)

    #include <SylixOS.h>
    ULONG Lw_SemaphoreM_Delete(LW_HANDLE *pulId);
    函数 Lw_SemaphoreM_Delete 原型分析:
    • 此函数返回错误号;
    • 参数 pulId 是互斥信号量的句柄。
       

    3、Wait

    线程如果需要等待一个互斥信号量,可以调用 Lw_SemaphoreM_Wait 函数。

    #include <SylixOS.h>
    ULONG Lw_SemaphoreM_Wait(LW_HANDLE ulId, ULONG ulTimeout);

    函数 Lw_SemaphoreM_Wait 原型分析:

    • 此函数成功返回 0,失败返回错误号;
    • 参数 ulId 是互斥信号量的句柄;
    • 参数 ulTimeout 是等待的超时时间,单位为时钟嘀嗒 Tick

    4、Post

    释放一个互斥信号量使用 Lw_SemaphoreM_Post 函数。
     
     
    #include <SylixOS.h>
    ULONG Lw_SemaphoreM_Post(LW_HANDLE ulId);

    函数 Lw_SemaphoreM_Post 原型分析:

    • 此函数成功返回 0,失败返回错误号;
    • 参数 ulId 是互斥信号量的句柄。

    需要注意的是,只有互斥信号量的拥有者才能释放该互斥信号量。

    5、Statu

    下面函数可以获得互斥信号量的状态信息。
     
    #include <SylixOS.h>
    ULONG Lw_SemaphoreM_Status(LW_HANDLE ulId,
         BOOL *pbValue,
         ULONG *pulOption,
         ULONG *pulThreadBlockNum);
    ULONG Lw_SemaphoreM_StatusEx(LW_HANDLE ulId,
         BOOL *pbValue,
         ULONG *pulOption,
         ULONG *pulThreadBlockNum,
         LW_HANDLE *pulOwnerId);
    以上两个函数原型分析:
    • 函数成功返回 0,失败返回错误号;
    • 参数 ulId 是互斥信号量的句柄;
    • 输出参数 pbValue 用于接收互斥信号量当前的状态;
    • 输出参数 pulOption 用于接收互斥信号量的创建选项;
    • 输出参数 pulThreadBlockNum 用于接收当前阻塞在该互斥信号量的线程数。
    • 输出参数 pulOwnerId 用于接收当前拥有该互斥信号量的线程的句柄。
     
    获得一个互斥信号量的名字,可以调用以下函数:
    #include <SylixOS.h>
    ULONG Lw_SemaphoreM_GetName(LW_HANDLE ulId, PCHAR pcName);
    函数 Lw_SemaphoreM_GetName 原型分析:
    • 此函数成功返回 0,失败返回错误号;
    • 参数 ulId 是互斥信号量的句柄;
    • 输出参数 pcName 是互斥信号量的名字,pcName 应该指向一个大小为LW_CFG_OBJECT_NAME_SIZE 的字符数组。
     
     
     
    展开全文
  • uCOS中特殊信号量——互斥信号量 本文作为一个学习uCOS经验分享,希望能给初学小白们一个参考。以例程和运行效果来说明,对一些概念性东西这里不做过多解释,网上相关文章多如牛毛。本文中实验需要一些...
  • 信号量的概念 信号量广泛用于进程或线程间的同步和互斥, 信号量本质上是一个非负的整数计数器, 它被用来控制对公共资源的访问 编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限, 当信号量...
  • 信号量 记录锁 互斥量之间的区别 ...如果多个进程间共享一个资源,则可以使用这三种技术中的一种来...若使用信号量,则先创建一个包含一个成员的信号量集合,然后将该信号量的值初始化为1. 为了分配资源,以s
  • 自旋锁就是锁住资源 等待 资源没有被其他 线程拿到资源... 信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护共享资源,初始值为1就变成互斥锁。 信号量加锁和解锁 可以在不同线程...
  • Linux-信号量互斥

    2021-01-06 18:01:45
    Linux-信号量互斥 1. 消费者生产者问题 通过创建两个线程,一个代表生产者,另一个代表消费者,申请一段固定大小内存区域表示缓冲区,向缓冲区末尾插入...mutex::初始值为1,该信号量作用为控制一次只有一个线程可以
  • 互斥锁(Mutex):是初始值为1的信号量 自旋锁:与互斥锁类似,但在无法得到资源时,互斥锁内核线程处于睡眠阻塞状态,而自旋锁处于忙等待状态。 一、信号量 Linux内核的信号量在概念和原理上与.
  • 信号量的初始化 sem_init int sem_init(sem_t *sem, int pshared, unsigned int value); 功能: 初始化信号量 参数: sem :要是初始化的信号量  pshared: 信号量共享的范围(0: 线程间使用 非0:进程间使用) ...
  • 互斥: p即-1,v即+1,互斥需要p自己v自己,无论多少个线程只需要一个信号量 同步: 有几个线程就需要几个信号量,p自己v下一个,在初始时候,sem_init为1先执行
  • //只有得到信号量的进程才能执行临界区代码; //当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。 1.定义信号量 struct semaphore sem; 初始化信号量 //初始化信号量,设置信号量sem的为val。 void ...
  • 一、互斥量 (一)互斥量的定义 ...互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。在初始化的时候,互斥量永远都处于开锁的状态,而被线程持有的时候则立刻转为闭锁的状态。互
  • 为使多个进程能互斥地访问某临界资源,只需为该资源设置一互斥信号量mutex,并设其初始值为1,然后将各进程访问该资源临界区cs置于wait(mutex)和signa(mutex)操作之间即可。这样,每个欲访问该临界资源进程在进入...
  • 信号量初始时候会给出一定的值,这个决定了同时可以有几个访问对象。通常来说是1,也就是同时只能有一个访问对象。 信号量为非负整数。在执行down操作时候讲信号量减1,如果信号量本身为0,减一则进行...
  • 信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护共享资源。 一个任务要想...
  • 1.信号量用来做共享资源保护 当获取信号量down时候会进行两...那么如上图,当信号量用作互斥保护共享资源时候,一般来说信号量初始化sem_init(&sem,1),也就是信号量初始为1。 进程1首先获取信号量down(...
  • 来看一看信号量的使用。 概念部分: 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。 编程时可根据操作信号量值的结果判断是否对公共资源具有访问...
  • value制定信号量的初始值。 sem_destroy用来销毁信号量,以释放占用的内核资源。 sem_wait函数以原子操作将信号量的值减1.如果减为0,则sem_wait会被阻塞住,直到非0. sem_trywait是非阻塞版本,不管value是不是0,...
  • 设置互斥信号量mutex,初始值为1 在临界区之前执行P(mutex) 在临界区之后执行V(mutex) /*信号量机制实现互斥*/ semaphore mutex = 1; // 初始化信号量 P1(){ .... P(mutex); // 使用临界资源前需要加锁 临界区...
  • 在《Unix网络编程 卷二进程间通信》中有用System V模拟 POSIX信号量的论述。 LwIP是一个轻型TCP/IP协议栈,它利用操作系统模拟层实现了信号灯。 主要有如下接口函数: /**新建一个信号灯,并初始化灯的为...
  • sem_init:初始化信号量sem_t,初始化的时候可以指定信号量的初始值,以及是否可以在多进程间共享。sem_wait:一直阻塞等待直到信号量&gt;0。sem_timedwait:阻塞等待若干时间直到信号量&gt;0。sem_post:使...
  • 1. Pthread互斥锁以及Pthread信号量的创建等相关函数在pthread.h中有a) pthread_mutex_t数据类型用来声明互斥锁;b) 函数pthread_mutex_init(&amp;mutex,NULL)用来创建互斥锁,其中第一个参数为声明的互斥锁...
  • 有名信号量named semaphore 打开关闭删除 无名信号量unnamed semaphore, 信号量对象要存放到共享内存区(全局...生产者:p(sem_full)表示缓冲区的容量,每当生产一个产品的时候,信号量的计数值就减1; 仓库不满就.
  • 信号量的应用

    2020-03-08 20:23:01
    为使多个进程互斥访问某临界资源,须为该资源设置一个互斥信号量mutex,并设初始值为1,然后将各进程访问资源临界区CS置于wait(mutex)和signal(mutex)之间。 例如用记录型信号量实现两个进程互斥适用一个打印机...
  • 互斥量(mutex)从概念上来说类似于一个二进制信号量,即初始值为1的信号量互斥量被获取之后就不能再被获取,因此对互斥获取和释放操作常常称为加锁和解锁操作。   互斥量只能由获取它线程进行释放,如果...
  • 信号量的主要目的有两个:共享资源访问。 与任务同步。 FreeRTOS中信号量分为如下几种: 1、二信号量 2、计数型信号量 3、互斥信号量 4、递归互斥信号量 2.计数型信号量 创建计数型信号量: 释放计数型信号量:...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 241
精华内容 96
关键字:

互斥信号量的初始值