嵌入式实时操作系统原理与创新实践 课件 第六章-邮箱与事件_第1页
嵌入式实时操作系统原理与创新实践 课件 第六章-邮箱与事件_第2页
嵌入式实时操作系统原理与创新实践 课件 第六章-邮箱与事件_第3页
嵌入式实时操作系统原理与创新实践 课件 第六章-邮箱与事件_第4页
嵌入式实时操作系统原理与创新实践 课件 第六章-邮箱与事件_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

RT-Thread多事件同步与轻量级消息传递第6章邮箱与事件1、邮箱与事件基础2、事件机制与源码3、邮箱机制与源码4、实验、小结与思考目录CONTENTS事件邮箱实验回顾概念基础邮箱与事件的作用03概念基础两类IPC解决的问题不同:事件组合条件,邮箱传递一个机器字。01事件用32位事件集表达最多32个条件,适合一对多和多对多同步02邮箱每封邮件固定为4字节,通常直接传数值或指针03共同点都继承rt_ipc_object,可按FIFO或优先级组织等待线程04中断场景都可用于中断与线程通信,但中断内不能执行延时等待事件邮箱实验回顾概念基础事件与邮箱的差异04概念基础事件集传递的是“哪些条件已经发生”接收方声明感兴趣的位可选择AND、OR和CLEAR邮箱传递的是一个32位值收发双方通过邮件池解耦可用指针承载可变长度数据事件适合同步条件,邮箱适合轻量级数据传递。事件邮箱实验回顾概念基础32位事件集05概念基础rt_uint32_tset;EVENT_0=1U<<0;EVENT_4=1U<<4;EVENT_5=1U<<5;01每一位表示一个独立事件021表示该事件已经发生03多个事件通过按位或组成掩码04接收条件通过按位与进行判断事件编号n对应1U<<n;事件集最多同时记录32个事件。事件邮箱实验回顾概念基础AND与OR条件06概念基础RT_EVENT_FLAG_AND全部目标位都置1才满足判断式:(event->set&set)==setRT_EVENT_FLAG_OR任意一个目标位置1即满足判断式:(event->set&set)!=0AND等待条件齐备,OR响应任一可用来源。事件邮箱实验回顾概念基础电阻计算中的AND同步07概念基础01电流采样ADC完成后发送事件1U<<402电压采样ADC完成后发送事件1U<<503计算线程等待(1U<<4)|(1U<<5)04触发条件使用AND,两个采样结果都准备好后计算电阻05重新采样配合CLEAR清除已消费的事件位事件邮箱实验回顾概念基础双传感器中的OR同步08概念基础主传感器和备用传感器都可提供电压值。01主传感器完成采样后置位EVENT_PRIMARY02备用传感器完成采样后置位EVENT_BACKUP03接收线程使用RT_EVENT_FLAG_OR04任一来源先完成,处理线程即可继续运行事件邮箱实验回顾概念基础邮箱与消息队列09概念基础01消息大小邮箱固定4字节;消息队列可在初始化时指定消息长度02存储结构邮箱使用rt_ubase_t数组;消息队列维护格式化消息块03访问方式邮箱通过in_offset和out_offset直接访问数组04适用数据邮箱适合数值、句柄和指针;消息队列适合复制完整消息05主要优势邮箱控制开销小,内存占用低,收发路径更直接事件邮箱实验回顾概念基础邮箱的收发状态10概念基础01可发送entry<size,将value写入msg_pool[in_offset]02邮箱已满发送线程可立即失败,也可进入suspend_sender_thread等待03可接收entry>0,从msg_pool[out_offset]取出邮件04邮箱为空接收线程可立即失败,也可进入suspend_thread等待05状态更新收发后更新entry,并让偏移量在数组末尾回到0事件邮箱实验回顾概念基础中断上下文的使用限制11概念基础中断可以发送事件或邮件,但不能阻塞等待。01发送操作应选择不会等待的接口或参数02事件接收和邮箱接收应在线程上下文执行03中断内阻塞会延长中断响应,严重时使系统卡死04复杂数据可先记录到缓冲区,再用事件或邮箱通知线程事件邮箱实验回顾概念基础事件控制块12事件structrt_event{structrt_ipc_objectparent;rt_uint32_tset;};01parent:对象信息和suspend_thread02set:当前已经发生的事件位03发送操作通过按位或更新set04接收操作按option检查目标位事件邮箱实验回顾概念基础线程控制块中的事件信息13事件#ifdefined(RT_USING_EVENT)rt_uint32_tevent_set;rt_uint8_tevent_info;#endif01event_set:等待时保存目标事件组合,唤醒后保存实际触发位02event_info:保存AND、OR和CLEAR等接收选项03发送端遍历等待线程时,根据这两个字段判断是否唤醒事件邮箱实验回顾概念基础事件掩码示例14事件set=(1U<<1)|(1U<<3);set=0b00000000_00000000_00000000_00001010;set=10;位1和位3组成掩码10。AND要求两位都出现,OR只要求其中一位出现。事件邮箱实验回顾概念基础事件接收选项15事件01ANDRT_EVENT_FLAG_AND,目标事件必须全部发生02ORRT_EVENT_FLAG_OR,目标事件任意一个发生即可03CLEAR接收成功后清除本次消费的事件位04组合方式用按位或把AND或OR与CLEAR组合05基本约束AND与OR必须选择一个,set不能为0事件邮箱实验回顾概念基础rt_event_init()接口16事件rt_err_trt_event_init(rt_event_tevent,constchar*name,rt_uint8_tflag);参数含义event事件集控制块指针name用户定义的事件集名称flag等待线程排序方式:RT_IPC_FLAG_FIFO或RT_IPC_FLAG_PRIO静态对象由调用者提供控制块,初始化成功返回RT_EOK。事件邮箱实验回顾概念基础rt_event_init():对象与等待策略17事件RT_ASSERT(event!=RT_NULL);rt_object_init(&event->parent.parent,RT_Object_Class_Event,name);event->parent.parent.flag=flag;01检查控制块指针02登记对象类型和名称03保存FIFO或优先级排序方式04后续挂起线程按flag插入链表事件邮箱实验回顾概念基础rt_event_init():IPC与事件集18事件rt_ipc_object_init(&event->parent);event->set=0;returnRT_EOK;01初始化suspend_thread链表02set清零表示当前没有事件03初始状态下所有接收条件都不满足04发送事件后才会更新set并检查等待线程事件邮箱实验回顾概念基础事件对象的创建与删除19事件01rt_event_create动态申请控制块并完成与init相同的初始化02rt_event_delete删除动态事件对象并释放控制块内存03rt_event_detach关闭静态事件对象,不释放调用者提供的内存04生命周期静态对象使用detach,动态对象使用delete事件邮箱实验回顾概念基础rt_event_recv()参数20事件参数含义event事件集控制块指针set线程感兴趣的事件组合option选择AND或OR,可按位或加入RT_EVENT_FLAG_CLEARtimeout0为不等待,正数为限时等待,负数为永久等待recved非空时返回实际触发的事件位;不需要时传RT_NULL事件邮箱实验回顾概念基础rt_event_recv():参数与初始状态21事件RT_ASSERT(event!=RT_NULL);RT_ASSERT(rt_object_get_type(&event->parent.parent)==RT_Object_Class_Event);if(set==0)return-RT_ERROR;status=-RT_ERROR;thread=rt_thread_self();thread->error=RT_EOK;01控制块必须有效且类型正确02set为0表示不关心任何事件,直接报错03status默认表示条件不满足04清除当前线程的旧错误码事件邮箱实验回顾概念基础rt_event_recv():AND判断22事件if(option&RT_EVENT_FLAG_AND){if((event->set&set)==set)status=RT_EOK;}01event->set保存已经发生的事件02set保存线程要求的事件03按位与保留两者同时为1的位04结果等于set,说明目标事件全部发生事件邮箱实验回顾概念基础rt_event_recv():OR判断23事件elseif(option&RT_EVENT_FLAG_OR){if(event->set&set)status=RT_EOK;}else{RT_ASSERT(0);}01按位与结果非零,说明至少一个目标事件发生02OR路径只需要一个匹配位03option未指定AND或OR时触发断言04CLEAR是附加选项,不能代替AND或OR事件邮箱实验回顾概念基础rt_event_recv():接收成功24事件if(status==RT_EOK){if(recved)*recved=event->set&set;thread->event_set=event->set&set;thread->event_info=option;}01只返回目标掩码范围内已经触发的位02recved是调用者可选的输出参数03线程控制块也保存相同的触发结果04函数结束时返回线程错误码事件邮箱实验回顾概念基础rt_event_recv():CLEAR语义25事件if(option&RT_EVENT_FLAG_CLEAR)event->set&=~set;01按位取反后:目标位为0,其余位为102按位与只清除本次目标事件03未选择CLEAR时,事件位保持置位04保持置位可能让后续recv立即成功事件邮箱实验回顾概念基础rt_event_recv():不等待失败26事件elseif(timeout==0){thread->error=-RT_ETIMEOUT;rt_hw_interrupt_enable(level);return-RT_ETIMEOUT;}01当前事件集不满足条件02timeout为0,不允许挂起03恢复中断后立即返回04调用者根据-RT_ETIMEOUT选择后续处理事件邮箱实验回顾概念基础rt_event_recv():记录等待条件27事件thread->event_set=set;thread->event_info=option;rt_ipc_list_suspend(&event->parent.suspend_thread,thread,event->parent.parent.flag);01在线程控制块中保存掩码和选项02把当前线程挂入事件等待链表03链表排序由事件对象的flag决定04发送端稍后读取这些字段判断是否唤醒事件邮箱实验回顾概念基础rt_event_recv():定时与恢复28事件if(timeout>0){rt_timer_control(&thread->thread_timer,RT_TIMER_CTRL_SET_TIME,&timeout);rt_timer_start(&thread->thread_timer);}rt_hw_interrupt_enable(level);rt_schedule();if(thread->error!=RT_EOK)returnthread->error;01正数等待:启动定时器02永久等待不启动定时器03恢复中断后主动调度04线程被事件或超时唤醒05醒来后先检查error事件邮箱实验回顾概念基础recved与event_set29事件01接收成功recved和thread->event_set都保存实际触发的事件位02等待期间thread->event_set暂时保存线程感兴趣的目标掩码03发送唤醒OR路径会把thread->event_set缩小为实际匹配位04接口选择调用者需要直接读取结果时提供recved,否则传RT_NULL事件邮箱实验回顾概念基础rt_event_send()接口30事件rt_err_trt_event_send(rt_event_tevent,rt_uint32_tset);参数含义event事件集控制块指针set需要发送的一个或多个事件位set为0时返回-RT_ERROR;正常发送完成返回RT_EOK。事件邮箱实验回顾概念基础rt_event_send():记录事件31事件RT_ASSERT(event!=RT_NULL);if(set==0)return-RT_ERROR;need_schedule=RT_FALSE;level=rt_hw_interrupt_disable();event->set|=set;01拒绝空事件掩码02进入临界区保护事件集和等待链表03按位或把新事件并入现有set04先不调度,遍历结束后统一决定事件邮箱实验回顾概念基础rt_event_send():遍历等待链表32事件if(!rt_list_isempty(&event->parent.suspend_thread)){n=event->parent.suspend_thread.next;while(n!=&event->parent.suspend_thread){thread=rt_list_entry(n,structrt_thread,tlist);...}}01链表非空才进入扫描02表头不代表线程,因此从next开始03n回到表头表示遍历完成04rt_list_entry由节点地址取得线程控制块事件邮箱实验回顾概念基础rt_event_send():检查线程条件33事件status=-RT_ERROR;if(thread->event_info&RT_EVENT_FLAG_AND){if((thread->event_set&event->set)==thread->event_set)status=RT_EOK;}elseif(thread->event_info&RT_EVENT_FLAG_OR){if(thread->event_set&event->set)status=RT_EOK;}01每个等待线程拥有独立的event_set02AND检查全部目标位03OR检查任一目标位04一次发送可满足多个线程事件邮箱实验回顾概念基础rt_event_send():清除与唤醒34事件n=n->next;if(status==RT_EOK){if(thread->event_info&RT_EVENT_FLAG_CLEAR)event->set&=~thread->event_set;rt_thread_resume(thread);need_schedule=RT_TRUE;}01先保存下一个节点,避免恢复线程后破坏遍历位置02按线程选项清除已消费事件03把满足条件的线程恢复为就绪态04只设置调度标记,不在循环中反复调度事件邮箱实验回顾概念基础rt_event_send():统一调度35事件rt_hw_interrupt_enable(level);if(need_schedule==RT_TRUE)rt_schedule();returnRT_EOK;01完成整条等待链表的扫描02恢复中断,退出临界区03用need_schedule合并调度,避免扫描过程中反复切换04高优先级就绪线程可能立即获得CPU事件邮箱实验回顾概念基础rt_event_control()36事件rt_err_trt_event_control(rt_event_tevent,intcmd,void*arg);01当前主要用于RT_IPC_CMD_RESET02恢复suspend_thread中的等待线程03清空事件集set04重置后对象回到刚初始化的状态事件邮箱实验回顾概念基础事件机制的使用要点37事件01掩码唯一每个业务事件占用不同的位,避免事件含义冲突02条件明确接收方必须选择AND或OR,必要时加入CLEAR03清除策略不清除适合状态型条件,清除适合一次性通知04并发唤醒一次send会遍历全部等待线程,可能恢复多个线程05临界区事件集和链表操作都在关中断保护范围内完成事件邮箱实验回顾概念基础邮箱控制块38邮箱structrt_mailbox{structrt_ipc_objectparent;rt_ubase_t*msg_pool;rt_uint16_tsize;rt_uint16_tentry;rt_uint16_tin_offset;rt_uint16_tout_offset;rt_list_tsuspend_sender_thread;};邮箱管理邮件数组和环形索引,还要维护两条等待链表。事件邮箱实验回顾概念基础邮箱字段:存储状态39邮箱01msg_poolrt_ubase_t数组首地址,每个元素保存一封4字节邮件02size邮件池可容纳的总邮件数03entry当前有效邮件数量,范围为0到size04in_offset下一次发送写入的数组位置05out_offset下一次接收读取的数组位置事件邮箱实验回顾概念基础邮箱字段:等待线程40邮箱01suspend_thread继承自parent,保存等待接收邮件的线程02suspend_sender_thread保存邮箱已满时等待发送的线程03发送后若接收链表非空,恢复一个等待接收的线程04接收后若发送链表非空,恢复一个等待发送的线程05排序方式两条链表均依据邮箱对象的flag组织线程事件邮箱实验回顾概念基础邮件池的环形索引41邮箱mb->msg_pool[mb->in_offset]=value;if(++mb->in_offset>=mb->size)mb->in_offset=0;*value=mb->msg_pool[mb->out_offset];if(++mb->out_offset>=mb->size)mb->out_offset=0;01发送端只移动in_offset02接收端只移动out_offset03到达数组末尾后回到004entry区分邮箱为空和邮箱已满事件邮箱实验回顾概念基础rt_mb_init()参数42邮箱参数含义mb邮箱控制块指针name用户定义的邮箱名称msg_pool邮件池首地址size邮件池可容纳的邮件数flag等待线程排序方式:FIFO或优先级顺序事件邮箱实验回顾概念基础rt_mb_init():对象与IPC43邮箱RT_ASSERT(mb!=RT_NULL);rt_object_init(&mb->parent.parent,RT_Object_Class_MailBox,name);mb->parent.parent.flag=flag;rt_ipc_object_init(&mb->parent);01检查静态控制块02登记邮箱对象类型和名称03保存等待线程的排序方式04初始化接收线程挂起链表事件邮箱实验回顾概念基础rt_mb_init():邮件池与索引44邮箱mb->msg_pool=(rt_ubase_t*)msgpool;mb->size=size;mb->entry=0;mb->in_offset=0;mb->out_offset=0;rt_list_init(&mb->suspend_sender_thread);01绑定调用者提供的邮件池02记录容量并清空有效邮件数03收发偏移量都从0开始04单独初始化发送线程挂起链表事件邮箱实验回顾概念基础邮箱的创建与删除45邮箱01rt_mb_create动态申请邮箱控制块和邮件池,并完成初始化02rt_mb_detach关闭静态邮箱对象,内存仍由调用者管理03rt_mb_delete删除动态邮箱,同时释放控制块和邮件池04容量参数动态创建时size表示邮件个数,每封固定为一个rt_ubase_t事件邮箱实验回顾概念基础rt_mb_send_wait()参数46邮箱rt_err_trt_mb_send_wait(rt_mailbox_tmb,rt_ubase_tvalue,rt_int32_ttimeout);参数含义mb目标邮箱控制块指针value32位邮件内容,可直接传数值或指针timeout0不等待,正数限时等待,负数永久等待成功返回RT_EOK;邮箱满且不等待时返回-RT_EFULL。事件邮箱实验回顾概念基础rt_mb_send_wait():邮箱已满47邮箱temp=rt_hw_interrupt_disable();if(mb->entry==mb->size&&timeout==0){rt_hw_interrupt_enable(temp);return-RT_EFULL;}while(mb->entry==mb->size){thread->error=RT_EOK;if(timeout==0)return-RT_EFULL;...}01关中断后检查entry和size02不等待调用立即失败03等待调用进入while循环04线程每次恢复后重新检查邮箱是否仍满事件邮箱实验回顾概念基础rt_mb_send_wait():挂起与定时48邮箱rt_ipc_list_suspend(&mb->suspend_sender_thread,thread,mb->parent.parent.flag);if(timeout>0){tick_delta=rt_tick_get();rt_timer_control(&thread->thread_timer,RT_TIMER_CTRL_SET_TIME,&timeout);rt_timer_start(&thread->thread_timer);}rt_hw_interrupt_enable(temp);rt_schedule();01发送线程进入专用挂起链表02限时等待记录起始tick03启动线程内置定时器04恢复中断并让出CPU事件邮箱实验回顾概念基础rt_mb_send_wait():剩余超时49邮箱if(thread->error!=RT_EOK)returnthread->error;temp=rt_hw_interrupt_disable();if(timeout>0){tick_delta=rt_tick_get()-tick_delta;timeout-=tick_delta;if(timeout<0)timeout=0;}01线程可能因接收方释放空间而提前恢复02恢复后先检查超时或其他错误03重新进入临界区04扣除已经消耗的tick,继续while检查事件邮箱实验回顾概念基础rt_mb_send_wait():写入邮件50邮箱mb->msg_pool[mb->in_offset]=value;++mb->in_offset;if(mb->in_offset>=mb->size)mb->in_offset=0;01value写入当前in_offset02写入成功后输入偏移量加一03到达size时回绕到004先写入数据,再增加有效邮件数事件邮箱实验回顾概念基础rt_mb_send_wait():entry与接收者51邮箱if(mb->entry<RT_MB_ENTRY_MAX)mb->entry++;elsereturn-RT_EFULL;if(!rt_list_isempty(&mb->parent.suspend_thread)){rt_ipc_list_resume(&mb->parent.suspend_thread);rt_hw_interrupt_enable(temp);rt_schedule();returnRT_EOK;}01entry记录有效邮件数02防止计数超过字段上限03先增加entry再唤醒接收者,保证接收方看到完整状态04恢复中断后立即调度一次事件邮箱实验回顾概念基础rt_mb_send()52邮箱rt_err_trt_mb_send(rt_mailbox_tmb,rt_ubase_tvalue){returnrt_mb_send_wait(mb,value,0);}参数含义mb目标邮箱控制块指针value32位邮件内容rt_mb_send()是rt_mb_send_wait()的不等待封装。事件邮箱实验回顾概念基础rt_mb_recv()参数53邮箱rt_err_trt_mb_recv(rt_mailbox_tmb,rt_ubase_t*value,rt_int32_ttimeout);参数含义mb目标邮箱控制块指针value接收32位邮件内容的地址timeout0不等待,正数限时等待,负数永久等待成功接收返回RT_EOK;未等到邮件返回-RT_ETIMEOUT。事件邮箱实验回顾概念基础rt_mb_recv():邮箱为空54邮箱if(mb->entry==0&&timeout==0)return-RT_ETIMEOUT;while(mb->entry==0){thread->error=RT_EOK;rt_ipc_list_suspend(&mb->parent.suspend_thread,thread,mb->parent.parent.flag);//限时等待时启动定时器rt_schedule();//恢复后检查错误并更新timeout}01不等待调用直接返回超时02等待调用进入while循环03接收线程挂入suspend_thread04唤醒后重新确认entry是否非零事件邮箱实验回顾概念基础rt_mb_recv():取出邮件55邮箱*value=mb->msg_pool[mb->out_offset];++mb->out_offset;if(mb->out_offset>=mb->size)mb->out_offset=0;if(mb->entry>0)mb->entry--;01从当前out_offset复制邮件02输出偏移量前移并在末尾回绕03有效邮件数减一04取出后产生一个新的空闲邮件位置事件邮箱实验回顾概念基础rt_mb_recv():恢复发送者56邮箱if(!rt_list_isempty(&mb->suspend_sender_thread)){rt_ipc_list_resume(&mb->suspend_sender_thread);rt_hw_interrupt_enable(temp);rt_schedule();returnRT_EOK;}01接收操作释放了一个邮件槽位02从发送等待链表恢复一个线程03恢复中断后执行调度04被唤醒发送者重新检查邮箱是否仍满事件邮箱实验回顾概念基础用指针传递可变长度数据57邮箱structm

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论