MTK task 简单总结(速成版)(11页).doc
-MTK task 简单总结(速成版)-第 11 页MTK task 小结 1MTK 的基本执行单元是task,从操作系统的角度来理解,task 有些像线程而不是进程,进程之间的地址空间是相互隔离的,说白点就是进程之间的全局变量是不相互干扰的。而线程之间则是用同一个地址空间,MTK的task之间的地址空间也是共同的,也就是说在MTK 编程里,定义了一个全局变量,那么在任何一个task里面都能引用,(这里只是举个例子,在实际编程过程中最好不要用全局变量,实在没有办法避开,那么全局变量也要分模块化,进行封装)。所以说,MTK 的task 更像线程,MTK 用的是实时操作系统 nucleus,是非抢占式操作系统,也就是当高优先级的task 在运行时,低优先级的task是得不到运行时间的,除非等高优先级的task 因为种种原因挂起。MTK 还有一个跟task 想关的概念叫 module,它跟task 之间的关系是:一个task 可以对应多个module。task 主要表示是一个执行单元,module 主要是用于传递消息,在MTK 中,消息传递是module 为单位 src_mod > des_mod,而不是以task为单位。 MTK task 小结 2虽然MTK手机,是feature phone(功能机),不像symbian 6 那样可以同时运行多个应用。但是MTK还是由许多task组成。平时MTK的后台播放MP3就是由一个task 完成的。具体以后分析。现在来看看MTK 最主要的task,MMI task,MTK的应用程序都是在该task里面运行,它有一整套开发MTK应用的framework。先来看创建MMI task的函数kal_bool mmi_create(comptask_handler_struct *handle) /* Local Variables */ static comptask_handler_struct mmi_handler_info = MMI_task, /* task entry function */ MMI_Init, /* task initialization function */ NULL, NULL, /* task reset handler */ NULL, /* task termination handler */ /* Code Body */ *handle = &mmi_handler_info; return KAL_TRUE;这个函数的结构,是MTK 创建task的基本结构,系统初始化时,会调用该函数。看里面的结构体typedef struct kal_task_func_ptr comp_entry_func; /task 的入口函数 task_init_func_ptr comp_init_func; /task 的初始化函数 task_cfg_func_ptr comp_cfg_func; /task 的配置函数 task_reset_func_ptr comp_reset_func; /task 的重置函数 task_end_func_ptr comp_end_func; /task 的终止函数 comptask_handler_struct;task 的入口函数是必须的,这个函数告诉系统,初始化完相应的task 控制块后,就要进入该函数来运行。task 初始化函数,是在进入 task 入口函数之前被调用,用来初始化可能需要的资源,可选。task 终止函数是,当task 结束是要调用,用来释放资源,可选。其他两个函数我也不清楚干什么,希望知道的共享下 先看MMI task 的初始化函数.MMI_BOOL MMI_Init(task_indx_type task_indx) /创建一个mutex(互斥体) mmi_mutex_trace = kal_create_mutex("mmi_trace"); /这个是初始化 2step 按键, 2step 按键是指 有一些按键具有半按下状态 /比如照相功能,按下一半进行聚焦,再按下一半拍照 mmi_frm_get_2step_keys();/初始化timer。L4InitTimer(); /初始化 UI 相关信息,里面有许多画点,图等函数 setup_UI_wrappers(); return MMI_TRUE;初始化函数比较简单。下面来看MMI 的入口函数,这个函数是整个MMI 运行的核心。/为了简单,删除了大部分宏控制程序void MMI_task(oslEntryType *entry_param) MYQUEUE Message; oslMsgqid qid; U32 my_index; U32 count = 0; U32 queue_node_number = 0; / 获得task的外部消息队列id,通过这个id,获得别的task 往MMI task发送的消息 / MMI task 有两个消息,外部消息队列和内部消息队列 / 外部消息队列的消息不直接处理,只是简单的存放到内部消息队列, / 这样使内部消息队列的优先级稍微高一点 qid = task_info_gentry_param->task_indx.task_ext_qid; mmi_ext_qid = qid; / 初始化 event 处理函数,这个几个event 必须在获得消息前就进行注册 / 不然可能使得这个event 丢弃。具体event 事件,下次介绍 InitEventHandlersBeforePowerOn(); /进入task 的while 循环 / task 的while(1) 循环使得这个task 不会结束,只有挂起或者运行 while (1) / 判断是否有 key 事件需要处理 if (g_keypad_flag = MMI_TRUE) mmi_frm_key_handle(NULL); / 获得外部消息队列里,消息的个数 msg_get_ext_queue_info(mmi_ext_qid, &queue_node_number); / 如果没有任何消息需要处理(内部消息和外部消息都没有,同时也没有按键需要处理) / OslNumOfCircularQMsgs 获得内部消息队列消息的个数 if (queue_node_number = 0) && (OslNumOfCircularQMsgs() = 0) && (g_keypad_flag = MMI_FALSE) U8 flag = 0; ilm_struct ilm_ptr; /去外部消息队列里获得消息,这是一个阻塞函数,也就是说,如果外部消息队列里, /没有任何消息,那么这个task 将被阻塞,或者说挂起,也就是不在运行, /直到有消息到达,才会被唤醒, 看过操作系统原理的,应该不难理解这个意思和这个本质 OslReceiveMsgExtQ(qid, &Message); /如果有消息,获得task 的index OslGetMyTaskIndex(&my_index); / 设置该task的获得mod 为MMI mod. OslStackSetActiveModuleID(my_index, MOD_MMI); /保存该消息,用于放入到内部队列 ilm_ptr.src_mod_id = Message.src_mod_id; ilm_ptr.dest_mod_id = Message.dest_mod_id; ilm_ptr.msg_id = Message.msg_id; ilm_ptr.sap_id = Message.sap_id; ilm_ptr.local_para_ptr = Message.local_para_ptr; ilm_ptr.peer_buff_ptr = Message.peer_buff_ptr; /放入内部队列 / 这个内部队列是个简单的循环队列 flag = OslWriteCircularQ(&ilm_ptr); / 对 timer 消息进行特殊处理 if (Message.src_mod_id != MOD_TIMER) hold_local_para(ilm_ptr.local_para_ptr); hold_peer_buff(ilm_ptr.peer_buff_ptr); OslFreeInterTaskMsg(&Message); else / 把外部消息放入到内部消息 mmi_frm_fetch_msg_from_extQ_to_circularQ(); /处理内部消息 count = OslNumOfCircularQMsgs(); while (count > 0) OslGetMyTaskIndex(&my_index); OslStackSetActiveModuleID(my_index, MOD_MMI); if (OslReadCircularQ(&Message) CheckAndPrintMsgId(U16) (Message.msg_id); /是否是 wap 的消息 / 这里就体现了一个task 可以对应多个mod if (Message.dest_mod_id = MOD_WAP) else switch (Message.msg_id) /timer消息 具体看 MTK timer 小结 2 case MSG_ID_TIMER_EXPIRY: kal_uint16 msg_len; /处理stack timer消息 EvshedMMITimerHandler(get_local_para_ptr(Message.oslDataPtr, &msg_len); break; /开机消息 /具体分析 见后文 case MSG_ID_MMI_EQ_POWER_ON_IND: mmi_eq_power_on_ind_struct *p = (mmi_eq_power_on_ind_struct*) Message.oslDataPtr; /* To initialize data/time */ SetDateTime(void*)&(p->rtc_time); gdi_init(); g_pwr_context.PowerOnMMIStatus = MMI_POWER_ON_INDICATION; switch (p->poweron_mode) case POWER_ON_KEYPAD: OslMemoryStart(MMI_TRUE); g_charbat_context.PowerOnCharger = 0; g_pwr_context.PowerOnMode = POWER_ON_KEYPAD; DTGetRTCTime(&StartUpTime); memset(&LastDuration, 0, sizeof(LastDuration); mmi_bootup_entry_disk_check(); break; case POWER_ON_PRECHARGE: case POWER_ON_CHARGER_IN: g_pwr_context.PowerOnMode = p->poweron_mode; InitializeChargingScr(); if (!g_charbat_context.isChargerConnected) QuitSystemOperation(); break; case POWER_ON_ALARM: g_pwr_context.PowerOnMode = POWER_ON_ALARM; gdi_layer_clear(GDI_COLOR_BLACK); AlmInitRTCPwron(); break; case POWER_ON_EXCEPTION: g_pwr_context.PowerOnMode = POWER_ON_EXCEPTION; gdi_layer_clear(GDI_COLOR_BLACK); OslMemoryStart(MMI_TRUE); SetAbnormalReset(); InitializeAll(); OslDumpDataInFile(); ClearInputEventHandler(MMI_DEVICE_ALL); ClearKeyHandler(KEY_END, KEY_LONG_PRESS);InitNvramData(); InitAllApplications(); mmi_pwron_exception_check_display();break; default: break; break; / event 时间,这个也是MMI task 的一个重点 default: ProtocolEventHandler( (U16) Message.oslMsgId, (void*)Message.oslDataPtr, (int)Message.oslSrcId, (void*)&Message); break; OslFreeInterTaskMsg(&Message); msg_get_ext_queue_info(mmi_ext_qid, &queue_node_number); count-;MMI task 内容比较多,删除了一些代码,留下主要的骨干。总体来看,1 把外部消息放入内部消息队列 2 处理各种消息,开机消息,按键消息 和event 机制注册的各种其他消息MTK task 小结 3在 MTK task 小结 2 中 大概描述了下 MMI task 的工作方式:从外部队列获取消息放入内部消息队列,内部消息队列根据消息类型注册的回调函数,进行调用(event 机制,这个又是MMI framework的主要部分之一)。在MTK上,用户(开发人员)可以根据需要,创建task。创建一个task 分为 4 步:1 增加一个task index 到 custom_task_indx_type 2 增加一个mod index 到 custom_module_type3 把mod 关联到 相应的 task上,因为一个task 可以对应多个mod,所以需要把mod 挂载到 task上。(用挂载这个词,应该就比较好理解了,task 是MTK 执行的基本单位,所以一个mod 要能独立运行,就要挂载到某个task 上,为什么不一个mod一个task呢,我想task越多,多系统效率影响就越大。那么就可以考虑互斥的mod 挂载到一个task上,反正互斥的,不会同时需要运行,就像音乐,视频,照相机一样,不会同时运行)4 创建 task 基本信息 到 custom_comp_config_tbl下面来具体看一个例子。1 添加 task index typedef enum INDX_CUSTOM1 = RPS_CUSTOM_TASKS_BEGIN, INDX_CUSTOM2,#ifdef TASK_CREATE_TEST INDX_TASK_TEST,#endif RPS_CUSTOM_TASKS_END custom_task_indx_type;我们增加了一个 task index INDX_TASK_TEST2 添加一个 mod indextypedef enum MOD_CUSTOM1 = MOD_CUSTOM_BEGIN, MOD_CUSTOM2,#ifdef TASK_CREATE_TEST MOD_TASK_TEST,#endif MOD_CUSTOM_END custom_module_type;我们增加了一个mod index MOD_TASK_TEST3 挂载mod 到 task上custom_task_indx_type custom_mod_task_g MAX_CUSTOM_MODS = INDX_CUSTOM1, /* MOD_CUSTOM1 */ INDX_CUSTOM2, /* MOD_CUSTOM2 */#ifdef TASK_CREATE_TEST INDX_TASK_TEST,#endif INDX_NIL /* Please end with INDX_NIL element */这样就把 MOD_TASK_TEST 挂载到 INDX_TASK_TEST上面了,这里的映射关系是通过index 来控制的,也就是说要的到MOD_TASK_TEST 对应的 task index,只要这样 task index = custom_mod_task_gMOD_TASK_TEST; 所以创建过程中,顺序一定要对应好,不然容易出错。4 创建task 信息const comptask_info_struct custom_comp_config_tbl MAX_CUSTOM_TASKS = /* INDX_CUSTOM1 */ "CUST1", "CUST1 Q", 210, 1024, 10, 0, #ifdef CUSTOM1_EXIST custom1_create, KAL_FALSE#else NULL, KAL_FALSE#endif /* INDX_CUSTOM2 */ "CUST2", "CUST2 Q", 211, 1024, 10, 0, #ifdef CUSTOM2_EXIST custom2_create, KAL_FALSE#else NULL, KAL_FALSE#endif ,#ifdef TASK_CREATE_TEST /* INDX_TASK_TEST */ "TAST_TEST", "TASK_TEST Q", 212, 1024, 10, 0, task_test_create, KAL_FALSE#endif这样就创建好了task 的信息,这里说task 需要的信息typedef struct kal_char *comp_name_ptr; /task 的name kal_char *comp_qname_ptr; /外部队列name kal_uint32 comp_priority; /优先级 kal_uint16 comp_stack_size; /stack 大小 kal_uint8 comp_ext_qsize; /外部队列大小 kal_uint8 comp_int_qsize; /内部队列大小 kal_create_func_ptr comp_create_func; /task 创建函数 kal_bool comp_internal_ram_stack; /是否是internal_ram_stack comptask_info_struct;task 的优先级是数值越大,优先级越低。由于是MTK 用的是实时操作系统,高优先级的task 只要需要,就会先运行,一直运行,所以task的优先级定义时需要考虑清楚。comp_internal_ram_stack 表示是否使用internal ram stack,internal ram 相对速度要快,但是数量很有限,一般自己创建的不要去使用,容易引起问题。MTK task 小结 4在 MTK task 小结 3 中写到创建一个MTK task,没有写完,今天把剩下的写完。下面需要介绍 创建task 信息的函数。kal_bool task_test_create(comptask_handler_struct *handle) static const comptask_handler_struct task_test_handler_info = task_test_main, /* task entry function */ NULL, /* task initialization function */ NULL, /* task configuration function */ NULL, /* task reset handler */ NULL, /* task termination handler */ *handle = (comptask_handler_struct *)&task_test_handler_info; return KAL_TRUE;这个函数的结构是不是很眼熟,对,就是 MTK task 小结 2 介绍MMI task 创建函数 mmi_create,创建函数的格式都是一样的,具体结构体的说明就看 MTK task 小结 2 ,相应的函数可以补充,简单期间就有一个入口函数,不过一般都够了。void task_test_main(task_entry_struct * task_entry_ptr) / 消息实体 ilm_struct current_ilm; /消息队列id oslMsgqid qid = task_info_gtask_entry_ptr->task_indx.task_ext_qid; /初始化一些信息,这里只会被执行一次 tast_test_init() / 进入消息循环 while ( 1 ) /接受消息,如果没有消息,挂起该 task receive_msg_ext_q(qid , ¤t_ilm); /根据消息 id 处理各种消息 switch (current_ilm.msg_id) case MSG_ID_TASK_TEST_XX: break; default: break; free_ilm(¤t_ilm);这样,一个task 就建立完成了。 如果想给自己的task 建立一个强大的 timer 功能,那么就可以根据 MTK timer 小结 3 里介绍的那样,建立一个event scheduler timer,注意,这个stack timer 里的mod 要写成MOD_TASK_TEST 而不是 MOD_MMI,这样 MSG_ID_TIMER_EXPIRY 就会发送到这个task里面,只要进行相应的处理就ok了。task 之间的交互是通过消息来传递的,当然也可以通过全局变量,共享内存等等(这里不是linux 那样的共享内存,因为MTK 里面,整个系统的地址空间只有一个,那么一个task里的内存,可以被另一个task访问,只要知道地址,没有限制,具体可以看 MTK 内存管理简单总结)。只要自己控制好(互斥问题),还是比较灵活的。比如想要在这个task 里面处理按键事件,比较繁琐,先要在MMI task里面接受按键事件,然后根据自己定义的消息,把这个按键事件发送到这个task里面,这个task里面根据相应的消息处理函数,分发处理。一般来说,task 做一些后台处理比较方便(跟io有关的),如果想实现并行(linux时间片那样系统自动切换task),那是不现实,因为这个不是抢占式系统,优先级高task的除非自己挂起,否则会一直运行。虽然说如果两个task 如果优先级一样,或进行轮询,但是我自己试了,没有实现,不知道是否是当时没有用对。如果有实现的童鞋一定要告诉我, MTK task 小结 5 (TASK之间的消息机制)今天继续昨天的,说一下task之间的消息发送。task 之间发送消息,第一是要添加一个消息 id,在custom_sap.h里面,/* Add customization message id here */MSG_ID_CUSTOM1_CUSTOM2 = CUSTOM_MSG_CODE_BEGIN,MSG_ID_CUSTOM2_CUSTOM1,MSG_ID_MSG_TEST, /添加了一个消息id接下来是发送MTK 发送消息是mod 到 mod,因为mod是挂载到task上,那么最终还是发送到task上。MTK 发送消息是有一套固定的接口,对消息实体也有固定要求。消息的主体是 ilm_struct 结构:typedef struct ilm_struct module_type src_mod_id; /源 mod id module_type dest_mod_id; /目的 mod id sap_type sap_id; / Service Access Pointer Identifier 不清楚干什么用的 msg_type msg_id; / 消息 id 就是上面创建的MSG_ID_MSG_TEST local_para_struct *local_para_ptr; /消息信息载体 1 peer_buff_struct *peer_buff_ptr; /消息信息载体 2 ilm_struct;这个消息结构不是随便malloc 出来的,是由专门的函数 allocate_ilm 来创建,这个函数会从一个pool 去申请,这样做可以方便控制,同时也可以减少内存分配释放操作,碎片的产生。创建完毕后,可以填完剩下的信息, 注意 allocate_ilm 参数是当前要发送消息的mod id,不要搞错了。比如:ilm_struct *ilm_ptr = allocate_ilm(MOD_TASK_TEST); /创建一个消息结构ilm_ptr->src_mod_id = MOD_TASK_TEST; /设定源mod idilm_ptr->dest_mod_id = MOD_MMI; /设定目的mod idilm_ptr->sap_id = 0;ilm_ptr->msg_id = (msg_type) MSG_ID_MSG_TEST; /设定消息id接着需要一个消息 实体,也就是搭载具体消息信息的结构体typedef struct local_para_struct LOCAL_PARA_HDR