鸿蒙内核源码分析多任务环境下的事件控制块

事件(event)是一种任务间通信的机制,可用于任务间的同步。
多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。
一对多同步模型:一个任务等待多个事件的触发。可以是任意一个事件发生时唤醒任务处理事件,也可以是几个事件都发生后才唤醒任务处理事件。
多对多同步模型:多个任务等待多个事件的触发。
鸿蒙提供的事件具有如下特点:
任务通过创建事件控制块来触发事件或等待事件。
事件间相互独立,内部实现为一个32位无符号整型,每一位标识一种事件类型。第25位不可用,因此最多可支持31种事件类型。
事件仅用于任务间的同步,不提供数据传输功能。
多次向事件控制块写入同一事件类型,在被清零前等效于只写入一次。
多个任务可以对同一事件进行读写操作。
支持事件读写超时机制。
再看事件图
注意图中提到了三个概念 事件控制块 事件 任务 接下来结合代码来理解事件模块的实现.
事件控制块长什么样?
typedef struct tagevent { uint32 uweventid; /**< event mask in the event control block,//标识发生的事件类型位,事件id,每一位标识一种事件类型 indicating the event that has been logically processed. */ los_dl_list steventlist; /** los_eventinit
//初始化一个事件控制块lite_os_sec_text_init uint32 los_eventinit(pevent_cb_s eventcb){ uint32 intsave; intsave = los_intlock();//锁中断 eventcb->uweventid = 0; //其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生) los_listinit(&eventcb->steventlist);//事件链表初始化 los_intrestore(intsave);//恢复中断 return los_ok;} 代码解读:
事件是共享资源,所以操作期间不能产生中断.
初始化两个记录者 uweventid steventlist
事件生产过程 -> oseventwrite
lite_os_sec_text void oseventwriteunsafe(pevent_cb_s eventcb, uint32 events, bool once, uint8 *exitflag){ lostaskcb *resumedtask = null; lostaskcb *nexttask = null; bool schedflag = false; eventcb->uweventid |= events;//对应位贴上标签 if (!los_listempty(&eventcb->steventlist)) {//等待事件链表判断,处理等待事件的任务 for (resumedtask = los_dl_list_entry((&eventcb->steventlist)->pstnext, lostaskcb, pendlist); &resumedtask->pendlist != &eventcb->steventlist;) {//循环获取任务链表 nexttask = los_dl_list_entry(resumedtask->pendlist.pstnext, lostaskcb, pendlist);//获取任务实体 if (oseventresume(resumedtask, eventcb, events)) {//是否恢复任务 schedflag = true;//任务已加至就绪队列,申请发生一次调度 } if (once == true) {//是否只处理一次任务 break;//退出循环 } resumedtask = nexttask;//检查链表中下一个任务 } } if ((exitflag != null) && (schedflag == true)) {//是否让外面调度 *exitflag = 1; }}//写入事件lite_os_sec_text static uint32 oseventwrite(pevent_cb_s eventcb, uint32 events, bool once){ uint32 intsave; uint8 exitflag = 0; scheduler_lock(intsave); //禁止调度 oseventwriteunsafe(eventcb, events, once, &exitflag);//写入事件 scheduler_unlock(intsave); //允许调度 if (exitflag == 1) { //需要发生调度 los_mpschedule(os_mp_cpu_all);//通知所有cpu调度 los_schedule();//执行调度 } return los_ok;} 代码解读:
给对应位贴上事件标签,eventcb->uweventid |= events; 注意uweventid是按位管理的.每个位代表一个事件是否写入,例如 uweventid = 00010010 代表产生了 1,4 事件
循环从steventlist链表中取出等待这个事件的任务判断是否唤醒任务. oseventresume
//事件恢复,判断是否唤醒任务lite_os_sec_text static uint8 oseventresume(lostaskcb *resumedtask, const pevent_cb_s eventcb, uint32 events){ uint8 exitflag = 0;//是否唤醒 if (((resumedtask->eventmode & los_waitmode_or) && ((resumedtask->eventmask & events) != 0)) || ((resumedtask->eventmode & los_waitmode_and) && ((resumedtask->eventmask & eventcb->uweventid) == resumedtask->eventmask))) {//逻辑与 和 逻辑或 的处理 exitflag = 1; resumedtask->taskevent = null; ostaskwake(resumedtask);//唤醒任务,加入就绪队列 } return exitflag;} 3.唤醒任务ostaskwake只是将任务重新加入就绪队列,需要立即申请一次调度 los_schedule .
事件消费过程 -> oseventread
lite_os_sec_text static uint32 oseventread(pevent_cb_s eventcb, uint32 eventmask, uint32 mode, uint32 timeout, bool once){ uint32 ret; uint32 intsave; scheduler_lock(intsave); ret = oseventreadimp(eventcb, eventmask, mode, timeout, once);//读事件实现函数 scheduler_unlock(intsave); return ret;}//读取指定事件类型的实现函数,超时时间为相对时间:单位为ticklite_os_sec_text static uint32 oseventreadimp(pevent_cb_s eventcb, uint32 eventmask, uint32 mode, uint32 timeout, bool once){ uint32 ret = 0; lostaskcb *runtask = oscurrtaskget(); runtask->eventmask = eventmask; runtask->eventmode = mode; runtask->taskevent = eventcb;//事件控制块 ret = ostaskwait(&eventcb->steventlist, timeout, true);//任务进入等待状态,挂入阻塞链表 if (ret == los_errno_tsk_timeout) {//如果返回超时 runtask->taskevent = null; return los_errno_event_read_timeout; } ret = oseventpoll(&eventcb->uweventid, eventmask, mode);//检测事件是否符合预期 return ret;} 代码解读:
事件控制块是给任务使用的, 任务给出读取一个事件的条件
eventmask 告诉系统屏蔽掉这些事件,对屏蔽的事件不感冒.
eventmode 已什么样的方式去消费事件,是必须都满足给的条件,还是只满足一个就响应.
条件给完后,自己进入等待状态 ostaskwait,等待多久 timeout决定,任务自己说了算.
oseventpoll检测事件是否符合预期,啥意思?看下它的代码就知道了
//根据用户传入的事件值、事件掩码及校验模式,返回用户传入的事件是否符合预期lite_os_sec_text uint32 oseventpoll(uint32 *eventid, uint32 eventmask, uint32 mode){ uint32 ret = 0;//事件是否发生了 los_assert(osintlocked());//断言不允许中断了 los_assert(los_spinheld(&g_taskspin));//任务自旋锁 if (mode & los_waitmode_or) {//如果模式是读取掩码中任意事件 if ((*eventid & eventmask) != 0) { ret = *eventid & eventmask; //发生了 } } else {//等待全部事件发生 if ((eventmask != 0) && (eventmask == (*eventid & eventmask))) {//必须满足全部事件发生 ret = *eventid & eventmask; //发生了 } } if (ret && (mode & los_waitmode_clr)) {//是否清除事件 *eventid = *eventid & ~ret; } return ret; } 编程实例
本实例实现如下流程。
示例中,任务example_taskentry创建一个任务example_event,example_event读事件阻塞,example_taskentry向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
在任务example_taskentry创建任务example_event,其中任务example_event优先级高于example_taskentry。
在任务example_event中读事件0x00000001,阻塞,发生任务切换,执行任务example_taskentry。
在任务example_taskentry向任务example_event写事件0x00000001,发生任务切换,执行任务example_event。
example_event得以执行,直到任务结束。
example_taskentry得以执行,直到任务结束。
#include los_event.h#include los_task.h#include securec.h/* 任务id */uint32 g_testtaskid;/* 事件控制结构体 */event_cb_s g_exampleevent;/* 等待的事件类型 */#define event_wait 0x00000001/* 用例任务入口函数 */void example_event(void){ uint32 ret; uint32 event; /* 超时等待方式读事件,超时时间为100 ticks, 若100 ticks后未读取到指定事件,读事件超时,任务直接唤醒 */ printf(example_event wait event 0x%x \n, event_wait); event = los_eventread(&g_exampleevent, event_wait, los_waitmode_and, 100); if (event == event_wait) { printf(example_event,read event :0x%x\n, event); } else { printf(example_event,read event timeout\n); }}uint32 example_taskentry(void){ uint32 ret; tsk_init_param_s task1; /* 事件初始化 */ ret = los_eventinit(&g_exampleevent); if (ret != los_ok) { printf(init event failed .\n); return -1; } /* 创建任务 */ (void)memset_s(&task1, sizeof(tsk_init_param_s), 0, sizeof(tsk_init_param_s)); task1.pfntaskentry = (tsk_entry_func)example_event; task1.pcname = eventtsk1; task1.uwstacksize = os_tsk_default_stack_size; task1.ustaskprio = 5; ret = los_taskcreate(&g_testtaskid, &task1); if (ret != los_ok) { printf(task create failed .\n); return los_nok; } /* 写g_testtaskid 等待事件 */ printf(example_taskentry write event .\n); ret = los_eventwrite(&g_exampleevent, event_wait); if (ret != los_ok) { printf(event write failed .\n); return los_nok; } /* 清标志位 */ printf(eventmask:%d\n, g_exampleevent.uweventid); los_eventclear(&g_exampleevent, ~g_exampleevent.uweventid); printf(eventmask:%d\n, g_exampleevent.uweventid); /* 删除任务 */ ret = los_taskdelete(g_testtaskid); if (ret != los_ok) { printf(task delete failed .\n); return los_nok; } return los_ok;} 运行结果


美国海军计划在2021年将MQ-8C无人机部署到濒海战斗舰上
多通道恒流标准源技术参数介绍
PCB丝印如何摆?请查收这份手册!
2011中国电脑市场TOP15品牌出炉
易思维获得了特斯拉新全球生产基地的在线测量设备订单
鸿蒙内核源码分析多任务环境下的事件控制块
小米全面屏电视全系开售 五大主流爆品款款低价
指纹门禁锁结构组成
面向初学者的11个最佳树莓派项目介绍
芯片等离子活化原理 提高化学惰性、抗腐蚀性、耐磨性
飞纳公司承接了来自包括华为在内的几大供应商的亿元订单
深圳VR厂商月出货3000万 狼吃狼时代利润低至1毛
四创电子北京分公司揭牌仪式在京举行
Waymo无人车为避开左转故意绕路
VK16K33内存映射和多功能LED控制器驱动程序
安森美推出双通道同步整流控制器NCP4318
新能源汽车补贴降幅,微型电动车难以存活
同步降压型转换器LTC3388性能分析
Vishay推出高速红外发射器,亮度提高30 %,且可消除多余的侧向辐照光
印制板设计标准