贝贝花花包包店,精品555皮具,钱夹,皮夹

字体: | 推荐给好友 上一篇 | 下一篇

ACE技术论文集-第1章 ACE自适配通信环境

发布: 2008-6-13 15:02 | 作者: Prashant Jain and D | 来源: 转载 | 查看: 683次

 
1.3.2 反应堆(Reactor):事件多路分离和事件处理器分派
 
通信软件对许多不同种类的事件(比如基于定时器、基于I/O、基于信号和基于同步的事件)进行多路分离和处理。例如,WWW服务器通常在内部使用事件循环、监控一个周知的Internet端口(通常是端口80)。该端口与一个应用特有的处理器(handler)相关联,此处理器侦听客户在端口80上的连接。当客户连接时,WWW服务器接受连接,并创建一个事件处理器为此HTTP请求服务。例如,如果Netscape浏览器发送一个GET请求,WWW服务器将返回所请求的内容给浏览器。
为统一并自动化事件驱动的处理活动,ACE提供了一个事件多路分离和事件处理器分派构架,称为ACE_Reactor[10]。Reactor将UNIX和Windows NT事件多路分离机制(比如select和poll)的功能封装在一个可移植和扩展的OO包装中[10]。这些OS事件多路分离系统调用检测在一或更多I/O句柄上同时发生的不同类型的输入和输出事件。
为改善应用的可移植性,不管使用了何种事件多路分离机制,ACE_Reactor都提供同样的接口。另外,ACE_Reactor还封装了在多线程事件处理环境中正确而高效地执行回调方式的分派所必需的互斥机制。
 
 
图1-5 ACE_Reactor类属组件
 
ACE_Reactor中的对象的结构在图1-5中演示。这些对象负责(1)事件(比如定时器驱动的呼出队列生成的临时事件、通信端口上收到的I/O事件,以及信号事件)的多路分离和(2)分派预登记事件处理器的适当方法,以处理这些事件。如图1-5所示,所有事件处理器对象都派生自ACE_Event_Handler抽象基类。该类指定一个被用于ACE_Reactor接口,以针对特定事件的到达、分派特定的应用特有的方法。
ACE_Reactor使用在Event Handler中声明的虚方法,以集成基于I/O句柄、基于定时器,和基于信号的事件的多路分离。基于I/O句柄的事件经由handle_input、handle_output和handle_exceptions方法分派;基于定时器的事件经由handle_timeout方法分派;而基于信号的事件经由handle_signal分派。
ACE_Event_Handler的子类(比如1.4.1描述的Logging_Handler)可以通过定义另外的方法和数据成员来扩展基类的接口。此外,ACE_Event_Handler接口中的虚方法可以有选择地被子类重载,以实现应用特有的功能。例如,在1.4.2介绍的PBX监控服务器中,应用特有的子类定义了Event Handler对象,通过继承和/或实例化1.3.1描述的SOCK SAP或TLI SAP传输接口类的对象,与客户进行通信。在ACE_Event_Handler基类中的虚方法被子类定义后,应用可以实例化所得的事件处理器对象。
下面的例子实现一个简单的程序,它使用双向通信信道,在两个进程间持续地前后交换消息。此例演示服务是怎样从ACE_Event_Handler继承的。它还描述ACE_Reactor怎样被用于多路分离和分派基于I/O、基于信号和基于定时器的事件。下面所示的Ping_Pong类从ACE_Event_Handler继承接口,并实现它自己的应用特有的功能:
 
class Ping_Pong : public ACE_Event_Handler
{
public:
Ping_Pong (char *b)
: len (min (strlen (b) + 1, BUFSIZ))
{
strncpy (this->buf, b, BUFSIZ);
}
 
virtual int handle_input (ACE_HANDLE handle)
{
return read (handle, this->buf, BUFSIZ);
}
 
virtual int handle_output (ACE_HANDLE handle)
{
return write (handle, this->buf, this->len);
}
 
virtual int handle_signal (int signum)
{
this->finished = 1;
}
 
virtual int handle_timeout (const Time_Value &, const void *)
{
this->finished = 1;
}
 
bool done (void)
{
return this->finished == 1;
}
 
private:
sig_atomic_t finished;
char buf[BUFSIZ];
size_t len;
};
 
  双向通信信道使用SVR4 UNIX STREAM管道创建:
 
static int init_handles (ACE_HANDLE handles[])
{
if (pipe (handles) == -1)
LM_ERROR ((LOG_ERROR, "%p\n%a", "pipe", 1));
 
// Enable message-oriented mode instead of
// bytestream mode.
int arg = RMSGN;
if (ioctl (handles[0], I_SRDOPT, arg) == -1
|| ioctl (handles[1], I_SRDOPT, arg) == -1)
return -1;
}
 
