12345下一页
返回列表 发新帖我要提问本帖赏金: 3.00元(功能说明)

事件驱动构架

[复制链接]
19825|98
 楼主| Simon21ic 发表于 2015-8-14 12:34 | 显示全部楼层 |阅读模式
本帖最后由 Simon21ic 于 2015-8-16 13:31 编辑

看来这个坛子里,还有人玩系统构架。我就来开开脑洞吧

大家是否有想过,从芯片的硬件角度,系统是如何运行的?
我的答案是,从硬件角度,系统都是事件驱动的,事件就是各种中断。那为什么不设计一个软件系统,完全由事件驱动?

系统模型:
系统只是响应各个中断,中断产生事件,发送给特定能够处理这个事件的模块,然后,模块可以发送多个事件给其他多个模块,直到所有产生的事件都被处理了,系统休眠。
main函数,可以认为是编译器产生的代码,自动处理了复位事件,初始化完运行环境后,调用main.c

在这个构架下,main函数就是这样的:
  1. int main(void)
  2. {
  3.         vsf_leave_critical();
  4.         vsfsm_init(&app.sm);
  5.         while (1)
  6.         {
  7.                 vsfsm_poll();
  8.                
  9.                 vsf_enter_critical();
  10.                 if (!vsfsm_get_event_pending())
  11.                 {
  12.                         // sleep, will also enable interrupt
  13.                         core_interfaces.core.sleep(SLEEP_WFI);
  14.                 }
  15.                 else
  16.                 {
  17.                         vsf_leave_critical();
  18.                 }
  19.         }
  20. }
vsf_leave_critical和vsf_enter_critical分别是开中断和关中断。系统启动了app的进程,然后就进入消息轮询,如果发现系统没有消息需要处理,那就休眠,等待中断唤醒。
  1. vsf_err_t vsfsm_poll(void)
  2. {
  3.         struct vsfsm_t *sm;
  4.         
  5.         while (vsfsm_evt_count)
  6.         {
  7.                 sm = vsfsm_evtq_head->sm;
  8.                 (vsfsm_evtq_head == &vsfsm_evtq[VSFSM_CFG_EVTQ_SIZE - 1]) ?
  9.                         vsfsm_evtq_head = &vsfsm_evtq[0] : vsfsm_evtq_head++;
  10.                 vsf_enter_critical();
  11.                 sm->evt_count--;
  12.                 vsfsm_evt_count--;
  13.                 vsf_leave_critical();
  14.                 vsfsm_dispatch_evt(sm, vsfsm_evtq_head->evt);
  15.         }
  16.         return VSFERR_NONE;
  17. }
消息轮询,也只是从事件队列里,拿出消息,然后发给对应的进程处理。

