通过消息通讯的方式,主要解决多任务系统中,业务执行的时序性问题,协调不同任务执行不同的步骤,统一节拍。多数的软件系统都是多任务系统,要求不同的任务(可以是线程,也可以是进程)运行特定的功能,各个任务间要通讯,使用消息是比较通用的的方式,有很多专业的消息中间件软体,下文描述如何进行消息通讯的设计。
一、各个任务的具体执行
不同任务承担特定的功能,将任务分解成不同的执行状态,通过条件迁移任务状态,这是常用的任务设计方法。消息只是给任务以激励,任务收到激励后产生反应,当然消息也可以是任务迁移的条件;对应出在什么样的任务状态下,响应什么样的消息,执行什么样的动作,回应什么样的消息,任务状态有迁移到哪种状态;
二、通过时序图明确任务间的通讯
在明确的各个任务后,业务流程的执行,一个执行流或数据流,需要流经不同的任务,同时又要有不同的回应,所以在时序图中可以先描述完全正常的执行流和数据流,不考虑异常的情况,减少先前设计任务间通讯过于复杂的情况,反而影响了设计。
正常的执行流和数据流通过不同的任务进行特定的处理,其中的处理中是包含有执行的先后次序,不同任务间常用的传递方式,消息是用的比较多的一种,而且相对比较灵活。特别是对时序有要求的传输场景非常合适。
一个任务向另外一个任务发消息,也可以直接发给自己,都是由消息队列来记录下先后次序。
举个数字电视的播放节目的例子,用户按遥控器上的按键,STB的接收到键值后,UI绘制出播放的节目的台号,同时TUNER进行信号频率的锁定,信号锁定后,数据经过解复用后,输出到解码器,解码器运算解码出视频图像和声音,输出到显示屏和喇叭。
三、代码中消息的定义
通常的做法是按模块定义,发出消息一方的模块,接收消息一方只是处理,消息可以带不同的参数,可以表示更多的含义,还可以有效避免过多的消息定义。
设计技巧
消息ID的格式: 源+目的+消息名称
源一般是指发出消息的模块或任务名称
目的一般是指接收消息的模块或任务名称
#define MCONTRL (0x01000000)
#define TUNER (0x02000000)
#define MCONTRL_TUNER_REQLOCK (MCONTRL | TUNER | 0x000000001)
#define MCONTRL_TUENR_STOP (MCONTRL | TUNER | 0x000000002)
#define TUNER_MCONTRL_ANSWER (TUNER | MCONTRL | 0x00000001)
也可以采用数据结构的方式来进行描述,数据结构方式来定义扩展性要比上一种要强些,使用起来代码多些,没有上面一种使用的直观。
typedef struct msg_s
{
int msgsource;
int msgtarget;
int msgindex;
int msgtype;
int msglen;
int *msgbuffer;
}MSG_S;
四、多任务下消息处理代码框架
多任务下代码设计已经是经典的设计了,有各种参考代码,可以通过代码生成器生成框架代码。
void kb_callback(char keyvalue, int mode)
{
UI_MSG_S msginfo;
msginfo.msgsource = MSG_KB;
msginfo.msgtarget = MSG_UIAPP;
switch(keyvalue)
{
case 0x01:
msginfo.msgindex = MSG_KB_KEY_1;
break;
case 0x02:
msginfo.msgindex = MSG_KB_KEY_2;
break;
case 0x03:
msginfo.msgindex = MSG_KB_KEY_3;
break;
case 0x04:
msginfo.msgindex = MSG_KB_KEY_4;
break;
case 0x05:
msginfo.msgindex = MSG_KB_KEY_5;
break;
case 0x06:
msginfo.msgindex = MSG_KB_KEY_6;
break;
case 0x07:
msginfo.msgindex = MSG_KB_KEY_7;
break;
case 0x08:
msginfo.msgindex = MSG_KB_KEY_8;
break;
/*
add other keyvalue
*/
default:
break;
}
EFT_SendMsg(msgid, (char *)&msginfo, sizeof(msginfo) );
return;
}
int main(void)
{
int msgid, serviceid, timerid;
…
AC_InitMsg(); //初始化消息队列
AC_KBInit();
AC_TimerInit();
AC_ServiceInit();
msgid = AC_CreateMsgQueue();
printf("\n +++ msgid =%d +++\n", msgid);
serviceid = AC_ServiceCreate(service_callback);
printf("\n +++ serviceid =%d +++\n", serviceid);
AC_KBRegisterCallBack(kb_callback);
timerid = AC_TimerCreate(timer_callback, 10);
printf("\n +++ timerid =%d +++\n", timerid);
AC_TimerStart(timerid);
printf("\n +++ TASK START +++\n");
…
while(1)
{
ret = AC_ReceiveMsg(msgid, buffer, 1024, 0);
if (ret <=0)
{
continue;
}
UI_Proc(msgid ,buffer, ret);
}
return 0;
}