主程序通过打开适当的通信信道开始运行。接着,程序派生一个子进程,在两个进程中各实例化一个名为callback的Ping_Pong事件处理器对象,为基于I/O、基于信号,和基于定时器的事件而将callback对象登记到ACE_Reactor的一个实例上,然后进入事件循环。如下所示:
 
int main (int argc, char *argv[])
{
ACE_HANDLE handles[2];
ACE_Reactor reactor;
 
init_handles (handles);
 
pid_t pid = fork ();
Ping_Pong callback (argv[1]);
 
// Register I/O-based event handler
reactor.register_handler (
handles[pid == 0],
&callback,
ACE_Event_Handler::READ_MASK
| ACE_Event_Handler::WRITE_MASK);
 
// Register signal-based event handler
reactor.register_handler (SIGINT, &callback)
 
// Register timer-based event handler
reactor.schedule_timer (&callback, 0, 10)
 
/* Main event loop (run in each process) */
while (callback.done () == false)
reactor.handle_events ();
 
return 0;
}
 
基于定时器和基于信号的事件的callback事件处理器存储在ACE_Reactor中的相应的表中。同样地,当调用register_handler方法登记基于I/O的事件处理器时,ACE_Reactor在一个内部表中存储适当的句柄。当应用随后通过调用ACE_Reactor::handle_events方法执行它的主事件循环时,该句柄被作为参数传递给底层的OS I/O多路分离系统调用(例如,select或poll)。
 
 
图1-6 ACE Reactor交互图
 
当与预登记的事件处理器callback对象相关联的输入、输出,和定时器事件在运行时发生时,ACE_Reactor自动检测这些事件并分派适当的事件处理器对象的方法。被分派的callback对象的方法负责完成应用特有的功能(比如写入消息到通信信道、从通信信道读取消息,或是设置触发程序终止的标志)。这些组件间的协作通过对象交互图在图1-6中描述。
 
1.3.3 并发:多线程和同步机制
 
ACE并发类属包含的OO包装(例如,ACE_Mutex、ACE_Condition、ACE_Semaphore和ACE_RW_Mutex)封装了相应的Solaris[31]和POSIX Pthreads[32]多线程和同步机制。这些包装使在类中作为成员域出现的同步对象的初始化过程得以自动化,并且还简化了线程和同步机制的常见的使用模式。例如,下面的代码演示了怎样将SunOS mutex_t和cond_t同步机制的ACE封装用于典型的共享资源管理类:
 
class Resource_Manager
{
public:
Resource_Manager (u_int initial_resources)
: resource_add_ (this->lock_),
resources_ (initial_resources) {}
 
int acquire_resources (u_int amount_wanted)
{
this->lock_.acquire ();
 
while (this->resources_ < amount_wanted)
{
this->waiting_++;
 
// Block until resources are released.
this->resource_add_.wait ();
}
 
this->resources_ -= amount_wanted;
this->lock_.release ();
}
 
int release_resources (u_int amount_released)
{
this->lock_.acquire ();
this->resources_ += amount_released;
if (this->waiting_ == 1)
{
this->waiting_ = 0;
this->resource_add_.signal ()
}
else if (this->waiting_ > 1)
{
this->waiting_ = 0;
this->resource_add_.broadcast ();
}
this->lock_.release ();
}
 
// ...
 
private:
ACE_Mutex lock_;
ACE_Condition resource_add_;
u_int resources_;
u_int waiting_;
// ...
};
 
注意ACE_Condition对象resource_add的构造器是怎样将ACE_Mutex对象lock_与Condition对象绑定在一起的。与底层的SunOS cond_t cond_wait接口相比较,这样的方式简化了ACE_Condition::wait调用接口。
尽管ACE_Mutex包装为同步多线程控制提供了一种相对优雅的方法,它们仍是潜在地易错的,因为开发者有可能会忘记调用release方法(或是由于程序员的疏忽,或是由于C++异常的发生)。为改善应用的健壮性,ACE同步机制有效地利用了C++类构造器和析构器语义。为确保ACE_Mutex锁被自动获取和释放,ACE提供了名为ACE_Guard的助手类,定义如下:
 
template
class ACE_Guard
{
public:
ACE_Guard (MUTEX &m): lock_ (m)
{
this->lock_.acquire ();
}
 
?ACE_Guard (void)
{
this->lock_.release ();
}
 
private:
MUTEX &lock_;
}
 
