原创 在uc-osII中进程间数据的传递如何进行心得(转载)

2009-11-14 13:40 4093 5 5 分类: MCU/ 嵌入式
不可避免的,任务之间肯定有共享数据,或者说需要传递一些参数,那么它们在修改、引用时,如何办呢?

保护和提供任务之间的通信方法有:


       1:通过全局变量


      这个是前后台系统最常用的参数传递方式,那么在uc-osII中也可以使用它,只是需要注意一些,譬如说,有两个任务需要对一个全局变量操作,那么在一个任务使用时,另外的一个任务是不能对其操作的,那么,如何做到呢?不切换任务的运行就可以做到,直到对这个全局变量操作完以后才可以进行任务切换,那么这里不让任务切换:1,就是不让节拍中断函数运行,禁止中断,就是进入临界段OS_Enter_Critical();,操作完后,退出临界段OS_Exit_Critical();2,就是设置一个锁,把切换任务的一段上锁OS_Sched_Lock();,使之不让它运行切换任务的程序,在对这个全局变量操作完以后,进行解锁OS_Sched_Un_Lock();,就可以了,后者相当去前者的嵌套;


      2:信号量、邮箱、消息队列(邮箱数组)


用这个传递时,需要的是一个事件控制块,因为这3个都可以叫做事件,其中的道理,我也不是很明白,捉摸中





typedef struct {



    void   *OSEventPtr;                /* 指向消息或者消息队列的指针 */



    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表      */



    INT16U OSEventCnt;               /* 计数器(当事件是信号量时) */



    INT8U   OSEventType;                   /* 事件类型 */



    INT8U   OSEventGrp;               /* 等待任务所在的组 */



} OS_EVENT;


大家注意:在使用OSSemCreate()时,前面已经创建了一个空闲链表,而这个表是由每一个事件控制块中的OSEventPtr指针指向下一个事件控制块的,而最后一个事件控制块的OSEventPtr指针指向的是空,就是(OS_EVENT *)0,所以有了上面的前提以后,再理解下面的创建一个事件控制块就非常好理解了。


OS_EVENT *OSSemCreate (INT16U cnt) ;该函数返回的数据类型为指针,指针指向的数据类型为OS_EVENT(事件的数据类型为结构体)。也就是函数返回一个地址,地址里存的是新创建的结构体类型所占据的内存的首地址。


OS_EVENT *OSSemCreate (INT16U cnt)


{


OS_EVENT *pevent;


pevent       =          OSEventFreeList;


              /*首先创建一个指向OS_EVENT结构体类型的指针pevent*/


            /*(OS_EVENT *)0的意思是把0强制转换为OS_EVENT 结构指针,0指*/


             /*向的内容为空,但是结构依然占有内存*/


   if (OSEventFreeList != (OS_EVENT *)0) {     //如果不是空           


                                                      /* See if pool of free ECB pool was empty   */
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;


             /*修改指针OSEventFreeList指向下一个,因为在创建空链表时 OSEventFreeList->OSEventPtr就是指向的下一个事件控制块,那么让OSEventFreeList转到下一个。OSEventFreeList,把void类型的OSEventPtr强制转*/



    }     


if (pevent != (OS_EVENT *)0) {    /*和上面一样,如果事件控制块不为空*/
            pevent->OSEventType    = OS_EVENT_TYPE_SEM;


/*是哪一种类型:


#define OS_EVENT_TYPE_UNUSED      0
         #define OS_EVENT_TYPE_MBOX           1
         #define OS_EVENT_TYPE_Q                    2
         #define OS_EVENT_TYPE_SEM              3
         #define OS_EVENT_TYPE_MUTEX       4
         #define OS_EVENT_TYPE_FLAG            5


*/
            pevent->OSEventCnt     = cnt;       /*设置计数型信号量的值*/
            pevent->OSEventPtr     = (void *)0;


                              /*没有使用邮箱或者消息队列*/
#if OS_EVENT_NAME_SIZE > 1
        pevent->OSEventName[0] = '?';   /* 不知道的名称,是什么意思?*/
        pevent->OSEventName[1] = OS_ASCII_NUL;
#endif


OS_EventWaitListInit(pevent);通过调用OSEventWaitListInit()对事件控制块中的等待任务列表进行初始化。该函数初始化一个空的等待任务列表,其中没有任何任务。该函数的调用参数只有一个,就是指向需要初始化的事件控制块的指针pevent


return (pevent); /*返回创建的结构体类型数据*/                                     


}



信号量的种类与用途:


         互斥信号量、二值信号量、计数信号量


         互斥的用于解决互斥问题,据说会引起优先级反转的问题


         二值的用于解决同步问题


          计数的用于解决资源计数问题


         比如说你的电脑上有3台打印机,表示有三个资源,那么设置计数信号量为3,如果有一个任务使用了一台打印机,那么计数信号量就减一操作,表示还有2个打印机可用,此时若有另外一个任务也想使用打印机,同样减一操作,假如说同时有四个任务都想使用打印机,而只有3台,那么此时的计数信号量已经为0,所以,有一个任务肯定是需要处于等待状态的


          对于邮箱,先建立一个邮箱,它也是一个链表,有邮箱的头和尾,发消息好像就是把消息的地址传给邮箱,相当于插入,或者说申请一个消息资源,对其进行写操作,而接收消息也是接收存储于邮箱中的消息的指针,使用完后归还资源


          创建邮箱和创建信号量差不多,不同之处在于事件控制块的类型被设置成OS_EVENT_TYPE_MBOX,以及使用.OSEventPtr域来容纳消息指针,而不是使用.OSEventCnt 域。也就是二者的不同之处在于二者的类型不同:sem 是信息量,使用的的是.OSEventCnt ,用它来表征共享资源的情况,表征有无任务使用共享资源(互斥型信息量)或者是否达到能同时使用该共享资源的任务的最大的数目(计数型信息量),因此我们只用查询.OSEventCnt 变量的具体值就可以判断共享资源的使用情况。 Mbox是消息邮箱,即不同任务传递事件的时候传递的是一个指针,也就是一个地址,这个地址是共享资源的首地址。


OS_EVENT *OSMboxCreate (void *msg)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR cpu_sr;
#endif   
    OS_EVENT *pevent;



    if (OSIntNesting > 0) {                      /* See if called from ISR ...                         */
        return ((OS_EVENT *)0);                  /* ... can't CREATE from an ISR                       */
    }
    OS_ENTER_CRITICAL();
    pevent = OSEventFreeList;                    /* Get next free event control block                  */
    if (OSEventFreeList != (OS_EVENT *)0) {      /* See if pool of free ECB pool was empty             */
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    if (pevent != (OS_EVENT *)0) {
        pevent->OSEventType    = OS_EVENT_TYPE_MBOX;
        pevent->OSEventCnt     = 0;
        pevent->OSEventPtr     = msg;            /* Deposit message in event control block             */
#if OS_EVENT_NAME_SIZE > 1
        pevent->OSEventName[0] = '?';
        pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
        OS_EventWaitListInit(pevent);
    }
    return (pevent);                             /* Return pointer to event control block              */
}
                                                                                    


消息队列也可以用于同步,只需将消息队列的缓冲区的长度设置为0,这样单独发送消息的任务将因为没有缓冲区而进入等待状态,而单独接收消息的任务也将没有消息可取而进入等待状态,只有消息的发送和接收都发生了,此时的这两个任务才将得以继续运行,从而达到同步效果

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
5
关闭 站长推荐上一条 /3 下一条