uc/os-ii信号量的理解

作者在 2009-07-02 23:46:02 发布以下内容
uc/os-ii信号量的理解
2007-12-07 11:44:36
 标签:uc 信号量 os-ii    [推送到技术圈]

1. 信号量的理解
1uc/os-ii的信号量是由两个部分组成:一部分是16位的无符号整型信号量的计数值(0~65535);另一部分是等待该信号量的任务组成的等待任务表。(另外参考事件控制块ECB
2)信号量可以是2值的变量(称为二值信号量),也可以是计数式的。根据信号量的值,内核跟踪那些等待信号量的任务。
3)建立信号量的工作必须在任务级代码中或者多任务启动之前完成。
4)任务要得到信号量的问题。
想得到信号量的任务,必须执行等待操作(pend)。如果信号量有效(0),则信号量减1,任务得以继续运行。如果信号量无效,则等待信号量的任务就被列入等待信号量的任务表中。多少内核允许定义等待超时,当等待时间超过了设定值,该信号量还是无效,则等待该信号量的任务进入就绪态,准备运行,并返回出错代码(等待超时错误)
5)任务对信号量的释放问题。
任务执行发信号(post)操作来释放信号量。如果没有任务等待信号量,那么信号量的值仅是简单的加1(则信号量大于0,有效);如果有任务等待该信号量,那么就会有另一个任务进入就绪态,信号量的值就不加1
之后,这个释放的信号量给那个等待中的任务,要看内核如何调度的。收到信号量的任务可能是如下两者之一:
◆等待任务中,优先级最高的;(uc/os-ii仅支持这种方式)。
◆最早开始等待信号量的任务(如果是按先进先出FIFO原则)。
  
2. 信号量的有效与无效问题
信号量有效:信号量的计算器非0.OSEventCnt=0)。信号量有效表示任务对资源可用。
信号量无效:信号量的计算器为0。信号量无效表示任务对目前资源不可用,需要等待其他另一个任务(或者中断服务子程序)发出该信号量(OSSemPost)。
3. 信号量的值(.OSEventCnt)大小表示什么?
二值信号量,表示任务可以独占共享资源。
计数式信号量,用于某资源可同时为N个任务所用。
4. 信号量是如何实现任务之间的通信的?
参见第1点的(4)(5)概述。
5. 信号量有关的三个重要函数分析
◆OSSemCreate() 创建一个信号量   (注:由任务或启动代码操作)
创建工作必须在任务级代码中或者多任务启动之前完成。功能只要是先获取一个事件控制块ECB,写入一些参数。其中调用了OS_EeventWaitListInt()函数,对事件控制块的等待任务列表进行初始化。完成初始化工作后,返回一个该信号量的句柄(Handle)
  
◆OSSemPend() 等待一个信号量 (注:只能由任务操作)
本函数应用于任务试图获得共享资源的使用权、任务需要与其他任务或中断同步及任务需要等待特定事件发生的场合。
如果任务Task_A调用OSSemPend(),且信号量的值有效(非0),那么OSSemPend()递减信号量计数器(.OSEventCnt),并返回该值。换句话说,Task_A获取到共享资源的使用权了,之后就执行该资源。
如果如果任务Task_A调用OSSemPend(),信号量无效(为0),那么OSSemPend()调用OS_EventTaskWait()函数,把Task_A放入等待列表中。(等待到什么时候呢?要看OSSemPost()(或者等待超时情况),由它释放信号量并检查任务执行权,见下资料)
  
◆OSSemPost() 发出(释放)一个信号量 (注:由任务或中断操作)
本函数其中调用OS_EventTaskRdy()函数,把优先级最高的任务Task_A(在这假如是Task_A,另外假设当前调用OSSemPost()的任务是Task_B)从等待任务列表中去除,并使它进入就绪态。然后调用OSSched()进行任务调度。如果Task_A是当前就绪态中优先级最高的任务,则内核执行Task_A;否则,OSSched()直接返回,Task_B继续执行.
  互斥型信号量
1.互斥型信号量(mutex)
       互斥型信号量具备uc/os-ii信号量的所有机制,但还具有其他一些特性。
       任务可利用互斥型信号量来实现对共享资源的独占处理。
     Mutex是二值信号量,1表示资源是可以使用的。
  