ACE_Guard类的对象定义一个代码块,在块开始时获取ACE_Mutex,而在块结束时自动释放。
注意ACE_Guard类被定义为模板,由互斥机制参数化。有若干不同类型的互斥语义[33]。每种互斥共有一个通用接口(也就是,acquire/release),但具有不同的序列化和性能属性。ACE支持的两种互斥是非递归递归锁
 
  • 非递归锁:非递归锁提供互斥的一种高效的形式,它定义一个临界区,每一时刻只有单个线程可在其中执行。它们之所以是非递归的,是因为当前拥有锁的线程在将其释放前不可以再次获取它。否则,就会立即发生死锁。SunOS 5.x通过它的mutex_t、rwlock_t,和sema_t类型(POSIX Pthreads不提供后两种同步机制)为非递归锁提供支持。ASX构架提供Mutex、RW_Mutex,和Semaphore包装,以分别封装这些语义。
  • 递归锁:另外一方面,递归锁允许acquire方法嵌套调用,只要当前拥有该锁的线程就是试图重新获取它的线程。递归锁对于回调驱动的事件分派构架(比如1.3.2描述的反应堆)特别有用,在其中构架的事件循环执行对预登记的用户定义的对象的回调。因为随后用户定义的对象可能经由它的方法入口重入分派构架,必须使用递归锁以防止在回调过程中构架持有的锁发生死锁。
    下面的C++模板类为Solaris线程和POSIX Pthreads(它们自己不提供递归锁语义)的同步语义实现了递归锁语义:
 
template
class ACE_Recursive_Thread_Mutex
{
public:
// Initialize a recursive mutex.
ACE_Recursive_Thread_Mutex (void);
 
// Implicitly release a recursive mutex.
?ACE_Recursive_Thread_Mutex (void);
 
// Acquire a recursive mutex.
int acquire (void) const;
 
// Conditionally acquire a recursive mutex.
int tryacquire (void) const;
 
// Releases a recursive mutex.
int release (void) const;
 
private:
ACE_Mutex nesting_mutex_;
ACE_Condition mutex_available_;
thread_t owner_id_;
int nesting_level_;
};
 
注意这个类的接口与ACE中可用的其他锁定机制一致[22]。
下面的代码演示怎样将ACE_Guard和ACE_Recursive_Thread_Mutex用于回调机制中:
 
int Callback::dispatch (const Event_Handler *eh, Event *event)
{
// Constructor acquires the lock on entry.
ACE_Guard > mon (this->lock_);
 
eh->handle_event (event);
// Destructor of Guard releases the lock on exit.
}
 
该代码确保Event_Handler对象的登记作为临界区执行。该例子还演示了C++习语的使用[34],在其中,当ACE_Guard对象创建时,类的构造器自动在同步对象上获取锁。同样地,当mon对象出了作用域,类的析构器自动解锁对象。而且,mon的析构器将被自动调用以释放互斥锁,而不管if/else语句的哪一分支从方法返回。此外,如果在register_handler方法的处理过程中发生了C++异常,锁也将会被自动释放。ACE_Recursive_Thread_Mutex用于确保dispatch方法分派的应用特有的handle_event回调不会导致死锁,如果它们重入Callback对象的话。
ACE还提供一个ACE_Thread_Manager类,其中包括一组用于管理相互协作、以实现集体行为的线程组的机制。例如,ACE_Thread_Manager类提供的机制(比如suspend_all和resume_all)允许任意数目的参与线程被原子地挂起和恢复。
 
1.3.4 服务配置器(Service Configurator):显式动态链接机制
 
静态链接是这样一种技术:在编译时和/或静态链接时将所有对象文件绑定在一起,以组成完整的可执行程序。相反,动态链接使得对象文件可以在初始调用程序或迟后在运行时的任意时刻被加入地址空间,和/或被从中删除。SunOS 4.x和5.x同时支持隐式的显式的动态链接:
 
  • 隐式动态链接被用于实现共享对象文件,也称为共享库[35]。共享对象文件可减少主要和辅助存储的使用,因为只有一份共享对象的拷贝存在于内存中和磁盘上,而不管执行该代码的进程的数量是多少。而且,特定的地址解析和重定位操作可以延迟至动态链接的函数被初次引用时。这样的“懒惰计算”方案将进程启动时的链接编辑开销降到了最小。
  • 显式动态链接提供的接口允许应用获取、利用,和/或移除在共享对象文件中定义的符号的运行时地址绑定[36]。显式动态链接机制显著地增强了通信软件的功能和灵活性,因为服务可以在运行时插入和/或删除,而不用终止和/或重启整个应用。SunOS 5.x通过dlopen/dlsym/dlcose例程支持显式动态链接,而Win32通过LoadLibrary/GetProcAddress调用支持这一特性。
 
ACE提供Service Configurator类属来在一组类和继承层次中封装SunOS的显式链接机制。Service Configurator有效地利用了其他ACE组件来扩展传统的看守配置和控制构架的功能[20](比如listen[3]、inetd[2],和Windws NT Service Control Manager[37]),这些功能为下列行为提供自动的支持:(1)并发、多服务通信软件的静态和动态配置,(2)为I/O活动监控通信端口组,以及(3)分派在所监控端口上接收到的消息给适当的应用特有的服务。这一部分的余下部分讨论Service Configurator类属的主要组件。
 
