现在的位置: 首页 > 综合 > 正文

UC/OS II_任务间通讯_信号量

2014年01月07日 ⁄ 综合 ⁄ 共 3425字 ⁄ 字号 评论关闭

看信号量这块,琢磨不明白,上网找了个资料:
个人认为很通俗易懂
简单地说: 
    当信号量=0时,表示信号量代表的资源不可用,操作系统就调用OSSemPend()函数的任务加入该信号量的等待任务列表中; 
    当信号量>0时,表示信号量代表的资源可用,OSSemPend()函数返回,任务可以使用资源。 
    一般地,信号量的最大值(nmax)表示资源的最大同时共享数。nmax=1,表示资源最多只能由一个任务使用,如读写某内存单元时,为保证该单元不被其它任务篡改,就使用nmax=1的(二值)信号量;nmax>1,表示资源可由多个任务使用,如FIFO,一个任务写某单元时,另一个任务可以写其它单元,则可使用nmax>1的(多值)信号量,信号量的大小用来表示FIFO的可用单元数。 
    减1操作:当该信号量=0时表示FIFO已满,任务只能等待;当该信号量>0时表示FIFO有空,可以使用,同时要减1表示调用OSSemPend()函数的任务已经使用了一个资源(FIFO单元),可使用资源少了一个。 
    加1操作:当某任务调用OSSemPost()从FIFO中取出一个值时,该FIFO单元就空出一个可写单元,也就是资源多了一个,为表示这个变化,信号量要加1,一旦信号量由0->1,则把资源给等待任务列表中优先级最高的任务(通过OSSemPend()函数的返回)。 
    总之,信号量的值代表共享资源的剩余量,用掉一个减1,空出一个加1。 
举个例子: 
顾客(任务)到银行办事,银行(OS)现有N名业务员(共享资源)。 
1. 办事前先要取号(OSSemPend()),号条一般有“前面有xx位顾客”,表示正在等待服务(资源)的顾客(任务)数。 
2. 另外假设银行有一指示牌(信号量)指示当前空闲的业务员的数量为n(信号量的值)。 
3. 当n>0时,表示有空闲的业务员,那么顾客可以立即去业务员那办理业务(OSSemPend()立即返回),这样空闲的业务员就少一个,指示牌指示的数量(信号量的值)就要减1,但n只能减到0。 
4. 当n=0时,表示没有空闲的业务员,那么顾客只能等待(OSSemPend()不返回,切换到其它任务)。 
5. 当某位业务员为顾客办完手续后,他就空闲下来,这样空闲的业务员就多一个,指示牌指示的数量(信号量的值)就要加1,但n只能加到N。 
   这时银行就会去查找有没有正在等待的顾客,如果有,就找出其中优先级最高的顾客,让他来办理业务(OSSemPend()返回)。 
6. 顾客在取号时若设置了等待时间,那么在等待时间过后,银行就会通知顾客时间到(OSSemPend()返回),顾客接着去办其它事。 
7. 也有的顾客希望:在取号时,如果有空闲的业务员他就办事,没有的话就走(去办其它事),那么就要用特殊的取号方式(OSSemAccept())。 
8. N=1时,表示只有一个业务员,指示牌只能指示0或1两个值,这就是二值信号量。 

不知道这个例子能不能说清楚信号量的概念,请大伙完善。 

通过以上资料,看下例子

ucos信号量的简单使用

#include "includes.h" 
OS_STK task_stack[10][100];      // 任务栈 
INT32U system_time;             // 系统时钟 
OS_TCB my_task_state;      // 任务状态 
OS_EVENT *semaphore;       // 信号量 
OS_EVENT *mailbox;         // 邮箱 
OS_EVENT *msg;             // 消息队列 
void *msg_queue[10]; 

OS_EVENT *tsk1;       // 信号量1 
OS_EVENT *tsk2;       // 信号量2 
OS_EVENT *tsk3;       // 信号量3 
  
void my_task1(void *pdata); 
void my_task2(void *pdata); 
void my_task3(void *pdata); 
void my_task4(void *pdata); 
  
void main(void) 

 OSInit();        
  
 // 用户程序中不能使用优先级0,1,2,3以及最低的四个优先级                 
    OSTaskCreate(my_task1, (void *)0, &task_stack[0][100 - 1], 7); 
 OSTaskCreate(my_task2, (void *)0, &task_stack[1][100 - 1], 8); 
 OSTaskCreate(my_task3, (void *)0, &task_stack[2][100 - 1], 9); 
// OSTaskCreate(my_task4, (void *)0, &task_stack[3][100 - 1], 9); 
  
 // 任务的通信机制 
 // 先建立相应的ECB,才能使用信号量,邮箱或者消息队列 
  
 // 建立信号量,初始化为0,表示事件的发生 
 // 如果大于0,则表示对共享资源的访问 
 tsk1 = OSSemCreate(0);    // task1 <==> task3        
 tsk2 = OSSemCreate(3);    // task1 <==> task2 
 tsk3 = OSSemCreate(0);    // task2 <==> task3 
  
// mailbox = OSMboxCreate((void *)0);           // 建立邮箱 
// msg = OSQCreate(&msg_queue[0], 10);          // 建立消息队列 
  
    OSStart();     
}  
  
void my_task1(void *pdata) 
{  
 INT32U i, j; 
 INT8U err; 
 pdata = pdata;  
 start_os_time();     // 启动系统时钟 
 OSSemPost(tsk1);  // 向task1发送信号量 
    
 while(1){  
  // 等待一个信号量 
  // 如果信号量大于0,将信号量减1,置OS_NO_ERR,然后返回 
  // 如果信号量为0,将该任务挂起到信号量的等待任务列表中,然后任务调度 
  OSSemPend(tsk1, 0, &err);   
  if(err == OS_NO_ERR){ 
   for(j = 0; j < 6; j++){ 
    i = 6000000; 
    while(i--);    
    LED_Monitor1 ^= 1; 
   } 
   // 发送一个信号量,表示一个事件发生了 
   // 当信号量 >= 0时,将信号量加1 
   // 如果有任务等待该信号量,优先级最高的任务进入就绪态,然后任务调度 
   OSSemPost(tsk2); 
  }    
 } 
}       
        
void my_task2(void *pdata) 

 INT32U i, j; 
 INT8U err; 
 pdata = pdata; 
   
 while(1){ 
  // 等待来自task1的信号量 
  OSSemPend(tsk2, 0, &err); 
  if(err == OS_NO_ERR){ 
   for(j = 0; j < 6; j++){ 
    i = 6000000; 
    while(i--); 
    LED_Monitor2 ^= 1; 
   }   
   // 发送一个信号量,切换到task1 
   // task1从OSSemPend()里面的某处继续执行 
   OSSemPost(tsk3); 
  }   
 } 
}     
void my_task3(void *pdata) 

 INT8U err; 
 INT32U i, j; 
 pdata = pdata; 
 while(1){ 
  // 等待来自task2的信号量 
  OSSemPend(tsk3, 0, &err); 
  if(err == OS_NO_ERR){ 
   for(j = 0; j < 6; j++){ 
    i = 6000000; 
    while(i--); 
    LED_Monitor3 ^= 1;     
   } 
   OSSemPost(tsk1); 
  } 
 } 
}

抱歉!评论已关闭.