嵌入式操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.5.4 各任务的实现

路由器的软件功能被分解、划分成几个相互独立的任务之后,就很容易在支持多任务的嵌入式操作系统上实现了。假设我们的路由器是在Hello China上进行开发的,而Hello China支持完善的多任务(多线程)机制和任务同步机制,因此,在实现的时候,我们定义6个线程对象用来代表实现路由器功能的6个功能模块。

__KERNEL_THREAD_OBJECT*    lpIpForwarding;   //Ip forwarding thread.
__KERNEL_THREAD_OBJECT*    lpOspf;           //OSPF thread.
__KERNEL_THREAD_OBJECT*    lpBgp;            //BGP thread.
__KERNEL_THREAD_OBJECT*    lpTelnet;         //Telnet server thread.
__KERNEL_THREAD_OBJECT*    lpConsole;        //COM interface thread.
__KERNEL_THREAD_OBJECT*    lpTcpUdp;         //TCP/UDP thread.

Hello China在完成自身初始化后,就应该启动上述6个任务了。在目前的实现中,Hello China的所有初始化功能都是在__init函数中完成的。因此,一个很好的选择就是修改__init函数,在该函数的尾部(这时候所有操作系统功能都已经初始化)创建并启动上述线程。相关代码如下。

VOID __init()
{
    ... ...
    lpIpForwarding=
    KernelThreadManager.CreateKernelThread((__COMMON_ OBJECT*)
                                    &KernelThreadManager,
                                    IpForwarding,
                                    NULL,
                                    0L,
                                    0L,
                                    NULL);
    if(NULL==lpIpForwarding)   //Can not create kernel thread.
        goto __TERMINAL;
    //
    //Create other 5 threads here.
    //
    ... ...
}

这样,当Hello China完成自身的初始化后,上述路由器功能的6个线程就会被启动,目标系统就具备路由器的功能了。

对于每个线程的实现相对来说就比较容易了,因为嵌入式操作系统提供了大量的基础设施,每个具体的功能模块可以充分利用这些基础设施来实现自身功能,比如内存分配、线程同步、消息传递、定时器等功能。我们以IP转发线程为例,说明如何利用操作系统提供的同步机制来实现IP转发功能。在我们的实例中,IP转发功能是作为一个单独的线程来实现的,该线程维护一个本地转发队列,队列中存储了等待转发的数据报文。队列中的数据报文,是由接口驱动程序添加的,一旦接口驱动程序接收到一个IP报文,则会把该IP报文添加到队列中。一旦队列中存在IP报文,IP转发线程就开始工作,依次检查IP队列中的每个报文,根据报文的目的IP地址,查找路由表,然后从查找出的接口上发送出去。若队列中没有IP报文,则IP转发线程进入阻塞状态,以节约系统资源。因此,该线程需要有一个事件对象来配合实现同步功能。该线程的功能描述如下。

__EVENT*      lpHasPacket=NULL;
VOID IpForwarding()
{
    ... ...
    lpHasPacket=ObjectManager.CreateObject(&ObjectManager,
                          NULL,
                          OBJECT_TYPE_EVENT);
                                        //Create event object to
synchronize itself.
if(NULL==lpHasPacket)    //Can not create object.
      goto __TERMINAL;
    while(TRUE)
    {
      lpHasPacket->WaitForThisObject((__COMMON_OBJECT*)
lpHasPacket);  //Wait for packet to reach.
      while(queue is not empty)
      {
          get a ip packet from the queue;
          look up routing table;
          forward the packet according to routing table;
      }
      lpHasPacket->ResetEvent((__COMMON_OBJECT*)lpHasPacket);
    }
    ... ...
}

上述代码中,IpForwarding函数首先创建一个事件对象,作为IP报文队列的指示器,然后进入一个无限循环。在循环的开始,等待创建的事件对象,若事件对象处于非信号状态,则会导致IP转发线程进入阻塞状态。一旦接口驱动程序接收到一个IP报文,则驱动程序会把IP报文挂到IP转发线程的发送队列,然后设置(SetEvent)事件对象。设置事件对象的结果是唤醒IP转发线程,IP转发线程一旦被唤醒,则依次检查转发队列中的IP报文,并查找路由表,完成报文的转发,直到IP队列变为空(所有IP报文处理完毕),然后转发线程复位事件对象,这样在循环的开始处转发线程又会阻塞自己,等待IP报文的再次到达。

其他线程的实现与此类似。需要说明的是,对于共享数据结构,比如路由表的访问,需要有一个互斥体对象来进行访问同步。比如在路由器的实现中,定义一个互斥体对象,来完成对路由表的互斥访问,代码如下。

__MUTEX*     lpRtMutex=NULL;
... ...
lpRtMutex->WaitForThisObject((__COMMON_OBJECT*)lpRtMutex);
modify routing table;
lpRtMutex->ReleaseMutex((__COMMON_OBJECT*)lpRtMutex);
... ...

这样,共享路由表数据结构的各线程之间共享该互斥体对象。在访问路由表的时候,首先获得互斥体对象,然后再进行修改,修改完毕之后,释放互斥体对象。这样可确保路由表的一致性。

上述这个路由器实例,非常简单,仅仅是为了说明如何利用嵌入式操作系统开发一个应用。实际的路由器其功能远不止这些,而且相互之间的关系更加复杂,但开发的基本方法和思路却与此类似。