1.3.4.1 ACE_Service_Object继承层次
 
Service Configurator中的主要配置单元是服务(service)。服务可以是简单的(比如返回一天中的当前时间)或高度复杂的(比如PBX事件话务的分布式、实时路由器[6,38])。为了给定义、配置和使用通信软件提供一致的环境,所有的应用服务都派生自ACE_Service_Object继承层次(在图1-7(1)中演示)。
 
 
图1-7 Service Configurator类属的组件关系
 
ACE_Service_Object类是一个有着继承关系的多层类型层次的焦点。在此类型层次中的抽象类提供的标准接口可以有选择地被应用特有的子类实现,以访问特定的、应用无关的Service Configurator机制。这些机制提供透明的动态链接、事件处理器登记、事件多路分离和服务分派。通过使处理器对象的应用特有部分与底层的应用无关的Service Configurator机制去耦合,显著地减少了在运行中的应用中插入和移除服务所必需的工作。
ACE_Service_Object继承层次由ACE_Event_Handler和ACE_Shared_Object抽象基类组成。ACE_Event_handler类已在上面的1.3.2中描述。其他类的行为在下面概述。
 
  • ACE_Shared_Object抽象基类:该基类指定用于将服务处理器对象动态链接进应用的地址空间的接口。ACE_Shared_Objecti抽象基类输出三个抽象方法:init、fini,和info。这些方法在Service Configurator提供的应用无关的可复用组件和利用这些组件的应用特有功能之间建立了协定。通过使用抽象方法,Service Configurator确保服务处理器实现遵从它提供特定的配置相关信息的义务。这些信息随后被Service Configurator用于在运行时自动链接、初始化、标识服务,以及解除服务的链接。
ACE_Shared_Object基类独立于ACE_Event_Handler类而定义,以清晰地区分它们的两组不相关的事务。例如,某些应用(比如编译器或文本编辑器)可能会从动态链接中获益,尽管它们可能不需要通信端口事件多路分离。相反,其他应用(比如ftp服务器)可能需要事件多路分离,但可能不需要动态链接。通过将这些接口分离成两个基类,应用就能够选择服务配置器的一个子集,而不会带来不必要的存储开销。
  • ACE_Service_Object抽象派生类:通常,安装和管理复杂的分布式系统需要有动态链接、事件多路分离和服务分派的支持,以使应用服务动态配置和重配置自动化。因而,Service Configurator定义了ACE_Service_Object类,将ACE_Event_Handler和ACE_Shared_Object抽象基类的接口结合在一起。所得到的抽象派生类提供的接口可被开发者用作实现服务、并将其配置进服务配置器的基础。
在开发过程中,ACE_Service_Object的应用特有的子类必须实现由ACE_Service_Object类接口指定的suspend和resume抽象方法。这些方法被Service Configurator自动调用,以响应外部的事件。应用开发者可以控制对象为挂起服务所采取的动作,而无须完全移除它或解除它的链接;唤醒先前挂起的服务也是一样。
此外,应用特有子类还必须实现ACE_Service_Object子类所继承(不是定义)的四个抽象方法(init、fini、info,和get_handle)。init方法用作服务处理器在运行时初始化过程中的入口。当ACE_Service_Object的实例被动态链接时,该方法负责执行应用特有的初始化。同样地,当ACE_Service_Object在运行时被解除链接并从应用中移除时,Service Configurator自动调用fini方法。该方法通常执行终止操作,由其释放动态分配的资源(比如内存、I/O句柄或同步锁)。info方法格式化用户可读的一个字符串,简洁地报告服务访问信息及为服务功能建立文档。客户可以查询应用以获取此信息,并将它用于与应用中运行的某个特定的服务联系。最后,get_handle方法被Reactor用于从服务处理器对象中提取底层的I/O句柄。此I/O句柄标识一个可被用于从客户那里接受连接或接收数据的传输端点。
  • 应用特有的具体派生子类:Service Object是一个抽象类,因为它的接口包含有继承自Event Handler和Shared Object抽象基类的抽象方法。因而,开发者必须提供具体的子类(1)定义上面描述的六个抽象方法,以及(2)实现必需的应用特有的功能。为完成后一任务,子类通常定义由Service Object接口输出的某些特定的虚方法。例如,常常实现handle_input方法,用以从客户那里接受连接或数据。
图1-7(1)中描述的ACE_Acceptor类是应用无关的子类的一个例子;它是分布式日志工具的一部分,接受连接请求。该类将在1.4.1介绍的例子中作进一步描述。
 
1.3.4.2 ACE_Service_Repository
 
