1# 事件 2 3 4## 基本概念 5 6事件(Event)是一种任务间通信的机制,可用于任务间的同步。 7 8多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。 9 10- 一对多同步模型:一个任务等待多个事件的触发。可以是任意一个事件发生时唤醒任务处理事件,也可以是几个事件都发生后才唤醒任务处理事件。 11 12- 多对多同步模型:多个任务等待多个事件的触发。 13 14OpenHarmony LiteOS-A的事件模块提供的事件,具有如下特点: 15 16- 任务通过创建事件控制块来触发事件或等待事件。 17 18- 事件间相互独立,内部实现为一个32位无符号整型,每一位标识一种事件类型。(0表示该时间类型未发生,1表示该事件类型已经发生,一共31种事件类型,第25bit位(`0x02U << 24`)系统保留) 19 20- 事件仅用于任务间的同步,不提供数据传输功能。 21 22- 多次向事件控制块写入同一事件类型,在被清零前等效于只写入一次。 23 24- 多个任务可以对同一事件进行读写操作。 25 26- 支持事件读写超时机制。 27 28 29## 运行机制 30 31 32### 事件控制块 33 34 35``` 36/** 37 * 事件控制块数据结构 38 */ 39typedef struct tagEvent { 40 UINT32 uwEventID; /* 事件集合,表示已经处理(写入和清零)的事件集合 */ 41 LOS_DL_LIST stEventList; /* 等待特定事件的任务链表 */ 42} EVENT_CB_S, *PEVENT_CB_S; 43``` 44 45 46### 事件运作原理 47 48**事件初始化**:会创建一个事件控制块,该控制块维护一个已处理的事件集合,以及等待特定事件的任务链表。 49 50**写事件**:会向事件控制块写入指定的事件,事件控制块更新事件集合,并遍历任务链表,根据任务等待具体条件满足情况决定是否唤醒相关任务。 51 52**读事件**:如果读取的事件已存在时,会直接同步返回。其他情况会根据超时时间以及事件触发情况,来决定返回时机:等待的事件条件在超时时间耗尽之前到达,阻塞任务会被直接唤醒,否则超时时间耗尽该任务才会被唤醒。 53 54读事件条件满足与否取决于入参eventMask和mode,eventMask即需要关注的事件类型掩码。mode是具体处理方式,分以下三种情况: 55 56- LOS_WAITMODE_AND:逻辑与,基于接口传入的事件类型掩码eventMask,只有这些事件都已经发生才能读取成功,否则该任务将阻塞等待或者返回错误码。 57 58- LOS_WAITMODE_OR:逻辑或,基于接口传入的事件类型掩码eventMask,只要这些事件中有任一种事件发生就可以读取成功,否则该任务将阻塞等待或者返回错误码。 59 60- LOS_WAITMODE_CLR:这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR或 LOS_WAITMODE_OR | LOS_WAITMODE_CLR)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。 61 62**事件清零**:根据指定掩码,去对事件控制块的事件集合进行清零操作。当掩码为0时,表示将事件集合全部清零。当掩码为0xffff时,表示不清除任何事件,保持事件集合原状。 63 64**事件销毁**:销毁指定的事件控制块。 65 66 **图1** 小型系统事件运作原理图 67 68 ![zh-cn_image_0000001180952545](figures/zh-cn_image_0000001180952545.png) 69 70 71## 开发指导 72 73 74### 接口说明 75 76OpenHarmony LiteOS-A内核的事件模块提供下面几种功能。 77 78 **表1** 事件模块接口 79 80| 功能分类 | 接口描述 | 81| -------- | -------- | 82| 初始化事件 | LOS_EventInit:初始化一个事件控制块 | 83| 读/写事件 | - LOS_EventRead:读取指定事件类型,超时时间为相对时间:单位为Tick<br/>- LOS_EventWrite:写指定的事件类型 | 84| 清除事件 | LOS_EventClear:清除指定的事件类型 | 85| 校验事件掩码 | - LOS_EventPoll:根据用户传入的事件ID、事件掩码及读取模式,返回用户传入的事件是否符合预期<br/>- LOS_EventDestroy:销毁指定的事件控制块 | 86| 销毁事件 | LOS_EventDestroy:销毁指定的事件控制块 | 87 88 89### 开发流程 90 91事件的典型开发流程: 92 931. 初始化事件控制块 94 952. 阻塞读事件控制块 96 973. 写入相关事件 98 994. 阻塞任务被唤醒,读取事件并检查是否满足要求 100 1015. 处理事件控制块 102 1036. 事件控制块销毁 104 105> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 106> - 进行事件读写操作时,事件的第25bit(`0x02U << 24`)为保留bit位,不可以进行位设置。 107> 108> - 对同一事件反复写入,算作一次写入。 109 110 111## 编程实例 112 113 114### 实例描述 115 116示例中,任务Example_TaskEntry创建一个任务Example_Event,Example_Event读事件阻塞,Example_TaskEntry向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。 117 1181. 在任务Example_TaskEntry创建任务Example_Event,其中任务Example_Event优先级高于Example_TaskEntry。 119 1202. 在任务Example_Event中读事件0x00000001,阻塞,发生任务切换,执行任务Example_TaskEntry。 121 1223. 在任务Example_TaskEntry向任务Example_Event写事件0x00000001,发生任务切换,执行任务Example_Event。 123 1244. Example_Event得以执行,直到任务结束。 125 1265. Example_TaskEntry得以执行,直到任务结束。 127 128 129### 编程示例 130 131本演示代码在./kernel/liteos_a/testsuites/kernel/src/osTest.c中编译验证,在TestTaskEntry中调用验证入口函数Example_EventEntry。 132 133示例代码如下: 134 135``` 136#include "los_event.h" 137#include "los_task.h" 138#include "securec.h" 139 140/* 任务ID */ 141UINT32 g_testTaskId; 142 143/* 事件控制结构体 */ 144EVENT_CB_S g_exampleEvent; 145 146/* 等待的事件类型 */ 147#define EVENT_WAIT 0x00000001 148#define EVENT_TIMEOUT 500 149/* 用例任务入口函数 */ 150VOID Example_Event(VOID) 151{ 152 UINT32 event; 153 154 /* 超时等待方式读事件,超时时间为100 ticks, 若100 ticks后未读取到指定事件,读事件超时,任务直接唤醒 */ 155 dprintf("Example_Event wait event 0x%x \n", EVENT_WAIT); 156 157 event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, EVENT_TIMEOUT); 158 if (event == EVENT_WAIT) { 159 dprintf("Example_Event,read event :0x%x\n", event); 160 } else { 161 dprintf("Example_Event,read event timeout\n"); 162 } 163} 164 165UINT32 Example_EventEntry(VOID) 166{ 167 UINT32 ret; 168 TSK_INIT_PARAM_S task1; 169 170 /* 事件初始化 */ 171 ret = LOS_EventInit(&g_exampleEvent); 172 if (ret != LOS_OK) { 173 dprintf("init event failed .\n"); 174 return -1; 175 } 176 177 /* 创建任务 */ 178 (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); 179 task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event; 180 task1.pcName = "EventTsk1"; 181 task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; 182 task1.usTaskPrio = 5; 183 ret = LOS_TaskCreate(&g_testTaskId, &task1); 184 if (ret != LOS_OK) { 185 dprintf("task create failed.\n"); 186 return LOS_NOK; 187 } 188 189 /* 写g_testTaskId 等待事件 */ 190 dprintf("Example_TaskEntry write event.\n"); 191 192 ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT); 193 if (ret != LOS_OK) { 194 dprintf("event write failed.\n"); 195 return LOS_NOK; 196 } 197 198 /* 清标志位 */ 199 dprintf("EventMask:%d\n", g_exampleEvent.uwEventID); 200 LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID); 201 dprintf("EventMask:%d\n", g_exampleEvent.uwEventID); 202 203 return LOS_OK; 204} 205``` 206 207 208### 结果验证 209 210编译运行得到的结果为: 211 212 213``` 214Example_Event wait event 0x1 215Example_TaskEntry write event. 216Example_Event,read event :0x1 217EventMask:1 218EventMask:0 219``` 220