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

事件驱动构架

[复制链接]
14008|98
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Simon21ic|  楼主 | 2015-8-14 12:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Simon21ic 于 2015-8-16 13:31 编辑

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

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

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

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

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

事件驱动,那么每个底层的任务,其实就仅仅是处理各种事件而已,事件可以具备一定的顺序,也可以是无顺序。
看到这里,熟悉状态机的朋友,应该觉得情切了吧?这里是TCPIP协议栈里的TCP处理状态机的一小部分代码:
static void vsfip_tcp_socket_input(void *param, struct vsfip_buffer_t *buf)
{
        struct vsfip_socket_t *socket = (struct vsfip_socket_t *)param;
        struct vsfip_tcppcb_t *pcb = (struct vsfip_tcppcb_t *)socket->pcb.protopcb;
        struct vsfip_tcphead_t head = *(struct vsfip_tcphead_t *)buf->buf.buffer;
        bool release_buffer = true;
        
        pcb->rwnd = head.window_size;
        switch (pcb->state)
        {
        case VSFIP_TCPSTAT_INVALID:
                goto release_buf;
        case VSFIP_TCPSTAT_CLOSED:
                vsfip_tcp_sendflags(socket, VSFIP_TCPFLAG_RST);
                goto release_buf;
        case VSFIP_TCPSTAT_LISTEN:
                if ((VSFIP_TCPFLAG_SYN == head.flags) &&
                        (socket->listener.accepted_num < socket->listener.backlog) &&
                        !socket->listener.closing)
                {
都是switch结构,当然,类似的,用if结构也是一样的逻辑。
单单用switch的方式实现系统功能是否相当麻烦?在传统的面向过程的阻塞代码,要移植到事件驱动可是相当麻烦的。
还好,瑞典人发明了PT协程(后面成为PT线程),我在构架里,实现了完全事件驱动的PT协程,一个协程的代码,差不多是这样的:
vsf_err_t XXX_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
        struct user_data_t *user_data = (struct user_data_t *)pt->user_data;
        
        vsfsm_pt_begin(pt);
        
        while (1)
        {
                if (vsfsm_sem_pend(&user_data->sem, pt->sm))
                {
                        vsfsm_pt_wfe(pt, user_data->sem.evt);
                }
               
                user_data->timer.evt = EVT_TIMEOUT;
                vsftimer_register(&user_data->timer);
                vsfsm_pt_wfe(pt, EVT_TIMEOUT);
               
                ....
        }
        
        vsfsm_pt_end(pt);
        return VSFERR_NONE;
}
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
帖子需要加精,故事也需要继续……

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

消息驱动的系统,几乎一定是用状态机实现的。这两个不是同一范畴内的东西

使用特权

评论回复
5
Simon21ic|  楼主 | 2015-8-16 13:33 | 只看该作者
突然发现论坛尽然屏蔽 共 产 主 义 ,这是要反了吗?

使用特权

评论回复
6
keer_zu| | 2015-8-17 10:54 | 只看该作者
yyy71cj 发表于 2015-8-17 06:57
乱说话的要用胶带封嘴的,切莫妄论国事,国事不是你的事;若是日寇来了,你就摊上事了……[em ...

浓浓北京风味

使用特权

评论回复
7
Simon21ic|  楼主 | 2015-8-17 14:16 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-17 14:55 编辑
yyy71cj 发表于 2015-8-17 06:54
消息驱动可以不拘一格,可以是去修改一个变量(可以称之为静态状态)、也可以去驱动一个事件(可以称之为行 ...

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

消息驱动确实可以很灵活,底层的实现的方式一般就是使用状态机的。我并不是说要把事件定义为状态,而是说,事件驱动的构架,基本上都是用状态机实现的。
你书里的实现方式,就是标准的状态机(if结构的),比如,用变量记录程序的运行状态。

使用特权

评论回复
8
Simon21ic|  楼主 | 2015-8-17 14:18 | 只看该作者
yyy71cj 发表于 2015-8-17 06:57
乱说话的要用胶带封嘴的,切莫妄论国事,国事不是你的事;若是日寇来了,你就摊上事了……[em ...

呵呵,对国事没任何兴趣,也可能过一段时间后就不是中国人了
只是这个屏蔽影响了我纯粹的技术讨论

使用特权

评论回复
9
susan133168| | 2015-8-17 14:32 | 只看该作者

使用特权

评论回复
10
keer_zu| | 2015-8-17 14:39 | 只看该作者
Simon21ic 发表于 2015-8-17 14:18
呵呵,对国事没任何兴趣,也可能过一段时间后就不是中国人了
只是这个屏蔽影响了我纯粹的技术讨论 ...

移民?

使用特权

评论回复
11
Simon21ic|  楼主 | 2015-8-17 14:56 | 只看该作者

谁知道呢。。。。

使用特权

评论回复
12
Simon21ic|  楼主 | 2015-8-17 15:00 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-17 15:04 编辑

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

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

当然,状态机并不仅仅是这种构架,很多应用代码,都是使用了状态机。
状态机的一个很大的作用,是可以把阻塞的代码,变成非阻塞的轮询代码。当然,轮询其实效率并不高,因为并不是每个时刻,所有任务都需要轮训的。如果再加上事件驱动的话,有事件需要处理的状态机才会被轮询,那效率就可以更高。

使用特权

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

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

我们也已经开发了不少应用了,还都没受到这些限制的影响,有一些应用,都是严格的硬实时,即使使用操作系统,任务切换的开销都会使得系统无法运行。VSF构架,是基于前后台系统的,这个可以具备的实时性,也不是操作系统可以比拟的。
我们也开发了通用的USB主从机协议栈,通用的TCPIP协议栈,可以说谁用谁知道。甚至有兼职人员,希望得到授权,用于他们自己公司里的产品开发。
当然,我以前说过,并没有打算推广这套构架,不过确实希望更多的人能够了解事件驱动用于嵌入式领域,我以后也可以更加容易找到兼职人员。

使用特权

评论回复
14
Simon21ic|  楼主 | 2015-8-17 22:47 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-17 22:55 编辑
yyy71cj 发表于 2015-8-17 20:34
软件的发展,中国人可以说是摸了几十年的石头,最后干脆就不摸了,走哪儿算哪儿,有淹死的, ...

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

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

目前能够维护这个,只是因为我有不少项目,不然也支撑不起每年的开销
真的要弄一个团队维护和运营的话,估计我早就倒闭了,很多东西,在实际运行的时候,并不是这么简单
不知道会不会有哪个大佬收购我们,再投入更多的资源,我们可以做物联网OS哦,只是没有资本拿到那些物联网平台的协议

使用特权

评论回复
15
Simon21ic|  楼主 | 2015-8-18 12:16 | 只看该作者
yyy71cj 发表于 2015-8-18 07:58
原本,我以为一个拥有这多物质的人是一个同样拥有财富的人……我错了……
有一个“老板”,叫 ...

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

物联网平台,会用到别人的服务器,别人为啥要公开?国内能赚钱的平台都不公开。
Google自己的也不公开吧?
还好现在免费云也不少,也不是没东西可以玩

使用特权

评论回复
16
wuhaiduo| | 2015-8-19 11:53 | 只看该作者
和我写的很像 基于事件和消息的协作式系统框架
那本魔法师的书我也买了   被表面现象迷惑了  只适合初学者

使用特权

评论回复
17
Simon21ic|  楼主 | 2015-8-19 17:57 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-19 17:59 编辑
yyy71cj 发表于 2015-8-19 16:25
从书中,你看到的表面现象是什么?

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

比如,多任务的话,可以让一个任务接受串口命令,收到后,激活另一个任务去处理
消息系统的话,可以做一个LED计数器,N个LED执行计数,定时器产生一个进位事件发给LED0,LED0进位的话,发送进位事件给LED1,以此类推。

使用特权

评论回复
18
Simon21ic|  楼主 | 2015-8-19 22:16 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-19 22:23 编辑
yyy71cj 发表于 2015-8-19 19:16
你的建议很好,如果有再版的机会,我会考虑加进一些理论化的东西。
当时写书的时候,由于这是 ...

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

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


使用特权

评论回复
19
Simon21ic|  楼主 | 2015-8-20 09:12 | 只看该作者
本帖最后由 Simon21ic 于 2015-8-20 09:21 编辑
yyy71cj 发表于 2015-8-20 08:19
为了写这本书,我准备了5年,写了5年,改了5年,不过这5年都是同一个5年,也就是说,准备、写、 ...

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

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

说实话,在我看来,你们对别人不给好评就说自以为是,也不是什么很有水平的处事方式。
另外,编程没有正果,这个是一直在发展的东西。我说的事件驱动,在服务器领域,已经是很常用的东西了,在嵌入式环境下,也有不少系统是事件驱动的,比如蓝牙协议栈。只是大部分人开发,使用的比较少。以后肯定还会有更加有意思的构架。玩技术就是要一直去学习,一直去思考。

使用特权

评论回复
20
Simon21ic|  楼主 | 2015-8-20 09:29 | 只看该作者
既然说到书,我推荐一本《玩转微控制器多任务程序设计》,我的一个朋友写的第二本,据说年底出版。

使用特权

评论回复
发新帖 本帖赏金 3.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

2597

帖子

104

粉丝