Service Configurator类属同时支持单服务和多服务通信软件的配置。因而,为简化运行时管理,常常有必要个别和/或共同地控制及协调由应用的正在活动的服务组成的各个ACE_Service_Object。ACE_Service_Repository是用于协调本地和远地查询的对象管理器,这些查询涉及由基于Service Configurator的应用提供的服务。在对象管理器中的搜索结构将服务名(以ASCII字符串表示)与ACE_Service_Object(以对象代码表示)绑定在一起。一个服务名唯一地标识在仓库中(repository)存储的一个ACE_Service_Object的实例。
ACE_Service_Repository中的每一条目都含有一个指向应用特有派生类的ACE_Service_Object部分的指针(如图1-7(2)所示)。这使得Service Configurator能够静态或动态地对应用进行ACE_Service_Object的装载、启用、挂起、恢复,或卸载。对于动态链接的ACE_Service_Object,仓库还维护了底层共享对象文件的句柄。当ACE_Service_Object所提供的服务不再被需要时,这个句柄用于从运行中的应用解除其链接并卸载之。
与ACE_Service_Repository一起,还提供了一个迭代器(iterator)类。该类用于访问仓库中的每一个ACE_Service_Object,而不会不恰当地危及数据封装。
 
1.3.4.3 ACE_Service_Config
 
ACE_Service_Config类是Service Configurator构架中的统摄组件。如图1-7(3)所示,该类集成了其他的Service Configurator构架组件(比如ACE_Service_Repository和ACE_Reactor),以使并发、多服务通信软件的静态和/或动态配置自动化。
ACE_Service_Config类使用配置文件(称为svc.conf)来指导它的配置和重配置活动。每一应用都可与一个独立的svc.conf配置文件相关联。同样地,一组应用也可以被单个的svc.conf文件描述。图1-8使用扩展的Backus/Naur格式(EBNF)描述了svc.conf文件中的主要语法成分。文件中的每个服务配置条目服务配置指令起头,它指定要执行的配置活动。表1-1总结了有效的服务配置指令。
 
 
图1-8 服务配置条目的EBNF格式
 
指令
描述
dynamic
动态链接和启用服务
static
启用静态链接的服务
remove
完全地移除服务
suspend
挂起服务,而不移除它
resume
恢复先前挂起的服务
表1-1 服务配置指令
 
对于每个动态链接的服务,相应的服务配置条目含有指示共享对象文件位置的属性,以及需用于在运行时初始化服务的参数。通过将服务属性和初始化参数统一进单一配置文件,显著地简化了应用中服务的安装和管理。svc.conf文件有助于使应用的结构与它的服务的行为去耦合。基于在svc.conf文件中指定的应用特有的属性和参数,这样的去耦合也允许对构架提供的机制进行“懒惰的”配置和重配置。
 
图1-9描述的状态转移图演示了Service Configurator类属中的一些方法,这些方法被调用,以响应在服务配置、执行和重配置的过程中发生的事件。例如,当CONFIGURE和RECONFIGURE事件发生时,ACE_Service_Config类的process_directives方法被调用,以查询svc.conf文件。在应用的新实例被初始配置时这个文件第一次被查询。无论何时接收到预先指定的外部事件(比如UNIX SIGHUP信号或来自socket的通知)、触发了应用的重配置,这个文件都会被再次查询。
 
 
图1-9 服务配置、执行和重配置的状态转移图
 
1.3.5 流:分层服务的集成
 
流类属是自适配通信环境的主要焦点。该类属含有自适配服务执行体(ASX)构架,它集成了较低级的OO包装组件(像IPC SAP)和较高级的类属(像Reactor和Service Configurator)。ASX构架有助于简化分层地集成在一起的通信软件的开发,特别是用户级通信协议栈和网络服务器。ASX构架设计用于改善应用特有的服务、以及这些服务所依赖的底层OS并发、IPC、显式动态链接和多路分离机制的模块性、可扩展性、复用性和可移植性。
ASX构架为通信软件的开发者提供下面两种好处:
 
  1. 它嵌入、封装,并实现了通常用于开发通信软件的一些关键设计模式。通过确定大型系统开发中的基本挑战,设计模式有助于提高软件的质量。这些挑战包括开发者之间的体系结构知识的交流;适应新的设计范式或体系样式;解决非功能性的压力,比如复用性、可移植性和可扩展性;以及避开那些通常只能通过经验来学习的开发陷阱和缺陷。
  2. 它严格地区分了关键的开发事务。ACE将通信软件的开发划分为两种不同的范畴:(1)不依赖于应用的事务,它们对于大多数或所有的通信软件来说都是通用的(比如端口监控;消息缓冲、排队和多路分理;服务分派;本地/远地进程间通信;并发控制;以及应用配置、安装和运行时服务管理),以及(2)应用特有的事务,它们依赖于特定的应用。通过复用ACE提供的OO包装和构架,开发者从花费时间重新发明一些经常重复出现的任务的方案中被解放出来。于是,这使得他们可以专注于那些组成特定应用的、关键的更高级功能需求和设计事务。
 
