A-A+

Cwinux源码解析(七)

2014年03月14日 Linux环境编程 评论 1 条 阅读 1,044 次
摘要:

Cwinux是如何实现框架功能的。使用Cwinux时,需要先继承AppFramework,并实现AppFramework的noticeXXX和onXXX两种类型的函数。本文描述什么是框架,noticeXXX和onXXX是如何实现的。

本文介绍Cwinux框架中,noticeXXX和onXXX的实现。

先对Framework做个简要介绍。

Cwinux是一个框架(Framework),不单纯是库(lib)。

框架和库的主要区别,在于:

1.  框架封装了处理流程(all the control flow is already there),库通常只是提供了工具型的代码。从复用角度看,库是简答的代码复用,框架有控制流(处理流程)的复用。

2.  框架、库和用户代码的关系不同。框架会调用用户的代码,而库的代码通常只被用户代码调用。

用形象一点的比喻的话,可以如下描述:当我们建造房屋时,库提供的是墙壁、门、窗等部件;框架则提供的是一个空架子,留下了很多空白的洞,用户去填充。

用比较常见的MFC和glibc为例,当我们要写MFC程序时,需要继承自MFC内置类,然后填充上自己的代码(如onDraw);glibc提供的则多是诸如sqrt(x)的工具类。

库和框架各适合有其应用的场景。通常来说,编写一个框架要比编写一个库难度大。

好,框架和库的概念介绍到这里,文末贴有几个链接,可供读者详细阅读。

 

下面具体到Cwinux框架,来看看框架本身和用户代码的关系,以及这种调用关系是如何实现的。

首先,介绍一下Cwinux的使用时的关键点。

1.  开发者的应用需要继承CwxAppFramework。例如 CwxEchoApp:CwxAppFramework。

2.  CwxAppFramework提供两类函数,开发者的应用按需重载其中的函数。其中的函数基本划分为两类:noticeXXXonXXX。noticeXXX是主动的动作(通知框架去做XXX),例如,noticeTcpConn通知框架去建立一个Tcp连接。onXXX 是被动动作,当应用收到XXX事件后,框架会调用应用的onXXX函数,例如,收到消息时,App的onRecvMsg会被调用。

3.  通常需要创建一个处理消息的Handler。CmdOp的子类,注册给Commander以便分发。

4.  通常需要建立线程池。将Commander注册给ThreadPool,线程执行时调用Commander的分发函数。

 

第3、4两个点正是此前几篇博文介绍的Cwinux消息处理框架。现在重点介绍1,2的内容。

Cwinux框架封装了网络编程应用的处理流程,这个流程可以被复用,并以CwxAppFramework的形式暴漏给用户(使用Cwinux的应用程序开发者)。

现在,我们深入Cwinux内部,看看onXXX和noticeXXX是如何实现的。

首先,看CwxAppFramework类的定义(只保留了用于本文解释的部分)。

如前所述,CwxAppFramework中声明了很多用于主动执行型的noticeXXX和事件发生响应型的onXXX的函数(截取的代码中只是一部分)。此外,CwxAppFramework的成员变量中有CwxAppReactor*对象m_pReactor

 

在看代码之前,我们先自己思考一下,如何实现onXXX这样的响应函数。

首先,要写一个框架,必然得有一个CwxAppFramework类来将框架留出的空白的洞暴漏给用户,处理流程已经封装在框架中,用户填充上这些空白的洞后,一个应用程序就可以运行了。

接下来,我们以CwxAppFramework:: onRecvMsg函数为例,开始思考一个具体的onXXX的实现。onRecvMsg的语义是onRecvMsg函数在接收到连接上的数据时调用。这里,对用户来说,网络连接上数据到来是事件,onRecvMsg执行是对应的响应(被动响应)。我们知道,说到底,事件的来源是硬件中断。当然,应用层的Cwinux看不到硬件中断,它只能看到操作系统对中断的封装,具体到当前问题,这个封装表现出来的API就是epoll。