2.关于优先级反转
    下面概述优先级反转原理:
       假设有三个任务,分别命名为A,B,CA的优先级最高,C的优先级最低。任务A和任务B处于挂起状态(请注意这条件),等待某一事件的发生,任务C正在运行。当任务C等待到共享资源(命名为S1)并使用后,如果任务A等待得事件到来之后,由于A的优先级最高,所以就会剥夺任务CCPU使用权。运行过程中,任务A也要使用资源S1,但S1的信号量还被任务C占用着,所有任务A只能进入挂起状态,等待任务CS1的信号量的释放。此时任务C得以继续运行。
       同理,任务B的事件到来后,会剥夺任务CCPU使用权。任务B把事情搞定以后,把CPU使用权归还给任务B(呵呵,优先级低就是给人欺负啊,所以做人还真的要争口气!)。任务B又得以继续运行,任务B认真处理完毕资源S1后,终于可以释放S1的信号量。而处于等待该信号量的任务A马上得到信号量并开始处理共享资源S1
    综述上面情况,任务C和任务A的优先级发生了反转。
       而互斥型信号量就是具有解决优先级反转问题的特性。
3uc/os-ii的互斥型信号量由三个部分组成:
       ◆一个标志,指示mutex是否可以使用(0或1)
◆一个优先级,准备一旦高优先级的任务需要这个mutex,赋予给占有mutex的任务。
◆一个等待该mutex的任务列表。
技术 | 阅读 8368 次
文章评论,共6条
vfdff(作者)
2009-07-02 23:47
1
一个是请求一个是释放。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSSemPend();// 请求<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用共享资源;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSSemPost();// 释放<br />
OSTimeDly
vfdff(作者)
2009-07-02 23:50
2
浅析μCOS/II v2.85内核OSSemPend()和OSSemPost()函数工作原理<br />
<br />
文章来源:http://gliethttp.cublog.cn[转载请声明出处]<br />
//1.OSSemPend()函数<br />
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT8U pend_stat;<br />
#if OS_CRITICAL_METHOD == 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_CPU_SR cpu_sr = 0;<br />
#endif<br />
<br />
#if OS_ARG_CHK_EN &gt; 0<br />
&nbsp;&nbsp;&nbsp; if (perr == (INT8U *)0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; if (pevent == (OS_EVENT *)0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_PEVENT_NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
#endif<br />
&nbsp;&nbsp;&nbsp; if (pevent-&gt;OSEventType != OS_EVENT_TYPE_SEM) {<br />
//确保该event控制块是Sem类型<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_EVENT_TYPE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; if (OSIntNesting &gt; 0) {<br />
//ISR中,不能使用OSSemPend()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_PEND_ISR;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; if (OSLockNesting &gt; 0) {<br />
//μCOS/II v2.85内核已经被强制锁住<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_PEND_LOCKED;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
//非法的统统不是,信号正常,所以有必要进一步处理<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_ENTER_CRITICAL();<br />
&nbsp;&nbsp;&nbsp; if (pevent-&gt;OSEventCnt &gt; 0) {<br />
//程序的其他地方已经触发了事件,异或在初始化时设定了n,如:OSSemCreate(2);<br />
//所以该task无需悬停,直接获得事件的使用权<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pevent-&gt;OSEventCnt--;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EXIT_CRITICAL();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_NONE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
//当前还没有任何事件发生,所以本task需要悬停,让出cpu[gliethttp]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSTCBCur-&gt;OSTCBStat |= OS_STAT_SEM;//是sem事件让本task进入悬停等待的<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSTCBCur-&gt;OSTCBStatPend = OS_STAT_PEND_OK;//假定不是超时,为正常收到信号<br />
//超时,如果timeout=0,那么,本task将一直悬停,仅仅当收到事件触发信号后才重新进入调度队列<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSTCBCur-&gt;OSTCBDly = timeout;<br />
//OS_EventTaskWait()函数实现的功能:<br />
//把本task从就绪控制矩阵中摘下,放到pevent事件专有的进程事件控制矩阵表中.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EventTaskWait(pevent);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EXIT_CRITICAL();<br />
//因为本task正在运行,所以本task现在的优先级最高,现在本task已经将自己从就绪控制矩阵--调度器(x,y)矩形阵列中<br />
//把自己摘掉,所以调度函数OS_Sched()一定会切换到另一个task中执行新task的代码[gliethttp]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_Sched();//具体参见《浅析μC/OS-II v2.85内核调度函数》<br />
//2007-09-09 gliethttp<br />
//可能因为OSSemPend()中指定的timeout已经超时<br />
//[由OSTimeTick()函数把本task重新置入了就绪控制矩阵,具体参考《浅析μC/OS-II v2.85内核OSTimeDly()函数工作原理》],<br />
//又或者确实在应用程序的某个地方调用了OSSemPost(),以下代码将具体解析是有什么引起的:1.超时,2.收到正常信号<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_ENTER_CRITICAL();<br />
&nbsp;&nbsp;&nbsp; if (OSTCBCur-&gt;OSTCBStatPend != OS_STAT_PEND_OK) {<br />
//是因为timeout超时,使得本task获得重新执行的机会<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pend_stat = OSTCBCur-&gt;OSTCBStatPend;<br />
//清除event事件块上本task的标志<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EventTOAbort(pevent);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EXIT_CRITICAL();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch (pend_stat) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case OS_STAT_PEND_TO:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_TIMEOUT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case OS_STAT_PEND_ABORT:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*perr = OS_ERR_PEND_ABORT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br />
&nbsp;&nbsp;&nbsp; }<br />
//由OSSemPost()抛出正常事件,唤醒了本task,因为在OSSemPost()时,<br />
//已经将本task在event事件控制矩阵上的对应位清除掉,并且把本task放入了就绪控制矩阵中,<br />
//否则本task也不会执行至此.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSTCBCur-&gt;OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OS_EXIT_CRITICAL();<br />
&nbsp;&nbsp;&nbsp; *perr = OS_ERR_NONE;<br />
}<br />
//----------------------------------------------------------------------<br />
//2.OS_EventTaskWait()函数<br />
void OS_EventTaskWait (OS_EVENT *pevent)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;INT8U y;<br />
//2007-09-09 gliethttp<br />
//pevent为此次task挂起的EventPtr单元<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSTCBCur-&gt;OSTCBEventPtr = pevent;<br />
//清除调度器中该task对应的标志位<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y = OSTCBCur-&gt;OSTCBY;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSRdyTbl[y] &amp;= ~OSTCBCur-&gt;OSTCBBitX;<br />
&nbsp;&nbsp;&nbsp; if (OSRdyTbl[y] == 0) {<br />
//当前y行对应的8个或16个task都已经悬停,那么当前y行也清除.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSRdyGrp &amp;= ~OSTCBCur-&gt;OSTCBBitY;<br />
&nbsp;&nbsp;&nbsp; }<br />
//2007-09-09 gliethttp<br />
//将该task的prio添加到pevent事件控制矩阵中,这个矩阵的功能和OSRdyGrp、OSRdyTbl没有区别<br />
//都是用来计算出已经就绪tasks中的优先级最高的那个<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pevent-&gt;OSEventTbl[OSTCBCur-&gt;OSTCBY] |= OSTCBCur-&gt;OSTCBBitX;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pevent-&gt;OSEventGrp |= OSTCBCur-&gt;OSTCBBitY;<br />
}
vfdff(作者)
2009-07-03 00:00
3
中断中发送的信号量的地方: <br />
if(tempChar == CHAR_LF) //收到LF符 <br />
{ <br />
&nbsp;&nbsp;&nbsp; RxBufEn = 0; <br />
<br />
&nbsp;&nbsp;&nbsp; #ifdef RxSigEn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//这个宏就是调试时用的,如果打开这个宏,使用信号量控制,程序就会跑到上面的undefined处, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//不启用时,一点问题没有,那样就是用RxBufEn变量来控制 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSSemPost(SemUartRec);//发信号量,接收完成 <br />
&nbsp;&nbsp;&nbsp; #endif <br />
} <br />
<br />
任务中等待信号量的地方: <br />
#ifdef RxSigEn <br />
&nbsp;&nbsp;&nbsp; OSSemPend(SemUartRec,0,&amp;err); //一直等待到收到完整数据 <br />
&nbsp;&nbsp;&nbsp; Uart0Process(); <br />
#else <br />
&nbsp;&nbsp;&nbsp; if(RxBufEn==0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //等待接收完成 <br />
&nbsp;&nbsp;&nbsp; { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Uart0Process(); <br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; OSTimeDly(10); <br />
#endif
vfdff(作者)
2009-07-03 23:14
4
理解互斥量和信号量 作者: JuKevin <br />
<br />
互斥量(Mutex) <br />
<br />
<br />
互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。 <br />
<br />
 <br />
<br />
&nbsp;&nbsp;<br />
<br />
Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。 <br />
<br />
Mutex可以被抽象为四个操作: <br />
<br />
- 创建 Create <br />
<br />
- 加锁 Lock <br />
<br />
- 解锁 Unlock <br />
<br />
- 销毁 Destroy <br />
<br />
Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。 <br />
<br />
不同操作系统中提供的Mutex函数: 动作\系统<br />
 Win32<br />
 Linyx<br />
 Solaris<br />
 <br />
创建<br />
 CreateMutex<br />
 pthread_mutex_init<br />
 mutex_init<br />
 <br />
加锁<br />
 WaitForSingleObject<br />
 pthread_mutex_lock<br />
 mutex_lock<br />
 <br />
解锁<br />
 ReleaseMutex<br />
 pthread_mutex_unlock<br />
 mutex_unlock<br />
 <br />
销毁<br />
 CloseHandle<br />
 pthread_mutex_destroy<br />
 mutex_destroy<br />
 <br />
<br />
<br />
<br />
&nbsp;&nbsp;<br />
<br />
信号量 <br />
<br />
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。 <br />
<br />
信号量可以分为几类: <br />
<br />
&sup2; 二进制信号量(binary semaphore):只允许信号量取0或1值,其同时只能被一个线程获取。 <br />
<br />
&sup2; 整型信号量(integer semaphore):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。 <br />
<br />
&sup2; 记录型信号量(record semaphore):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。 <br />
<br />
信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。 <br />
<br />
计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。 <br />
<br />
<br />
<br />
Semaphore可以被抽象为五个操作: <br />
- 创建 Create <br />
<br />
- 等待 Wait: <br />
<br />
线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。 <br />
<br />
-释放 Post <br />
<br />
执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。 <br />
<br />
-试图等待 TryWait <br />
<br />
如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。 <br />
<br />
-销毁 Destroy <br />
<br />
信号量,是可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。 动作\系统<br />
 Win32<br />
 POSIX<br />
 <br />
创建<br />
 CreateSemaphore<br />
 sem_init<br />
 <br />
等待<br />
 WaitForSingleObject<br />
 sem _wait<br />
 <br />
释放<br />
 ReleaseMutex<br />
 sem _post<br />
 <br />
试图等待<br />
 WaitForSingleObject<br />
 sem _trywait<br />
 <br />
销毁<br />
 CloseHandle<br />
 sem_destroy<br />
 <br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
互斥量和信号量的区别 <br />
<br />
1. 互斥量用于线程的互斥,信号线用于线程的同步。 <br />
<br />
这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。 <br />
<br />
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。 <br />
<br />
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源 <br />
<br />
2. 互斥量值只能为0/1,信号量值可以为非负整数。 <br />
<br />
也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。 <br />
<br />
3. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。
vfdff(作者)
2009-07-03 23:15
5
线程同步互斥的控制方法2008年03月29日 星期六 下午 04:28四种进程或线程同步互斥的控制方法<br />
&nbsp;&nbsp;&nbsp; 1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 <br />
  2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 <br />
  3、信号量:为控制一个具有有限数量用户资源而设计。 <br />
  4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。 <br />
&nbsp;&nbsp;&nbsp;   <br />
临界区(Critical Section)<br />
<br />
<br />
  保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。 <br />
  临界区包含两个操作原语: <br />
  EnterCriticalSection() 进入临界区 <br />
  LeaveCriticalSection() 离开临界区 <br />
  EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。 <br />
  MFC提供了很多功能完备的类,我用MFC实现了临界区。MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。 <br />
互斥量(Mutex) <br />
&nbsp;&nbsp;&nbsp;<br />
  互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。 <br />
&nbsp;&nbsp;&nbsp;<br />
  互斥量包含的几个操作原语: <br />
  CreateMutex() 创建一个互斥量 <br />
  OpenMutex() 打开一个互斥量 <br />
  ReleaseMutex() 释放互斥量 <br />
  WaitForMultipleObjects() 等待互斥量对象 <br />
&nbsp;&nbsp;&nbsp;<br />
  同样MFC为互斥量提供有一个CMutex类。使用CMutex类实现互斥量操作非常简单,但是要特别注意对CMutex的构造函数的调用 <br />
  CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL) <br />
  不用的参数不能乱填,乱填会出现一些意想不到的运行结果。<br />
<br />
信号量(Semaphores)<br />
<br />
<br />
  信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。 <br />
  PV操作及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。 <br />
  P操作 申请资源: <br />
&nbsp;&nbsp;&nbsp;  (1)S减1; <br />
&nbsp;&nbsp;&nbsp;  (2)若S减1后仍大于等于零,则进程继续执行; <br />
&nbsp;&nbsp;&nbsp;  (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。 <br />
&nbsp;&nbsp;&nbsp;V操作 释放资源: <br />
&nbsp;&nbsp;&nbsp;  (1)S加1; <br />
&nbsp;&nbsp;&nbsp;  (2)若相加结果大于零,则进程继续执行; <br />
&nbsp;&nbsp;&nbsp;  (3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。 <br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;  信号量包含的几个操作原语: <br />
&nbsp;&nbsp;&nbsp;  CreateSemaphore() 创建一个信号量 <br />
&nbsp;&nbsp;&nbsp;  OpenSemaphore() 打开一个信号量 <br />
&nbsp;&nbsp;&nbsp;  ReleaseSemaphore() 释放信号量 <br />
&nbsp;&nbsp;&nbsp;  WaitForSingleObject() 等待信号量 <br />
事件(Event) <br />
&nbsp;&nbsp;&nbsp;<br />
  事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。 <br />
  信号量包含的几个操作原语: <br />
&nbsp;&nbsp;&nbsp;  CreateEvent() 创建一个信号量 <br />
&nbsp;&nbsp;&nbsp;  OpenEvent() 打开一个事件 <br />
&nbsp;&nbsp;&nbsp;  SetEvent() 回置事件 <br />
&nbsp;&nbsp;&nbsp;  WaitForSingleObject() 等待一个事件 <br />
&nbsp;&nbsp;&nbsp;  WaitForMultipleObjects()         等待多个事件 <br />
&nbsp;&nbsp;&nbsp;    WaitForMultipleObjects 函数原型: <br />
&nbsp;&nbsp;&nbsp;     WaitForMultipleObjects( <br />
&nbsp;&nbsp;&nbsp;     IN DWORD nCount, // 等待句柄数 <br />
&nbsp;&nbsp;&nbsp;     IN CONST HANDLE *lpHandles, //指向句柄数组 <br />
&nbsp;&nbsp;&nbsp;     IN BOOL bWaitAll, //是否完全等待标志 <br />
&nbsp;&nbsp;&nbsp;     IN DWORD dwMilliseconds //等待时间 <br />
&nbsp;&nbsp;&nbsp;     ) <br />
  参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。<br />
<br />
总结: <br />
  1. 互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。 <br />
  2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来等待进程和线程退出。 <br />
  3. 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号灯对象可以说是一种资源计数器。
vfdff(作者)
2009-08-05 22:22
6
typedef struct _WndMailBox<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;MSG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg[SIZE_WND_MAILBOX];<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iReadPos;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iWritePos;<br />
} WndMailBox;<br />
typedef WndMailBox* PWndMailBox;<br />
<br />
<br />
5. 信号量有关的三个重要函数分析 <br />
◆OSSemCreate() 创建一个信号量&nbsp;&nbsp;&nbsp;(注:由任务或启动代码操作) <br />
创建工作必须在任务级代码中或者多任务启动之前完成。功能只要是先获取一个事件控制块ECB,写入一些参数。其中调用了OS_EeventWaitListInt()函数,对事件控制块的等待任务列表进行初始化。完成初始化工作后,返回一个该信号量的句柄(Handle)。<br />
&nbsp;&nbsp;<br />
◆OSSemPend() 等待一个信号量 (注:只能由任务操作) <br />
本函数应用于任务试图获得共享资源的使用权、任务需要与其他任务或中断同步及任务需要等待特定事件发生的场合。 <br />
如果任务Task_A调用OSSemPend(),且信号量的值有效(非0),那么OSSemPend()递减信号量计数器(.OSEventCnt),并返回该值。换句话说,Task_A获取到共享资源的使用权了,之后就执行该资源。 <br />
如果如果任务Task_A调用OSSemPend(),信号量无效(为0),那么OSSemPend()调用OS_EventTaskWait()函数,把Task_A放入等待列表中。(等待到什么时候呢?要看OSSemPost()(或者等待超时情况),由它释放信号量并检查任务执行权,见下资料) <br />
&nbsp;&nbsp;<br />
◆OSSemPost() 发出(释放)一个信号量 (注:由任务或中断操作) <br />
本函数其中调用OS_EventTaskRdy()函数,把优先级最高的任务Task_A(在这假如是Task_A,另外假设当前调用OSSemPost()的任务是Task_B)从等待任务列表中去除,并使它进入就绪态。然后调用OSSched()进行任务调度。如果Task_A是当前就绪态中优先级最高的任务,则内核执行Task_A;否则,OSSched()直接返回,Task_B继续执行.<br />
<br />
<br />
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)<br />
<br />
中断中发送的信号量的地方: <br />
if(tempChar == CHAR_LF) //收到LF符 <br />
{ <br />
&nbsp;&nbsp;&nbsp; RxBufEn = 0; <br />
<br />
&nbsp;&nbsp;&nbsp; #ifdef RxSigEn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//这个宏就是调试时用的,如果打开这个宏,使用信号量控制,程序就会跑到上面的undefined处, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//不启用时,一点问题没有,那样就是用RxBufEn变量来控制 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OSSemPost(SemUartRec);//发信号量,接收完成 <br />
&nbsp;&nbsp;&nbsp; #endif <br />
} <br />
<br />
任务中等待信号量的地方: <br />
#ifdef RxSigEn <br />
&nbsp;&nbsp;&nbsp; OSSemPend(SemUartRec,0,&amp;err); //一直等待到收到完整数据 <br />
&nbsp;&nbsp;&nbsp; Uart0Process(); <br />
#else <br />
&nbsp;&nbsp;&nbsp; if(RxBufEn==0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //等待接收完成 <br />
&nbsp;&nbsp;&nbsp; { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Uart0Process(); <br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; OSTimeDly(10); <br />
#endif <br />
<br />
<br />
请各位帮我看一下,如果用了信号量,程序跑一会儿就死了,暂停调试会PC会停在Undefined处,值为0x00000040.<br />
在中断函数第一行加上OSIntNesting++; 或者OSIntEnter(); <br />
如果你加了还是有问题,那我就不知道为什么了,如果没加,出现你说的现象的原因是: <br />
OSSemPost(SemUartRec);里面调用了任务级调度函数。任务级和中断级调度的唯一区别就是,任务级多了一次压栈处理。因为中断本身已经压了一次栈,所以中断级调度函数里不能再压,否则栈定会越来越高,最后溢出。在中断函数里调用OSSemPost(SemUartRec);等于就是多压了一次栈,但是如果在发出信号量前加上OSIntNesting++; 或者OSIntEnter();任务级调度函数不会有任务调度动作,也就不会压栈,就不会出问题了。 <br />
<br />
其实是没有问题的,我是打的时候手误,呵呵,有个地方语法打错了.让大家失望了,我的BLOG地址是: http://blog.meijwang.com
游客请输入验证码
浏览1970311次