1.3.5.1主要的ASX特性
 
通过使应用特有的处理策略与下列配置相关的开发活动和机制去耦合,ASX构架增强了通信软件的灵活性:
 
  • 与每个应用进程相关联的服务的类型和数目:ASX构架允许应用将一或多个服务合并进单一的管理单元。这样的配置通信软件的多服务方法有助于(1)通过自动完成通用的服务初始化活动,简化开发并复用代码,(2)通过“按需”生成服务处理器,降低OS资源的消耗(比如进程表槽),(3)无须修改已有的源代码或终止正在执行的分派进程(比如inetd超级服务器)就能够更新应用服务,以及(4)通过一组一致的配置管理操作,统一网络服务的管理。
  • 服务被配置进应用的时间点:ASX构架有效地对Service Configurator构架(在1.3.4中描述)进行利用,以提供一个可扩展的、面向对象的接口,使显式动态链接的OS机制的使用得以自动化。通过允许内部服务在应用开始执行时或运行时被配置,动态链接增强了通信软件的可扩展性。该特性使得应用的服务可被动态配置,而无需修改、重编译、重链接,或重启活动的服务。在ASX构架中,选择静态或动态配置可针对每个服务单独进行。而且,选择可延迟至应用开始执行的时候。
  • 执行代理的类型:在ASX构架中,服务可在运行时通过若干不同类型的进程和线程执行代理来执行。通过使服务功能与用于调用服务的执行代理去耦合,ASX构架扩展了开发者可用的应用并发配置方式的选择范围。
高效的应用并发配置常常依赖于特定的服务需求和平台特性。例如,基于进程的配置对于实现长持续时间的服务(比如Internet ftp和telnet)来说也许是适当的,这样的配置使它们的安全机制基于进程所有权。在这种情况下,每个服务(或服务的每个活动实例)可被映射到不同的进程,并在多处理器平台上并行执行。但是,在其他环境中采用另外的配置可能更为合适。例如,在分离的线程中实现相互协作的服务(比如在分布式数据库引擎的终端系统中的服务)常常更加简单和高效,因为它们经常性地访问公用数据结构。在该方法中,每个服务可以在同一进程的不同线程中执行,以降低调度、上下文切换和同步的开销[6]。
  • 层次相关的服务被结合进应用的顺序:复杂服务可使用一系列互连的独立服务对象组成,它们通过传递消息进行通信。这些对象可以通过本质上任意的配置结合在一起,以满足应用要求和增强组件复用。
  • 基于I/O句柄和基于定时器事件的多路分离机制:这些机制用于将到来的连接请求和数据分派给预登记的应用特有的处理器。通过一个可扩展的和类型安全的面向对象接口,ASX构架使用Reactor类属来集成基于I/O句柄、基于定时器,和基于信号的事件的多路分离。
  • 底层IPC机制:应用服务可以使用1.3.1描述的IPC SAP机制来与本地或远地终端系统上的通信实体交换数据。不像弱类型的、“基于句柄”的socket和TLI接口,IPC SAP包装使得应用能够通过类型安全的、可移植的接口访问底层OS IPC机制。
 
ASX构架合并了来自若干模块化的通信构架的概念,其中包括系统V STREAMS[39]、x-kernel[40]和来自面向对象操作系统Choices的Conduit构架(对这些和其他通信构架的考察见[42])。这些构架全都支持通过将“积木块”协议和服务组件互连来灵活地配置通信子系统。总之,通过使处理功能与周边构架的基础构造去耦合,这些构架鼓励了标准的与通信相关的组件的开发(比如消息管理器、基于定时器的事件分派器、多路分离器[40]和各种协议功能[43])。如下所述,ASX构架包含的一些额外特性可进一步使处理功能与底层的处理体系结构去耦合。
不像STREAMS,被配置进ASX构架的应用服务在用户空间、而不是内核空间中执行。在用户空间、而不是OS内核中开发通用通信软件有着若干优点:
 
  • 对一般OS特性的访问:在用户空间中运行的应用可以访问全面的OS机制(比如动态链接、内存映射文件、多线程、大虚拟地址空间、进程间通信机制、文件系统和数据库)。相反,驻留内核的组件常常局限于一组有限的内核特有的机制。尽管有一些习语可以克服其中的一些局限(例如,维护一个用户级看守、代表驻留内存的组件完成文件I/O),这些工作方法往往不那么优雅和可移植。
  • 增强的开发环境:较高级编程工具(比如符号调试器)可用于开发用户空间中的应用。相反,由于在内核编程环境中,调试工具的原始和微妙的时序干扰,在OS内核中开发网络服务是一项复杂而艰巨的任务[44]。在这样受限的环境中期待开发者有效地编程是很冒险的。
  • 增强的系统健壮性:在用户级进程或线程中产生的异常情况(比如废弃的NULL指针、除0错误,等等)只会影响出错进程或线程。但是,OS内核中的异常情况可能导致整个操作系统的“恐慌”和崩溃。而且,在每次崩溃后重启OS很快会变得冗长乏味。
  • 可移植性:在不同OS平台间(即使是同一OS平台的不同变种)移植内核级驱动程序通常比移植用户级应用组件要承担更多的复杂性。SunOS 5.x DDI/DKI API意图减少UNIX平台上的一些移植性问题,但那并未解决在其他平台上(比如OS/2、Windows NT、VMS和Novell Netware)的应用的移植性问题。
 
