野火RTT 19章信号量
2018年12月29日
10:46
相当于裸机中的标志
信号量:实现线程间的通信,实现线程间的同步货临界资源的互斥访问。
多线程系统中,线程间需要同步或互斥实现临界资源保护。
二值信号量:用于线程间、线程与中断间的同步。其他线程获取,信号量为0,释放信号量为1。二值信号量最大计数值为1。也就是信号量的个数最大为1。是计数型信号量的特殊情况(计数最大值为1)。
例子:比如某线程可以使用二值信号量等待中断里标记发生,当没有这个标记发生时将线程挂到阻塞态,当发生对应中断了,在中断中释放这个信号量。就会把阻塞态进入就绪态。
释放信号量函数会将调用恢复阻塞态线程的函数,同时会调用调度函数执行线程调度。
二值信号量:若还有因读信号量而被阻态的线程,那只能等被阻态的所有线程都就绪后,再次释放才能真正释放。比如,我二值信号量我设定初始值为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));
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;
}
二值信号量特性:(1)前提是要先创建信号量。(2)获取和释放要成对出现。(3)读取未被释放好的二值信号量的所有线程都会被移除就绪表而被挂起,同时这些线程又会按照FIFO先进先出或者优先级的顺序被挂到这个二值信号量控制块下的成员suspend链表下。调用释放函数时从suapend的next恢复掉一个线程——而不是一下子恢复掉所有线程,一个一个来!(4)二值信号量的意义:这样子就完成了第一个线程中获取信号量函数到释放信号量函数之间的代码运行完了,才能再去运行下个获取同样这个信号量的线程中获取和释放的这部分代码。以此类推。假如把获取信号量和释放信号量这个区间当成代码区间。理想的执行顺序:先运行存在这种区间的最高优先级线程的区间1这段代码,再运行次高优先级线程的区间2,在运行第3高优先级线程的区间3,……,直到最后用到这个二值信号量的线程中的区间n。实际上的运行顺序还得根据延时时间动态判断。