STM32:rtthread_信号量

发布时间 2023-07-14 22:30:39作者: caesura_k

1 信号量

  信号量是一种用于管理线程间资源关系的内核对象,线程可以获取或释放它从而达到同步或互斥的目的;

  信号量可以运用在多种场合中,形成锁,同步(多个线程可访问同一资源),资源计数等关系,也能方便的用于线程与线程,中断与线程的同步中;

  1.1 semaphore 信号量结构体

//rtdef.h
#ifdef RT_USING_SEMAPHORE
struct rt_semaphore
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */
    rt_uint16_t          value;                         /**< value of semaphore. */
};
typedef struct rt_semaphore *rt_sem_t;
#endif

struct rt_ipc_object
{
    struct rt_object parent;                            /**< inherit from rt_object */
    rt_list_t        suspend_thread;                    /**< threads pended on this resource */
};

struct rt_object
{
    char       name[RT_NAME_MAX];                       /**< name of kernel object */
    rt_uint8_t type;                                    /**< type of kernel object */
    rt_uint8_t flag;                                    /**< flag of kernel object */

#ifdef RT_USING_MODULE
    void      *module_id;                               /**< id of application module */
#endif
    rt_list_t  list;                                    /**< list node of kernel object */
};
typedef struct rt_object *rt_object_t;                  /**< Type for kernel objects. */


//IPC flags and control command definitions
#define RT_IPC_FLAG_FIFO                0x00            /**< FIFOed IPC. 先进先出 */
#define RT_IPC_FLAG_PRIO                0x01            /**< PRIOed IPC. 优先级 */

#define RT_IPC_CMD_UNKNOWN              0x00            /**< unknown IPC command */
#define RT_IPC_CMD_RESET                0x01            /**< reset IPC object */

#define RT_WAITING_FOREVER              -1              /**< Block forever until get resource. */
#define RT_WAITING_NO                   0               /**< Non-block. */

  1.2 rt_sem_create 创建信号量

    这个信号量的创建函数还挺简单的嘞,这个信号量信息不多看来很简单;事后也确实很简单;

    信号量的flag建议采用RT_IPC_FLAG_PRIO,可以保证线程的实时性;RT_IPC_FLAG_FIFO属于非实时调度,实时性不如优先级flag方式;

//ipc.c
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
{
    rt_sem_t sem;

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* allocate object */
    sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name);
    if (sem == RT_NULL)
        return sem;

    /* init ipc object */
    rt_ipc_object_init(&(sem->parent));

    /* set init value */
    sem->value = value;

    /* set parent */
    sem->parent.parent.flag = flag;

    return sem;
}
RTM_EXPORT(rt_sem_create);

   1.3 rt_sem_delete 删除信号量

//ipc.c 
rt_err_t rt_sem_delete(rt_sem_t sem)
{
    RT_DEBUG_NOT_IN_INTERRUPT;

    RT_ASSERT(sem != RT_NULL);

    /* wakeup all suspend threads */
    rt_ipc_list_resume_all(&(sem->parent.suspend_thread));

    /* delete semaphore object */
    rt_object_delete(&(sem->parent.parent));

    return RT_EOK;
}

//在rt_object_delete(rt_object_t object)函数中,主要执行了两个操作,一个是rt_list_remove(&object->list),一个是RT_KERNEL_FREE(object);
//object是指针,作为变量传递赋值的时候,可不等价于传递地址了吗?
//malloc函数在分配地址的时候,会自动先拿出4字节存储分配的内存大小,然后把后面的地址返回;
//所以free内存的时候,不需要传入内存大小,free也知道需要释放的内存大小;

   1.4 rt_sem_take 获取信号量

//ipc.c  如果当前信号量值大于0,那么信号量值减1,然后就返回继续执行当前线程函数;说明当前线程使用了一个信号量资源;
//       如果当前信号量值为0,说明没有信号量资源;那就根据time值判断是否等待;
//       如果不等待则直接返回超时函数;如果等待,则将当前线程悬起到suspend_thread,等到有信号量资源的时候再释放;
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
{
    register rt_base_t temp;
    struct rt_thread *thread;

    RT_ASSERT(sem != RT_NULL);

    RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent)));

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n",
                                rt_thread_self()->name,
                                ((struct rt_object *)sem)->name,
                                sem->value));

    if (sem->value > 0)
    {
        /* semaphore is available */
        sem->value --;

        /* enable interrupt */
        rt_hw_interrupt_enable(temp);
    }
    else
    {
        /* no waiting, return with timeout */
        if (time == 0)
        {
            rt_hw_interrupt_enable(temp);

            return -RT_ETIMEOUT;
        }
        else
        {
            /* current context checking */
            RT_DEBUG_IN_THREAD_CONTEXT;

            /* semaphore is unavailable, push to suspend list */
            /* get current thread */
            thread = rt_thread_self();

            /* reset thread error number */
            thread->error = RT_EOK;

            RT_DEBUG_LOG(RT_DEBUG_IPC, ("sem take: suspend thread - %s\n",
                                        thread->name));

            /* suspend thread */
            rt_ipc_list_suspend(&(sem->parent.suspend_thread),
                                thread,
                                sem->parent.parent.flag);

            /* has waiting time, start thread timer */
            if (time > 0)
            {
                RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n",
                                            thread->name));

                /* reset the timeout of thread timer and start it */
                rt_timer_control(&(thread->thread_timer),
                                 RT_TIMER_CTRL_SET_TIME,
                                 &time);
                rt_timer_start(&(thread->thread_timer));
            }

            /* enable interrupt */
            rt_hw_interrupt_enable(temp);

            /* do schedule */
            rt_schedule();

            if (thread->error != RT_EOK)
            {
                return thread->error;
            }
        }
    }

    RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));

    return RT_EOK;
}
RTM_EXPORT(rt_sem_take);

   1.5 rt_sem_release 释放信号量

//ipc.c     如果suspend_thread中有等待线程,那么将其恢复,然后rt_schedule,
//          让另外一个等待信号量的suspend_thread有了执行权就相当于这里的信号量加1了;
//          if suspend_thread中没有等待线程,那么当前线程释放了信号量,就把信号量值加1;
rt_err_t rt_sem_release(rt_sem_t sem)
{
    register rt_base_t temp;
    register rt_bool_t need_schedule;

    RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent)));

    need_schedule = RT_FALSE;

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n",
                                rt_thread_self()->name,
                                ((struct rt_object *)sem)->name,
                                sem->value));

    if (!rt_list_isempty(&sem->parent.suspend_thread))
    {
        /* resume the suspended thread */
        rt_ipc_list_resume(&(sem->parent.suspend_thread));
        need_schedule = RT_TRUE;
    }
    else
        sem->value ++; /* increase value */

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    /* resume a thread, re-schedule */
    if (need_schedule == RT_TRUE)
        rt_schedule();

    return RT_EOK;
}
RTM_EXPORT(rt_sem_release);

  1.6 gitee

    rtthread_f1demo: 将rtthread nano3.0.3版本移植到stm32f1上; (gitee.com) 

2 小结

  写demo的时候应该单独开个文件来写的,这样就不用每次都修改main函数了,调用函数例程就叫xx_sample把;