在OS内核中实现分布式服务的主要原因是提高性能。例如,一个通信协议栈的驻留内核的实现常常有助于减少调度、上下文切换和跨越保护域边界的开销,并且因为使用了“线性的”(而非分页的)内存,还可以表现出更可预测的响应时间。但是,对于许多通信软件应用来说,用户空间开发所带来的更多的灵活性、简单性、健壮性和可移植性是关键性的需求,可以弥补潜在的性能下降。
 
1.3.5.2 Stream类属组件
 
Stream类属中的组件负责协调一或多个ACE_Stream对象的配置和在运行时的执行。 ACE_Stream是用户应用与之协作的对象,用以配置和执行ASX构架中的应用特有的服务。如图1-10所示,ACE_Stream包含有一系列互连的ACE_Module对象,它们可被(1)开发者在安装时或(2)应用在运行时链接在一起。ACE_Module对象用于将应用的体系结构分解成一系列互连的、功能独立的层次。通常每一层实现一簇相关的服务功能(比如端对端传输服务或表示层格式化服务)。每个ACE_Module包含的一对ACE_Task对象将该层划分为读端和写端处理功能。
OO语言特性(比如类、继承、动态绑定和参数化类型)使开发者无须修改不依赖于应用的ASX构架组件,就能够将应用特有的功能合并进流中。例如,将服务层增加进流涉及(1)从缺省ACE_Task接口继承并有选择地在子类中覆盖若干虚函数,以提供应用特有的功能,(2)分配一个新的ACE_Module,其中包含有应用特有的ACE_Task子类的两个实例(一个用于读端,一个用于写端),以及(3)将ACE_Module插入到流中。经由消息传递接口,邻近的相互连接的ACE_Task中的服务通过交换类型化的消息进行协作。
为避免再次发明习惯用语,流类属中的许多类名与系统V STREAMS构架中可用的类似组件相对应[39]。但是,在这两种构架中,用于支持可扩展性和并发的技术有着显著的差异。例如,为ASX流类增加应用特有的功能是通过从已有的构架组件继承若干定义良好的接口和实现来进行的。比起系统V STREAMS中所用的函数指针技术,使用继承来增加新功能提供了强得多的类型安全性。ASX流类还采用了不同的并发控制方案来减少死锁的可能性,并简化流中的Task之间的流控制。ASX流类对系统V STREAMS中所用的基于协同程序的、“无重量的”服务处理机制[45]进行了完全的重新设计和实现。这些ASX的变动意图在共享内存的多处理器平台上更为有效地利用多PE。
 
 
图1-10 ASX构架中的组件
 
余下部分详细讨论流类属的主要组件(例如,ACE_Stream类、ACE_Module类、ACE_Task类):
 
  • ACE_Stream类:ACE_Stream类定义流的应用接口。ACE_Stream对象包含有一个由一或多个层次相关的服务组成的栈,为应用提供双向的get/put风格的接口,用以通过组成特定流的互连Module发送和接收数据及控制消息。ACE_Stream还实现了一个push/pop风格的接口,允许应用通过插入和移除下面描述的ACE_Module类的对象、在运行时对流进行配置。
  • ACE_Module类:ACE_Module类定义独立的、应用特有的功能层。流通过互连一系列ACE_Module对象构成。流中的ACE_Module对象有着松散的耦合,并通过传递类型化的消息来与邻近的ACE_Module对象协作。每个ACE_Module对象包含有一对指针,指向应用特有的ACE_Task类的子类的对象。ACE_Task类在下面简略描述。
如图1-10所示,当流被打开时,两个缺省的ACE_Module对象(ACE_Stream_Head和ACE_Stream_Tail)自动被安装。这两个ACE_Module解释预定义的ASX构架控制消息和数据消息,这些消息在运行时在流中流动。ACE_Stream_Head类在应用和流之间提供消息缓冲接口。ACE_Stream_Tail类通常将来自网络或伪设备的消息转换为规范的内部消息格式,可被流中更高层的组件进一步处理。同样地,对于外发的消息,它将其从内部格式转换为网络消息。
  • ACE_Task抽象类:ACE_Task类是ACE中用于创建用户定义的、处理应用消息的主动对象(Active Object)[11]和被动对象(Passive Object)的中心机制。ACE的任务可以进行下面的活动:
 
    • 可被动态链接
    • 可用作I/O操作的多路分离端点
    • 可与多个线程控制相关联(也就是,变成所谓的“主动对象”)
    • 可将消息存储在队列中,以用于后续处理
    • 可执行用户定义的服务
 
