Rtthread信号量实现
信号量主要用作线程间的同步及互斥,信号量的获取不能在ISR中调用,会导致中断挂起,
系统不能有效的进行线程切换及运行。信号量分为动态创建信号量和静态创建信号量,当
创建信号量时系统会初始化IPC以及与semaphone相关的部分。在创建信号量指定的参数
过程中,flag参数定义FIFO时,信号量采取先入先出的方式,定义为IPRO时,信号量采取
优先级的方式,优先级高的线程将先获得等待的信号量。
代码实现:
/**
* IPC flags and control command definitions
*/
#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref 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. */
/**
* Base structure of IPC object
*/
struct rt_ipc_object
{
struct rt_object parent; /**< inherit from rt_object */
rt_list_t suspend_thread; /**< threads pended on this resource */
};
信号量是依赖IPC来实现的,先看看IPC部分源码:
//ipc结构中维护了一个suspend thread链表,用来将阻塞的线程挂载到上面
rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc)
{
/* initialize ipc object */
rt_list_init(&(ipc->suspend_thread));
return RT_EOK;
}
//根据flag的值(RT_IPC_FLAG_FIFO/RT_IPC_FLAG_PRIO)决定按优先级排序还是按照先进先出的顺序将当前线程插入链表
rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t *list,
struct rt_thread *thread,
rt_uint8_t flag)
{
/* suspend thread */
rt_thread_suspend(thread);//先将线程suspend,移除出就绪表,并将status切到suspend状态
switch (flag) //判断flag
{
case RT_IPC_FLAG_FIFO: //先进先出
rt_list_insert_before(list, &(thread->tlist));
break;
case RT_IPC_FLAG_PRIO://优先级
{
struct rt_list_node *n;
struct rt_thread *sthread;
/* find a suitable position */
for (n = list->next; n != list; n = n->next)//遍历链表
{
sthread = rt_list_entry(n, struct rt_thread, tlist);
/* find out */
if (thread->current_priority < sthread->current_priority)//比较优先级
{
/* insert this thread before the sthread */
rt_list_insert_before(&(sthread->tlist), &(thread->tlist));//插入到比它优先级低的线程前面(值越小越高)
break;
}
}
if (n == list)//链表是空的情况
rt_list_insert_before(list, &(thread->tlist));
}
break;
default:
break;
}
return RT_EOK;
}
//唤醒线程,将线程插入到就绪表
rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list)
{
struct rt_thread *thread;
/* get thread entry */
thread = rt_list_entry(list->next, struct rt_thread, tlist);
RT_DEBUG_LOG(RT_DEBUG_IPC, ("resume thread:%s\n", thread->name));
/* resume it */
rt_thread_resume(thread);//resume只将线程插入到就绪表中,跟suspend动作正好相反
return RT_EOK;
}
//唤醒所有suspend阻塞的线程
rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list)
{
struct rt_thread *thread;
register rt_ubase_t temp;
/* wakeup all suspended threads */
while (!rt_list_isempty(list))
{
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* get next suspended thread */
thread = rt_list_entry(list->next, struct rt_thread, tlist);
/* set error code to RT_ERROR */
thread->error = -RT_ERROR;
/*
* resume thread
* In rt_thread_resume function, it will remove current thread from
* suspended list
*/
rt_thread_resume(thread);
/* enable interrupt */
rt_hw_interrupt_enable(temp);
}
return RT_EOK;
}
下面是信号量的实现
#ifdef RT_USING_SEMAPHORE
/**
* Semaphore structure
*/
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of semaphore. */
rt_uint16_t reserved; /**< reserved field */
};
typedef struct rt_semaphore *rt_sem_t;
#endif
//初始化部分:跟其他模块是一样的实现,包括静态/动态的创建方法和对应的分离/删除
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
{
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(value < 0x10000U);
/* initialize object */
rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name);
/* initialize ipc object */
rt_ipc_object_init(&(sem->parent));
/* set initial value */
sem->value = (rt_uint16_t)value;
/* set parent */
sem->parent.parent.flag = flag;
return RT_EOK;
}
rt_err_t rt_sem_detach(rt_sem_t sem)
{
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent));
/* wakeup all suspended threads */
rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
/* detach semaphore object */
rt_object_detach(&(sem->parent.parent));
return RT_EOK;
}
#ifdef RT_USING_HEAP
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;
RT_ASSERT(value < 0x10000U);
/* allocate object */
sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name);
if (sem == RT_NULL)
return sem;
/* initialize ipc object */
rt_ipc_object_init(&(sem->parent));
/* set initial value */
sem->value = value;
/* set parent */
sem->parent.parent.flag = flag;
return sem;
}
rt_err_t rt_sem_delete(rt_sem_t sem)
{
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE);
/* wakeup all suspended threads */
rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
/* delete semaphore object */
rt_object_delete(&(sem->parent.parent));
return RT_EOK;
}
#endif
//获取信号量
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
{
register rt_base_t temp;
struct rt_thread *thread;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
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// <= 0
{
/* no waiting, return with timeout */
if (time == 0) //等待时间为0,不等待,直接返回超时error
{
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;
}
else //等待时间不为0
{
/* 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掉
rt_ipc_list_suspend(&(sem->parent.suspend_thread),
thread,
sem->parent.parent.flag);
/* has waiting time, start thread timer */
if (time > 0)//等待时间大于0
{
RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n",
thread->name));
//开启定时器,设置超时时间
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();//启动线程调度,因为当前线程已经被suspend了。
if (thread->error != RT_EOK)
{
return thread->error;
}
}
}
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));
return RT_EOK;
}
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
return rt_sem_take(sem, 0);//timeout 为0
}
rt_err_t rt_sem_release(rt_sem_t sem)
{
register rt_base_t temp;
register rt_bool_t need_schedule;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
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));
//当前信号量阻塞的线程链表不为空,其他线程会回收信号量,所以这里不用改变sem->value的值
if (!rt_list_isempty(&sem->parent.suspend_thread))//他的parent是rt_ipc_object
{
/* resume the suspended thread */
rt_ipc_list_resume(&(sem->parent.suspend_thread));//取出第一个线程,并恢复它
need_schedule = RT_TRUE;
}
else //没有等待同步的线程,这里需要自己恢复sem->value
{
if(sem->value < RT_SEM_VALUE_MAX)//回收信号量
{
sem->value ++; /* increase value */
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* resume a thread, re-schedule */
if (need_schedule == RT_TRUE)
rt_schedule();
return RT_EOK;
}
rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg)
{
rt_ubase_t level;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
if (cmd == RT_IPC_CMD_RESET)
{
rt_ubase_t value;
/* get value */
value = (rt_ubase_t)arg;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* resume all waiting thread */
rt_ipc_list_resume_all(&sem->parent.suspend_thread);
/* set new value */
sem->value = (rt_uint16_t)value;
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_schedule();
return RT_EOK;
}
return -RT_ERROR;
}
下面再看看mutex的实现
#ifdef RT_USING_MUTEX
/**
* Mutual exclusion (mutex) structure
*/
struct rt_mutex
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of mutex */
rt_uint8_t original_priority; /**< priority of last thread hold the mutex */
rt_uint8_t hold; /**< numbers of thread hold the mutex */
struct rt_thread *owner; /**< current owner of mutex */
};
typedef struct rt_mutex *rt_mutex_t;
#endif
#ifdef RT_USING_MUTEX
//mutex初始化函数
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
{
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
/* initialize object */
rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name);
/* initialize ipc object */
rt_ipc_object_init(&(mutex->parent));
mutex->value = 1;
mutex->owner = RT_NULL;
mutex->original_priority = 0xFF;
mutex->hold = 0;
/* set flag */
mutex->parent.parent.flag = flag;
return RT_EOK;
}
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
{
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent));
/* wakeup all suspended threads */
rt_ipc_list_resume_all(&(mutex->parent.suspend_thread));
/* detach semaphore object */
rt_object_detach(&(mutex->parent.parent));
return RT_EOK;
}
#ifdef RT_USING_HEAP
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
{
struct rt_mutex *mutex;
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate object */
mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name);
if (mutex == RT_NULL)
return mutex;
/* initialize ipc object */
rt_ipc_object_init(&(mutex->parent));
mutex->value = 1;
mutex->owner = RT_NULL;
mutex->original_priority = 0xFF;
mutex->hold = 0;
/* set flag */
mutex->parent.parent.flag = flag;
return mutex;
}
/**
* This function will delete a mutex object and release the memory
*
* @param mutex the mutex object
*
* @return the error code
*
* @see rt_mutex_detach
*/
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
{
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE);
/* wakeup all suspended threads */
rt_ipc_list_resume_all(&(mutex->parent.suspend_thread));
/* delete mutex object */
rt_object_delete(&(mutex->parent.parent));
return RT_EOK;
}
#endif
/**
* This function will take a mutex, if the mutex is unavailable, the
* thread shall wait for a specified time.
*
* @param mutex the mutex object
* @param time the waiting time
*
* @return the error code
*/
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
{
register rt_base_t temp;
struct rt_thread *thread;
/* this function must not be used in interrupt even if time = 0 */
RT_DEBUG_IN_THREAD_CONTEXT;
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
/* get current thread */
thread = rt_thread_self();//获取当前线程
/* disable interrupt */
temp = rt_hw_interrupt_disable();
RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent)));
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_take: current thread %s, mutex value: %d, hold: %d\n",
thread->name, mutex->value, mutex->hold));
/* reset thread error */
thread->error = RT_EOK;
if (mutex->owner == thread)//当前mutex的拥有者就是当前线程(重复加锁)
{
if(mutex->hold < RT_MUTEX_HOLD_MAX)
{
/* it's the same thread */
mutex->hold ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
else //其他线程获取mutex
{
/* The value of mutex is 1 in initial status. Therefore, if the
* value is great than 0, it indicates the mutex is avaible.
*/
if (mutex->value > 0) //锁未被使用
{
/* mutex is available */
mutex->value --;
/* set mutex owner and original priority */
mutex->owner = thread;//更新mutex拥有者owner线程
mutex->original_priority = thread->current_priority;//保存线程优先级
if(mutex->hold < RT_MUTEX_HOLD_MAX)
{
mutex->hold ++;//hold标志++
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
else //锁已经被其他线程占有
{
/* no waiting, return with timeout */
if (time == 0) //不等待
{
/* set error as timeout */
thread->error = -RT_ETIMEOUT;
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;//返回超时错误
}
else //等待
{
/* mutex is unavailable, push to suspend list */
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n",
thread->name));
/* change the owner thread priority of mutex */
if (thread->current_priority < mutex->owner->current_priority)//需要获取锁的线程优先级比当前占有锁的优先级高
{
//这里将当前持锁的线程优先级提高到与等待持锁线程一样的优先级
rt_thread_control(mutex->owner,
RT_THREAD_CTRL_CHANGE_PRIORITY,
&thread->current_priority);//修改当前占有锁的线程的优先级为当前线程
}
/* suspend current thread */
rt_ipc_list_suspend(&(mutex->parent.suspend_thread),
thread,
mutex->parent.parent.flag);//因为锁被其他线程占用,所以先挂起当前线程等待
/* has waiting time, start thread timer */
if (time > 0)//等待时间大于0
{
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_take: start the timer of thread:%s\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 error */
return thread->error;
}
else
{
/* the mutex is taken successfully. */
/* disable interrupt */
temp = rt_hw_interrupt_disable();
}
}
}
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent)));
return RT_EOK;
}
/**
* This function will release a mutex, if there are threads suspended on mutex,
* it will be waked up.
*
* @param mutex the mutex object
*
* @return the error code
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex)
{
register rt_base_t temp;
struct rt_thread *thread;
rt_bool_t need_schedule;
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
need_schedule = RT_FALSE;
/* only thread could release mutex because we need test the ownership */
RT_DEBUG_IN_THREAD_CONTEXT;
/* get current thread */
thread = rt_thread_self();//获取当前线程
/* disable interrupt */
temp = rt_hw_interrupt_disable();
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_release:current thread %s, mutex value: %d, hold: %d\n",
thread->name, mutex->value, mutex->hold));
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent)));
/* mutex only can be released by owner */
if (thread != mutex->owner)//谁加锁谁释放
{
thread->error = -RT_ERROR;
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return -RT_ERROR;
}
/* decrease hold */
mutex->hold --;
/* if no hold */
if (mutex->hold == 0)//完全释放
{
/* change the owner thread to original priority */
//因为加锁的时候有提高当前线程的优先级,所以释放的时候要调整回来
if (mutex->original_priority != mutex->owner->current_priority)
{
rt_thread_control(mutex->owner,
RT_THREAD_CTRL_CHANGE_PRIORITY,
&(mutex->original_priority));
}
/* wakeup suspended thread */
if (!rt_list_isempty(&mutex->parent.suspend_thread))//唤醒在等待锁的线程
{
/* get suspended thread */
thread = rt_list_entry(mutex->parent.suspend_thread.next,
struct rt_thread,
tlist);
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n",
thread->name));
/* set new owner and priority */
mutex->owner = thread;
mutex->original_priority = thread->current_priority;
if(mutex->hold < RT_MUTEX_HOLD_MAX)//给唤醒的线程加上hold标志
{
mutex->hold ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
/* resume thread */
rt_ipc_list_resume(&(mutex->parent.suspend_thread));//resume 线程
need_schedule = RT_TRUE;//需要调度
}
else //没有线程在等待锁
{
if(mutex->value < RT_MUTEX_VALUE_MAX)
{
/* increase value */
mutex->value ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
/* clear owner */
mutex->owner = RT_NULL;
mutex->original_priority = 0xff;
}
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* perform a schedule */
if (need_schedule == RT_TRUE)
rt_schedule();//调度
return RT_EOK;
}
/**
* This function can get or set some extra attributions of a mutex object.
*
* @param mutex the mutex object
* @param cmd the execution command
* @param arg the execution argument
*
* @return the error code
*/
rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg)
{
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
return -RT_ERROR;
}
#endif /* end of RT_USING_MUTEX */