本帖最后由 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多字节,应该也算是一个微内核了吧。
状态机和操作系统就不讲了
|