ACE_Task抽象类定义的接口被派生类继承和实现,以提供应用特有的功能。它之所以是抽象类,是因为它的接口定义了下面描述的抽象方法(open、colse、put和svc)。通过使ACE_Stream类属提供的不依赖于应用的组件与继承并使用这些组件的、应用特有的子类去耦合,把ACE_Task定义为抽象类增强了复用。同样地,使用抽象方法允许编译器确保Task的子类遵从自己提供下面的功能的义务:
 
    • 初始化和终止方法:派生自ACE_Task的子类必须实现open和close方法,执行应用特有的ACE_Task初始化和终止活动。这些活动通常分配和释放像连接控制块、I/O句柄,及同步锁这样的资源。
ACE_Task可与ACE_Module一起或分开定义和使用。当与ACE_Module一起使用时,它们被成对地存储:一个ACE_Task子类操作读端的、自下而上发送给它的ACE_Module层的消息的处理;另一个操作写端的、自上而下发送给它的ACE_Module层的消息的处理。当ACE_Module在流中插入和移除时,Module的写端和读端的ACE_Task子类的open和close方法分别被ASX构架自动调用。
 
    • 应用特有的处理方法:除了open和close,ACE_Task的子类还必须定义put和svc方法。这些方法在消息上执行应用特有的处理功能。例如,当消息到达流的头或尾时,作为调用流中的每一ACE_Task的put和/或svc方法的结果,它们被“护送”着通过一系列互连的ACE_Task。
当在流中某一层的ACE_Task传递消息给另一层中相邻的ACE_Task时,put方法被调用。put方法相对于它的调用者同步地运行,也就是,它从最初调用其put方法的Task那里借用线程控制。该线程控制通常源自“上游”应用进程,“下游”处理I/O设备中断的进程池[40],或流内部的事件分派机制(比如面向连接的传输协议ACE_Module中的用于触发重发的定时器驱动呼出队列)。
如果ACE_Task作为被动对象执行(也就是,它总是从调用者那里借用线程控制),ACE_Task::put方法就是ACE_Task的入口,并用作ACE_Task在其中执行的上下文。相反,如果ACE_Task作为主动对象执行,ACE_Task::svc方法就用于相对于其他ACE_Task异步地执行应用特有的处理。不像put,svc方法不会被相邻的ACE_Task直接调用,而是被与它的ACE_Task相关联的一个单独的线程调用。该线程为ACE_Task的svc方法提供了执行上下文和线程控制。svc方法运行一个事件循环,持续地等待消息到达ACE_Task的ACE_Message_Queue(见下面的条目)。
在put或svc方法的实现中,消息可经由ACE_Task的put_next方法、被转发给流中相邻的ACE_Task.。put_next调用在相邻层中驻留的下一个ACE_Task的put方法。这个对put的调用可以借用调用者的线程控制,并立即处理消息(也就是,图1-11(1)中所示的同步处理方法)。相反,put方法可以将消息入队,并推迟给在单独的线程控制中执行的svc方法处理(也就是,图1-11(2)中所示的异步处理方法)。如在[6]中所讨论的,选择特定的处理方法对性能和编程的容易度有着显著的影响。
 
 
图1-11 调用put和svc方法的可选方法
 
    • 消息排队机制:除了open、close、put和svc抽象方法接口,每个ACE_Task还具有一个ACE_Message_Queue。ACE_Message_Queue是ACE中的一种标准组件,用于在ACE_Task之间传递信息。而且,当ACE_Task作为主动对象执行时,它的ACE_Message_Queue用于为svc方法中的后续处理缓冲一系列数据消息和控制消息。在消息到达时,svc方法使消息出队,并执行ACE_Task子类的应用特有的处理任务。
有两种消息可出现在ACE_Message_Queue中:简单的和复合的。简单消息包含单个ACE_Message_Block,而复合消息包含多个链接在一起的ACE_Message_Block。复合消息通常由一个控制块和一或多个跟随的数据块组成。控制块包含有“簿记”信息(比如目的地址和长度域),而数据块则包含消息的实际内容。通过传递消息指针而不是拷贝数据,使得在Task间传递消息的开销降到了最低。
ACE_Message_Queue含有一对高低水位标变量,用于在流中相邻的ACE_Module之间实现层到层的流控制。高水位标指示ACE_Message_Queue在它进行流控制前所想要缓存的消息的字节数。低水位标指示表明先前已进行流控制的ACE_Message_Queue不再被认为是“充满”时的“水位”。

32/3<123>
 

评分:0

我来说两句

seccode