zoukankan      html  css  js  c++  java
  • 【STM32F4】【银杏科技ARM+FPGA】iCore3移植RT-Thread--内核之信号量

    一、信号量的引入

      以车辆进出停车场为例。例如停车场有五个车位,起初五个车位都为空,此时来了七辆车,门卫允许其中五辆进入,然后放下车拦,剩下的车则必须在入口处等待,此后来的车也只能在入口处等待。这时,有一辆车离开停车场,门卫得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。

    在这个停车场系统中,车位是公共资源,每辆车就是一个线程,门卫起的就是信号量的作用。

    二、信号量的描述

      信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。信号量是用来解决线程同步和互斥的通用工具,但信号量比较简单、小巧灵活,不能解决优先级翻转问题。

      在实际操作系统中,信号量常应用与两种场景:

    1. 从外设中断中释放信号量,在任务中获取信号量。这种场景在串口接收中最为常见。
    2. 任务与任务之间的同步,一个任务通过信号量通知另一个任务。例如生产者与消费者场景。

    三、 信号量控制块

      信号量控制块与线程控制相似,每个信号量都有自己的信号量控制块,信号量控制块中包含了信号量的所有信息:信号量的状态信息、使用情况等。

    struct rt_semaphore
    {
        struct rt_ipc_object parent;                        /**< 继承自 ipc_object 类*/
    
        rt_uint16_t          value;                         /**< 信号量的值,最大为65535 */
    };

    四、 信号量接口函数

    1、创建动态信号量函数:当调用此函数时,系统完成对该控制块的初始化工作。信号量标志参数flag决定了多个线程等待的排队方式,当设定为 RT_IPC_FLAG_FIFO(先进先出)时,等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当设定为RT_IPC_FLAG_PRIO(优先级等待)时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。

    rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);

    (1)入口参数:

      name:信号量名称。

      value:信号量初始值。

      flag:信号量标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO。

    (2)返回值

      RT_NULL:创建失败。

      信号量的控制块指针:创建成功。

    2、删除动态信号量函数:此函数将使系统删除动态信号量,如果此时有线程正在等待该信号量,那么删除操作会先唤醒这些线程(等待线程的返回值是- RT_ERROR,表明异常唤醒),然后再释放信号量的内存资源。

    rt_err_t rt_sem_delete(rt_sem_t sem);

    (1)入口参数:

      sem:创建的信号量对象。

    (2)返回值:

      RT_EOK:删除成功。

    3、创建静态信号量函数:创建静态信号量也就是初始化信号量。对于静态信号量(它的内存空间在编译时期就被编译器分配出来,放在读写数据段或未初始化数据段上)就不再需要使用 rt_sem_create 接口来创建它,只需在使用前对它进行初始化即可。

    rt_err_t rt_sem_init(rt_sem_t    sem,

                                 const char *name,

                                 rt_uint32_t value,

                                 rt_uint8_t  flag);

    (1)入口参数:

     sem:信号量对象的句柄。

     name:信号量名称。

     value:信号量初始值。

     flag:信号量标志,它可以取如下数值:RT_IPC_FLAG_FIFO(先进先出) 或 RT_IPC_FLAG_PRIO(优先级等待)。

    (2)返回值:

     RT_EOK:初始化成功。

    4、删除静态信号量函数:即脱离信号量,就是让信号量对象从内核对象管理器中脱离。此时内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离,原来挂起在信号量上的等待线程将获得-RT_ERROR 的返回值。

    rt_err_t rt_sem_detach(rt_sem_t sem);

    (1)入口参数:

     sem:信号量对象的句柄。

    (2)返回值:

     RT_EOK:脱离成功。

    5、获取信号量函数:线程通过此函数获取信号量,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1。如果信号量的值等于零,此时当前信号量资源不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是-RT_ETIMEOUT。

    rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);

    (1)入口参数

     sem:信号量对象的句柄。

     time:指定的等待时间,单位是操作系统时钟节拍(OS Tick)。

    (2)返回值

     RT_EOK:成功获得信号量。

     RT_ETIMEOUT:超时依然未获得信号量。

     RT_ERROR:其他错误。

    6、无等待获取信号量:当用户不想在申请的信号量上挂起线程进行等待时,可以使用无等待方式获取信号量,它的作用是和rt_sem_take(sem, 0) 一样的,即当线程申请的信号量资源实例不可用的时候,它不会等待在该信号量上,而是直接返回RT_ETIMEOUT。

    rt_err_t rt_sem_trytake(rt_sem_t sem);

    (1)入口参数:

      sem:信号量对象的句柄。

    (2)返回值:

     RT_EOK:成功获得信号量。

     RT_ETIMEOUT:获取失败。

    7、释放信号量函数:释放信号量可以唤醒挂起在该信号量上的线程。当信号量的值等于零时,并且有线程等待这个信号量时,释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量,同时将把信号量的值加 1。

    rt_err_t rt_sem_release(rt_sem_t sem);

    (1)入口参数:

     sem:信号量对象的句柄。

    (2)返回值:

     RT_EOK:成功释放信号量。

    五、 信号量实例

    下面通过RT-Thread的信号量来完成一个简单的应用:ARM_LED交替闪烁。

      信号量是进程间通信的媒介,在此定义两个线程thread1和thread2。Thread1首先获取一个信号量,然后点亮LED0,延迟500ms过后熄灭LED0,随即释放一个信号量。Thread2以RT_WAITING_FOREVER的方式一直保持获取信号量,当Thread1释放信号量之后,Thread2获取到一个信号量开始运行,点亮LED1,延迟500ms过后熄灭,随即释放一个信号量。

    #include <rtthread.h>
    #include <rtdevice.h>
    #include <board.h>
    
    #define LED0_PIN    GET_PIN(I, 5)
    #define LED1_PIN    GET_PIN(I, 6)
    #define LED2_PIN    GET_PIN(I, 7)
    
    static rt_sem_t dynamic_sem = RT_NULL;
    
    static rt_thread_t tid1,tid2;
    
    void semaphore_led_init(void)
    {
        rt_pin_mode(LED0_PIN,PIN_MODE_OUTPUT);
        rt_pin_mode(LED1_PIN,PIN_MODE_OUTPUT);
    }
    
    void rt_thread_entry1(void *parameter)
    {
        rt_err_t result = RT_NULL;
        while(1)
        {    
            result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER); //获取信号量,模式为一直等待
            if(result != RT_EOK)
            {
                return;
            }
            rt_kprintf("thread1 take 1 sem
    ");
            rt_pin_write(LED0_PIN,0);
            rt_kprintf("led0-on in thread1 
    ");
            rt_thread_delay(RT_TICK_PER_SECOND/2);       //延时500ms;
            rt_pin_write(LED0_PIN,1);
            rt_kprintf("led0-off in thread1 
    ");
            rt_kprintf("thread1 release 1 sem
    ");
            rt_sem_release(dynamic_sem);                //释放信号量
        }
    }
     
    void rt_thread_entry2(void *parameter)
    {
        rt_err_t result = RT_NULL;
        while(1)
        {
            result = rt_sem_take(dynamic_sem,RT_WAITING_FOREVER);
            if(result != RT_EOK)
            {
                return;
            }
            rt_kprintf("thread2 take 1 sem
    ");
            rt_pin_write(LED1_PIN,0);
            rt_kprintf("led1-on in thread2 
    ");
            rt_thread_delay(RT_TICK_PER_SECOND/2);
            rt_pin_write(LED1_PIN,1);
            rt_kprintf("led1-off in thread2 
    ");
            rt_kprintf("thread2 release 1 sem
    ");
            rt_sem_release(dynamic_sem);
        }
    }
    
    int semaphore_led_sample_init(void *parameter)
    {
        semaphore_led_init();
        
        dynamic_sem = rt_sem_create("dsem",               //信号量名字
                                    1,                    //信号量初始值
                                    RT_IPC_FLAG_FIFO);    //先进先出
        if(dynamic_sem == RT_NULL)
        {
            rt_kprintf("Failed to create dynamic semaphore! 
    ");
            return 1;
        }
        
        tid1 = rt_thread_create("thread1",rt_thread_entry1,RT_NULL,512,5,10);
        if(tid1 == RT_NULL)
        {
            rt_kprintf("Failed to create thread1
    ");
            return 1;
        }
        rt_thread_startup(tid1);            //线程1创建成功,则启动线程
        
        tid2 = rt_thread_create("thread2",rt_thread_entry2,RT_NULL,512,6,10);
        if(tid2 == RT_NULL)
        {
            rt_kprintf("Failed to create thread2
    ");
            return 1;
        }
        rt_thread_startup(tid2);           //线程2创建成功,则启动线程
        return 0;
    }
     
    #if defined (RT_SAMPLES_AUTORUN) && defined(RT_USING_COMPONENTS_INIT)
        INIT_APP_EXPORT(semaphore_led_sample_init);
    #endif
     
    MSH_CMD_EXPORT(semaphore_led_sample_init, semaphore led sample);

     六、实验现象

    当线程1得到一个信号量是,led0闪烁一下释放信号量,由线程2得到信号量,led1闪烁一下释放信号量,再由线程1得到信号量,依次往复,ARM_LED红绿循环闪烁。

  • 相关阅读:
    利用正则表达式,完成参数的替换
    使用python读取yaml文件
    python+unittet在linux与windows使用的区别
    python发送requests请求时,使用登录的token值,作为下一个接口的请求头信息
    jmeter线程组之间传参
    requests:json请求中中文乱码处理
    ddt源码修改:HtmlTestRunner报告依据接口名显示用例名字
    使用openpyxl的styles,实现写入值时加背景色
    批量数据请求接口
    locust参数化
  • 原文地址:https://www.cnblogs.com/xiaomagee/p/13041691.html
Copyright © 2011-2022 走看看