继续思考,我们想想这个事件是如何从硬件到Cwinux的onRecvMsg的。首先网卡硬件中断,该事件被操作系统路由到网络驱动中处理,再经过TCP/IP协议层处理,最后反映到epoll相关函数(epoll_create / epoll_ctl / epoll_wait)在内核中建立的数据结构。这时,某个已经执行epoll_wait函数的线程从epoll_wait的等待过程中返回,并且epoll_wait还告诉我们fd=N的网络连接有数据到来,我们从该连接读取数据。这时,我们需要通知CwxAppFramework调用onRecvMsg函数。

因此,在epoll_wait返回后,框架要能够找到CwxAppFramework对象。我们猜测,一种可能的方法就是把CwxAppFramework对象(指针)通过某种方式传递到响应epoll_wait的对象中。这样,该对象在响应处理epoll_wait反馈的新发生的事件时,调用CwxAppFramework对象指针的onRecvMsg,这样达到通知用户的App应该该调用onRecvMsg。

 

下面我们来看看Cwinux中到底是如何实现onXXX的。

由前面的分析,再回顾一下前面所描述的Reactor模式相关代码,我们知道,响应epoll_wait的对象是CwxAppHandler4Base。查看其代码,没有发现CwxAppFramework的踪影!好吧,再看看CwxAppHandler4Base的具体子类,例如CwxAppHandler4Msg,找到了CwxAppFramework*。CwxAppHandler4Msg的代码大意如下(为方便阅读,做了修改,只保留了核心内容):

这段代码清晰表明了事件响应过程中是如何通知用户应用程序的。代码很清晰,不再赘述。另外,需要填个坑。在Cwinux简介那篇博文中,最后部分说过:建立Tcp监听时有两种模式可选,对应的响应函数不同(一个由框架接收数据生成消息,另一个只是由框架通知有数据到来)。这里正可从代码看出,两个处理方式的不同。建立Tcp监听时,指定isRawData的值,可以决定框架的不同动作:raw情况下,框架只通知有数据到来,getApp()->onRecvMsg(*this, bSuspend);not raw情况下,框架接收数据生成消息,通知有消息到来, this->getApp()->recvMessage(m_header, this->m_recvMsgData, *this, bSuspend)。

 

至此,我们已经解析了Cwinux实现onXXX这类通知用户应用程序有事件到来的具体过程。

 

下面介绍noticeXXX,其实现过程如图示。

notice

CwxAppNoticePipe是一个列表,里面串了CwxAppNotice对象。CwxAppNotice对象用来传递通知命令。AppFramework调用noticeTcpConnect函数,该函数会创建一个Handler4TcpConn对象handle,handle中填充该notice事件的相关参数,并调用m_pReactor串入noticePipe中。AppFramework在启动后进入事件循环run。在hook函数中,AppFramework检查是否有notice事件,如果有,则调用Reactor::noticed处理。

这里有一点需要注意:由于AppFramework在执行事件循环中会因epoll_wait等待(当前没有网络事件等),当这是我们执行noticeXXX时,需要唤醒AppFramework的事件循环,Cwinux的做法是预先创建一个管道(两个fd:m_pipeReader和m_pipeWriter),读取端m_pipeReader加入epoll_wait中监听,写入端m_pipeWriter留着,执行noticeXXX时,向写入端m_pipeWriter写入一个字节,则读取端m_pipeReader发生可读事件,epoll_wait旋即返回,进而有机会执行hook和noticeEvent函数来处理notice事件。

 

Referrence:

Framework vs Library:

http://www.programcreek.com/2011/09/what-is-the-difference-between-a-java-library-and-a-framework/

http://book.51cto.com/art/200801/65025.htm

http://blog.csdn.net/rflyee/article/details/8631753

 

1 条留言  访客:0 条  博主:0 条   引用: 1 条

来自外部的引用: 1 条

  • Cwinux源码解析系列 | 薛途的博客

给我留言