既然有人能够看懂,那就多讲一些,其实非常简单。
这里是USB设备端协议栈里的事件处理函数的一小部分:
  1. static struct vsfsm_state_t *
  2. vsfusbd_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
  3. {
  4.         struct vsfusbd_device_t *device =
  5.                                                                 container_of(sm, struct vsfusbd_device_t, sm);
  6.         vsf_err_t err = VSFERR_NONE;
  7.         
  8.         switch (evt)
  9.         {
  10.         case VSFSM_EVT_FINI:
  11.                 device->drv->fini();
  12.                 device->drv->disconnect();
  13.                 if (device->callback.fini != NULL)
  14.                 {
  15.                         device->callback.fini();
  16.                 }
  17.                 break;
  18.         case VSFSM_EVT_INIT:
  19.                 {

事件驱动,那么每个底层的任务,其实就仅仅是处理各种事件而已,事件可以具备一定的顺序,也可以是无顺序。
看到这里,熟悉状态机的朋友,应该觉得情切了吧?这里是TCPIP协议栈里的TCP处理状态机的一小部分代码:
  1. static void vsfip_tcp_socket_input(void *param, struct vsfip_buffer_t *buf)
  2. {
  3.         struct vsfip_socket_t *socket = (struct vsfip_socket_t *)param;
  4.         struct vsfip_tcppcb_t *pcb = (struct vsfip_tcppcb_t *)socket->pcb.protopcb;
  5.         struct vsfip_tcphead_t head = *(struct vsfip_tcphead_t *)buf->buf.buffer;
  6.         bool release_buffer = true;
  7.         
  8.         pcb->rwnd = head.window_size;
  9.         switch (pcb->state)
  10.         {
  11.         case VSFIP_TCPSTAT_INVALID:
  12.                 goto release_buf;
  13.         case VSFIP_TCPSTAT_CLOSED:
  14.                 vsfip_tcp_sendflags(socket, VSFIP_TCPFLAG_RST);
  15.                 goto release_buf;
  16.         case VSFIP_TCPSTAT_LISTEN:
  17.                 if ((VSFIP_TCPFLAG_SYN == head.flags) &&
  18.                         (socket->listener.accepted_num < socket->listener.backlog) &&
  19.                         !socket->listener.closing)
  20.                 {
都是switch结构,当然,类似的,用if结构也是一样的逻辑。
单单用switch的方式实现系统功能是否相当麻烦?在传统的面向过程的阻塞代码,要移植到事件驱动可是相当麻烦的。
还好,瑞典人发明了PT协程(后面成为PT线程),我在构架里,实现了完全事件驱动的PT协程,一个协程的代码,差不多是这样的:
  1. vsf_err_t XXX_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
  2. {
  3.         struct user_data_t *user_data = (struct user_data_t *)pt->user_data;
  4.         
  5.         vsfsm_pt_begin(pt);
  6.         
  7.         while (1)
  8.         {
  9.                 if (vsfsm_sem_pend(&user_data->sem, pt->sm))
  10.                 {
  11.                         vsfsm_pt_wfe(pt, user_data->sem.evt);
  12.                 }
  13.                
  14.                 user_data->timer.evt = EVT_TIMEOUT;
  15.                 vsftimer_register(&user_data->timer);
  16.                 vsfsm_pt_wfe(pt, EVT_TIMEOUT);
  17.                
  18.                 ....
  19.         }
  20.         
  21.         vsfsm_pt_end(pt);
  22.         return VSFERR_NONE;
  23. }
user_data是用户实现的相关的类的实例的指针。
PT协程,虽然看似是阻塞的,但是,实际运行是非阻塞方式的,也就是说,多个PT协程,是可以“同时”运行的。
vsfsm_sem_pend是等待信号量,如果信号量不是立即可以得到的话,那么当别人发出信号量后,本协程会收到对应的事件。
后面的vsfsm_pt_wfe就是wait for event,等待事件了,当然,这里不是死等,而是退出程序,等别人发送了事件了,在重新进入程序,从之前的退出点继续运行。
再后面注册的定时器,也是类似,等待超时事件。

既然这里用到了信号量(semaphore),那也简单说一下吧,信号量是操作系统里,用于线程间同步的工具。实质上,信号量的这种用法就是事件的缓冲。
按照上面的例子,如果这个线程用于处理某个请求,而请求的处理过程,需要一定的时间(比如这里等待超时)。
如果不使用信号量,而是用等待请求处理事件的话,那么就可能在后面等待超时的时候,收到这个请求处理事件,这个时候就会出问题(因为当时在等待超时事件,而非请求处理事件)。
所以,使用了信号量,先把请求处理事件缓冲在信号量里,等到处理线程可以处理下一个请求时,再去调用vsfsm_sem_pend获得信号(如果当时无法获得,就等待信号的获得事件)。

一个关键,事件处理过程,需要是非阻塞的,这点是系统实现多任务的关键。PT线程里,当需要阻塞的时候(比如等待超时),函数会退出,直到收到相应的事件后,再重新进入,从之前的退出点继续运行。
一些CPU占用过多的任务,也需要在适合的地方,主动释放CPU资源,让系统处理其他事件,处理完后,再回到自己的程序,继续运行。
甚至,可以在某些位置,判断事件队列里是否有事件要处理,有的话才释放CPU资源,没有就继续处理自己的任务。这是不是就是 共 产 主 义 的按需分配?

这里只是从使用角度,说明了一下我的系统,内核怎么实现的,大家可以想一下,cortexM等级的处理器,根据系统配置,内核部分占用300多 - 600多字节,应该也算是一个微内核了吧。
状态机和操作系统就不讲了






打赏榜单

21ic小喇叭 打赏了 3.00 元 2015-08-19

keer_zu 发表于 2015-8-14 13:23 | 显示全部楼层
继续啊
 楼主| Simon21ic 发表于 2015-8-14 14:11 | 显示全部楼层
讲完了,这个就是构架
事件如何处理就不讲了
 楼主| Simon21ic 发表于 2015-8-16 12:46 | 显示全部楼层
yyy71cj 发表于 2015-8-16 10:32
帖子需要加精,故事也需要继续……

我记得我引入消息驱动的时候,有人便提到状态机,然而再 ...

消息驱动的系统,几乎一定是用状态机实现的。这两个不是同一范畴内的东西
 楼主| Simon21ic 发表于 2015-8-16 13:33 | 显示全部楼层
突然发现论坛尽然屏蔽 共 产 主 义 ,这是要反了吗?
keer_zu 发表于 2015-8-17 10:54 | 显示全部楼层
yyy71cj 发表于 2015-8-17 06:57
乱说话的要用胶带封嘴的,切莫妄论国事,国事不是你的事;若是日寇来了,你就摊上事了……[em ...

浓浓北京风味
 楼主| Simon21ic 发表于 2015-8-17 14:16 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-17 14:55 编辑
yyy71cj 发表于 2015-8-17 06:54
消息驱动可以不拘一格,可以是去修改一个变量(可以称之为静态状态)、也可以去驱动一个事件(可以称之为行 ...

变量就可以认为是状态,比如,8位的变量,就是256个状态。
甚至一个IO口的高低电平,也可以是状态。

消息驱动确实可以很灵活,底层的实现的方式一般就是使用状态机的。我并不是说要把事件定义为状态,而是说,事件驱动的构架,基本上都是用状态机实现的。
你书里的实现方式,就是标准的状态机(if结构的),比如,用变量记录程序的运行状态。
 楼主| Simon21ic 发表于 2015-8-17 14:18 | 显示全部楼层
yyy71cj 发表于 2015-8-17 06:57
乱说话的要用胶带封嘴的,切莫妄论国事,国事不是你的事;若是日寇来了,你就摊上事了……[em ...

呵呵,对国事没任何兴趣,也可能过一段时间后就不是中国人了
只是这个屏蔽影响了我纯粹的技术讨论
susan133168 发表于 2015-8-17 14:32 | 显示全部楼层
keer_zu 发表于 2015-8-17 14:39 | 显示全部楼层
Simon21ic 发表于 2015-8-17 14:18
呵呵,对国事没任何兴趣,也可能过一段时间后就不是中国人了
只是这个屏蔽影响了我纯粹的技术讨论 ...

移民?
 楼主| Simon21ic 发表于 2015-8-17 14:56 | 显示全部楼层

谁知道呢。。。。
 楼主| Simon21ic 发表于 2015-8-17 15:00 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-17 15:04 编辑

构架上,可以看一下contiki的构架,很多实现方式和我的VSF类似,也是使用PT实现多任务。状态机的话,可以看一下QP的量子平台,使用的HSM层次状态机,在我的VSF里也可以支持。

我的构架里,底层是状态机构架,但是由于完全使用状态机开发的难度相对较高,所以引入了PT线程的方式,可以简化开发复杂性。我现在把以前的一些状态机轮询,使用PT来实现后,代码逻辑会简单很多,毕竟PT的开发思路,和以前的阻塞代码的方式类似。

当然,状态机并不仅仅是这种构架,很多应用代码,都是使用了状态机。
状态机的一个很大的作用,是可以把阻塞的代码,变成非阻塞的轮询代码。当然,轮询其实效率并不高,因为并不是每个时刻,所有任务都需要轮训的。如果再加上事件驱动的话,有事件需要处理的状态机才会被轮询,那效率就可以更高。
 楼主| Simon21ic 发表于 2015-8-17 16:41 | 显示全部楼层
这套构架,是我去年底开始开发的构架,我们自己的产品开发,目前已经完全基于这套构架来实现了。我就再说几点这套构架的优势和劣势。
优势:
1. 底层核心就是事件驱动
    不需要调度器,任务有事件要处理的话,自然就被调度。资源按照需求分配。
2. 使用PT来实现一些“阻塞”代码的开发,使得移植其他一些阻塞代码更加容易
    曾经移植linux上的一些TCPIP应用,比较容易就可以实现的。
3. 定义了芯片的接口层,可以在一定层度上实现应用代码和底层芯片无关
    之前甚至还测试过动态加载,代码编译一次,可以在同类处理器上运行。比如,按照CortexM0编译,可以在其他所有CorteXM的芯片上运行,而不需要重新编译。当然,对于不具备MMU的处理器,动态加载本身没有什么意义。
4. 完全面向对象的设计,方便应用开发,甚至也很容易支持动态分配

劣势:
1. 对开发人员的能力要求较高
    按照目前培训的结果,基本上6-8个人里,有一个能够掌握这种开发方式。当然,也可能和我自己的培训经验不够丰富有关。
2. 系统构架的一些本身的限制,比如协作式内核的限制,PT线程使用上的限制等等。
    当然,这些限制,并非不可解决。

我们也已经开发了不少应用了,还都没受到这些限制的影响,有一些应用,都是严格的硬实时,即使使用操作系统,任务切换的开销都会使得系统无法运行。VSF构架,是基于前后台系统的,这个可以具备的实时性,也不是操作系统可以比拟的。
我们也开发了通用的USB主从机协议栈,通用的TCPIP协议栈,可以说谁用谁知道。甚至有兼职人员,希望得到授权,用于他们自己公司里的产品开发。
当然,我以前说过,并没有打算推广这套构架,不过确实希望更多的人能够了解事件驱动用于嵌入式领域,我以后也可以更加容易找到兼职人员。
 楼主| Simon21ic 发表于 2015-8-17 22:47 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-17 22:55 编辑
yyy71cj 发表于 2015-8-17 20:34
软件的发展,中国人可以说是摸了几十年的石头,最后干脆就不摸了,走哪儿算哪儿,有淹死的, ...

这个技术本来就是公开的,源代码都发布了,只是国内不会有什么人用而已,我没有足够资源和精力去做推广

目前,能够带来利益的,是我做的产品,而不是这套构架
我并不认为,在国内这种监管下,通过做构架能够得到利益
你说的方式在欧美可能可行,可惜我们在中国

目前能够维护这个,只是因为我有不少项目,不然也支撑不起每年的开销
真的要弄一个团队维护和运营的话,估计我早就倒闭了,很多东西,在实际运行的时候,并不是这么简单
不知道会不会有哪个大佬收购我们,再投入更多的资源,我们可以做物联网OS哦,只是没有资本拿到那些物联网平台的协议
 楼主| Simon21ic 发表于 2015-8-18 12:16 | 显示全部楼层
yyy71cj 发表于 2015-8-18 07:58
原本,我以为一个拥有这多物质的人是一个同样拥有财富的人……我错了……
有一个“老板”,叫 ...

呵呵,这个就随便你怎么想了

物联网平台,会用到别人的服务器,别人为啥要公开?国内能赚钱的平台都不公开。
Google自己的也不公开吧?
还好现在免费云也不少,也不是没东西可以玩
wuhaiduo 发表于 2015-8-19 11:53 | 显示全部楼层
和我写的很像 基于事件和消息的协作式系统框架
那本魔法师的书我也买了   被表面现象迷惑了  只适合初学者
 楼主| Simon21ic 发表于 2015-8-19 17:57 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-19 17:59 编辑
yyy71cj 发表于 2015-8-19 16:25
从书中,你看到的表面现象是什么?

我觉得是,很多内容都是引导性的,一步一步引导读者了解这种思想,所以适合初学者
还有估计就是和实际应用结合不够

比如,多任务的话,可以让一个任务接受串口命令,收到后,激活另一个任务去处理
消息系统的话,可以做一个LED计数器,N个LED执行计数,定时器产生一个进位事件发给LED0,LED0进位的话,发送进位事件给LED1,以此类推。
 楼主| Simon21ic 发表于 2015-8-19 22:16 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-19 22:23 编辑
yyy71cj 发表于 2015-8-19 19:16
你的建议很好,如果有再版的机会,我会考虑加进一些理论化的东西。
当时写书的时候,由于这是 ...

我觉得他说的表面就是这个意思,很多内容都没有深入,只是介绍了这个思想。
其实,没有给你的书好评的人,我觉得几乎都是这样原因。你的读者和你其实并没有什么利益关系,没有必要故意黑你。
很多人,不只是用类似你书里的思想,甚至还自己实现了多任务的调度器,如果他们看你的书,估计也会是这个感觉。
反而我觉得你们认为不给好评的人都是自以为是,反倒显得你们不够大气。

我其实以前也想为我的构架写一本书,当然不是为了出版,写代码构架的书,除非成为经典,否则也赚不到什么钱,反而需要花费大量精力。
如果有这个书,给我兼职的人,可以更加容易自学,省掉我很多培训的事件。
不过每次要动手,才发现,状态机、操作系统、面向对象、事件驱动,每一个要能讲清楚,都需要花费大量的篇幅,最后实在没时间,只能放弃。


 楼主| Simon21ic 发表于 2015-8-20 09:12 | 显示全部楼层
本帖最后由 Simon21ic 于 2015-8-20 09:21 编辑
yyy71cj 发表于 2015-8-20 08:19
为了写这本书,我准备了5年,写了5年,改了5年,不过这5年都是同一个5年,也就是说,准备、写、 ...

在我看来,出书本来就是吃力不讨好的事,特别是我们行业的。既然选择出书了,肯定早有思想准备。

除非你的影响力巨大,否则,别人的想法你控制不了,你只能调整自己。
以前怀疑过我技术的人多了去了,没啥好计较的,又不靠别人赚钱,一笑了之就行。

说实话,在我看来,你们对别人不给好评就说自以为是,也不是什么很有水平的处事方式。
另外,编程没有正果,这个是一直在发展的东西。我说的事件驱动,在服务器领域,已经是很常用的东西了,在嵌入式环境下,也有不少系统是事件驱动的,比如蓝牙协议栈。只是大部分人开发,使用的比较少。以后肯定还会有更加有意思的构架。玩技术就是要一直去学习,一直去思考。
 楼主| Simon21ic 发表于 2015-8-20 09:29 | 显示全部楼层
既然说到书,我推荐一本《玩转微控制器多任务程序设计》,我的一个朋友写的第二本,据说年底出版。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

2597

帖子

104

粉丝
快速回复 在线客服 返回列表 返回顶部