当前位置:Linux教程 - Linux文化 - OSKit的部件对象模型 第四章

OSKit的部件对象模型 第四章



第四章 OSKIT中的接口注册机制

4.1 概述

为了管理OSKIT中的接口,OSKIT系统引入了接口注册的机制。所谓接口注册,就是在系统中建立一个服务数据库,里面记载了所有在该数据库中注册的接口信息,包括对象名、接口的iid、对象的引用计数等。系统提供给用户一个指向服务接口的指针,通过该指针,用户可以实现创建自己的数据库,在某个数据库中注册接口,在数据库中注销先前注册的某个接口,获得用某个iid注册的第一个接口,获得用某个iid注册的所有接口。系统在初始化时自动创建了一个特殊的数据库,称为全局数据库,在该数据库中注册了许多对于系统来说十分关键的接口,例如C库环境接口,内存接口,并提供了一些其它的函数以方便对全局数据库的操作,如直接将接口注册入全局数据库,以及直接在全局数据库中进行接口查询等。通过这些函数,系统能快捷方便地管理全局数据库。

4.2 数据库对象

OSKIT中的数据库对象是在service.c中定有的,其定义如下:

struct db {
  oskit_services_t  servi; /* 服务接口 */
  int       count; /* 引用计数 */
  struct iidnode  *iids; /* iid链指针 */
  oskit_mem_t   *memi; /* 所要使用的内存对象指针*/
};

其中用到的iidnode结构如下:

struct iidnode {
  struct iidnode *next; /*下一个iid节点*/
  oskit_guid_t  iid; /*全局iid*/
  struct objnode *objs; /*对象节点指针*/
  int objconut /*对象引用计数*/
};

而objnode的定义如下:

struct objnode {
  struct objnode  *next; /*下一个objnode节点*/
  oskit_iunknown_t *intf; /*iunknown接口类型指针*/
};

由此,可以得出该数据库的结构图为:

4.3 服务接口的定义

服务接口定义如下:

struct oskit_services {
  struct oskit_services_ops *ops; /*指向函数表格的指针*/
};
typedef struct oskit_services oskit_services_t;
struct oskit_services_ops {
/*接口查询*/
OSKIT_COMDECL (*query)(oskit_services_t *s,
    const struct oskit_guid *iid,void **out_ihandle);
/*增添引用计数*/
OSKIT_COMDECL_U (*addref)(oskit_services_t *s);
/*释放引用计数*/
OSKIT_COMDECL_U (*release)(oskit_services_t *s)
/*在数据库中注册接口*/
OSKIT_COMDECL (*addservice)(oskit_services_t *s,
    const struct oskit_guid *iid, void *intf);
/*在数据库中撤销先前注册接口*/
OSKIT_COMDECL (*remservice)(oskit_services_t *s,
    const struct oskit_guid *iid, void *intf);
/*在数据库中查询以某个iid注册的所有接口*/
OSKIT_COMDECL (*lookup)(oskit_services_t *s,
constoskit_guid_t *iid,void ***out_interface_array);
/*在数据库中查询以某个iid注册的第一个接口*/
OSKIT_COMDECL (*lookup_first)(oskit_services_t *s,
    const oskit_guid_t *iid, void **out_intf);
/*克隆整个数据库*/
OSKIT_COMDECL(*clone) (oskit_services_t*s,
oskit_services_t **intf);
};

另外在OSKIT中,还为操作全局数据库提供了一些函数,它们的定义如下;

/*创建全局数据库*/
oskit_error_t oskit_global_registry_create
    (structoskit_mem*memobject);
/*获得全局数据库*/
oskit_services_t *oskit_get_services(void);
/*在全局数据库中注册接口*/
oskit_error_t oskit_register(const struct oskit_guid *iid,
    void*interface);
/*在全局数据库中撤销先前注册的接口*/
oskit_error_t oskit_unregister(const struct oskit_guid
    *iid,void*interface);
/*查询用某个iid注册的所有接口*/
oskit_error_t oskit_lookup(const oskit_guid_t *iid, void
    ***out_interface_array);
/*查询用某个iid注册的第一个接口*/
oskit_error_t oskit_lookup_first(const oskit_guid_t *iid,
    void**out_interface);

4.4 数据库服务接口的实现

4.4.1 创建数据库

OSKIT中创建数据库的实现思路是:使用某个内存对象分配出数据库对象所需的存储空间,然后创建数据库,给数据库对象的每个成员赋值,最后返回数据库的服务接口指针。其流程图见下一页:



其实现的源代码如下:

oskit_error_t oskit_services_create
(oskit_mem_t *memi, oskit_services_t **out_intf)
{  struct db *s; /*定义数据库对象*/
  /*如果没有提供内存对象,则在全局数据库中查找*/
  if (!memi) {
    /*在全局数据库中查找第一个内存对象*/
    oskit_lookup_first(&oskit_mem_iid, (void*) &memi);
    if (!memi)
      panic("oskit_services_create:Null memory object!");
    }
    /* 给数据库对象分配内存空间 */
    s = oskit_mem_alloc(memi, sizeof(*s), 0);
    if (s == NULL)
      return OSKIT_E_OUTOFMEMORY;
    s->count = 1; /*给引用计数赋值*/
    s->memi = memi; /*给内存对象指针赋值*/
    /*给数据库对象中的服务接口指针赋值*/
    s->servi.ops = &services_ops;
    s->iids = 0;
    oskit_mem_addref(memi);
    /* 在全局数据库中增加该内存对象的引用计数 */
    *out_intf = &s->servi; /*返回服务接口指针*/
    return 0;
}

4.4.2 在数据库中注册接口

OSKIT在数据库中注册接口的过程是这样的,首先将服务接口指针转换为数据库对象指针,然后根据要注册的接口的iid,在数据库中查找已经注册的iid,若该iid已注册,则在该iid下查找用这个iid注册的接口名,如果找到则返回,否则,生成一个新的obj节点,并将要注册的接口赋给obj节点中的接口指针。如果要注册的iid找不到,则生成一个新的iid节点,在该iid节点下新建一个obj节点,并对它们进行赋值。在程序执行过程中,如果在分配内存时发现内存不够,则返回一个内存不足错误,由调用程序来处理。下面是注册接口的程序流程:



下面是注册接口的源程序:

参数:si:数据库指针
   iid:需要进行注册的全局iid指针
   interface:需要进行注册的接口指针
OSKIT_COMDECL services_addservice(oskit_services_t *si,
     const struct oskit_guid *iid, void *interface)
{  /*将服务接口指针转换为数据库指针*/
  struct db *s = (struct db *) si;
  /*将需要注册的接口指针转换为iunknown接口的指*/
  oskit_iunknown_t *iu = (oskit_iunknown_t*)interface;
  struct iidnode *in; /*定义iid节点指针*/
  struct objnode *on, **onp; /*定义obj节点指针*/
  /* 查找或创建相应的iid节点 */
  for (in = s->iids; ; in = in->next) {
    if (in == NULL) {
      /*如果没有找到,则创建新的iid节点*/
      in = oskit_mem_alloc(s->memi, sizeof(*in), 0);
      if (in == NULL) /*分配内存失败*/
        return OSKIT_E_OUTOFMEMORY; /*返回内存不足错误*/
      in->iid = *iid;
      in->objs = NULL;
      in->objcount = 0;
      in->next = s->iids;/*在iid链中插入该iid节点*/
      s->iids = in;
      break;
    }
    if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
    break; /*找到了该iid节点,退出循环*/
  }
  /* 在该iid的obj链中查找要注册的接口*/
  for (onp = &in->objs; *onp; onp = &(*onp)->next) {
    if ((*onp)->intf == interface)
      return 0; /*找到后,直接返回*/
  }
  /* 为这个接口创建一个新的obj节点*/
  on = oskit_mem_alloc(s->memi, sizeof(*on), 0);
  if (on == NULL)
    return OSKIT_E_OUTOFMEMORY; /*返回内存不足错误*/
  on->next = NULL;
  /*增添对该接口的引用计数*/
  on->intf = iu; oskit_iunknown_addref(iu);
  *onp = on;
  in->objcount++; /*增加该iid节点的接口计数*/
  return 0;
}

4.4.3 在数据库中注销接口

在数据库中注销接口所做的工作与注册接口相反:首先,在数据库中找到该iid节点,然后在该iid节点的接口链上查找所要注销的接口节点,如果找到,将其释放并对需要改动的数据库参数进行修改。在这过程中,如果发现iid节点或要释放的接口节点没找到则返回无效错误。

下面是在数据库中注销节点的源程序:

参数与在数据库中注册节点一致。

OSKIT_COMDECL services_remservice(oskit_services_t *si,
      const struct oskit_guid *iid, void *interface)
{  /*将服务接口指针转换为数据库指针*/
  struct db *s = (struct db *) si;
  struct iidnode *in; /*定义iid节点指针*/
  struct objnode *on, **onp; /*定义接口节点指针*/
  /* 找到相应的iid节点*/
  for (in = s->iids; ; in = in->next) {
    if (in == NULL)
      return OSKIT_E_INVALIDARG;/*找不到,返回无效错误*/
    if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
      break;
  }
  /* 找到并删除需要注销的接口节点*/
  for (onp = &in->objs; ; onp = &on->next) {
    on = *onp;
    if (on == NULL)
      return OSKIT_E_INVALIDARG; /*没找到接口,返回无效错误*/
    if (on->intf == interface)
      break;
  }
  *onp = on->next; /* 将该接口节点从接口节点链中删除 */
  oskit_iunknown_release(on->intf); /* 释放对该接口的引用 */
  /* 释放该接口节点占所占内存 */
  oskit_mem_free(s->memi, (void *) on, sizeof(*on), 0);
  in->objcount--; /* 将这个iid的接口引用计数减1 */
  return 0;
}

4.4.4 查找用某iid注册的第一个接口

在某些时候,系统或用户需要得到用指定iid注册的接口,因此系统提供了lookupfirst和lookup函数。Lookupfirst是用来查找以指定iid注册的第一个接口,如果找到,则返回该接口的指针。其工作过程是:首先,将服务接口指针转换为数据库对象指针。然后,在数据库的iid链中查找相应的iid节点,如果找到,则返回该iid节点的接口链中第一个接口指针,否则就返回空指针。其程序流程见下一页:

下面是该函数的源程序:

参数: si:数据库指针
   iid:指定的iid
   out_intf:找到的接口指针,用于返回
OSKIT_COMDECL services_lookup_first(oskit_services_t *si,
      const oskit_guid_t *iid, void **out_intf)
{
  /*将服务接口指针转换为数据库指针*/
  struct db *s = (struct db ) si;
  struct iidnode *in; /*iid节点指针*/
  oskit_iunknown_t *intf; /*用于返回的接口指针*/
  /* 查找相应的iid节点*/
  for (in = s->iids; ; in = in->next) {
  if (in == NULL) {
  *out_intf = NULL; /*找不到,返回空指针*/
    return 0;
  }
  if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
    break; /*找到该节点,退出循环*/
  }
  if (in->objcount == 0) {
    *out_intf = NULL; /*该iid节点下无注册接口,返回空指针*/
    return 0;
  }
  *out_intf = intf = in->objs->intf; /*返回找到的接口*/
  oskit_iunknown_addref(intf); /*增添的对该接口的引用*/
  return 0;
}

4.4.5 查找用某iid注册的所有接口

系统还提供了函数,在数据库中查找用指定iid注册的所有接口,该函数将用指定的iid注册的所有接口放在一个数组中,并返回该数组的地址。其实现过程是:首先在数据库中找到指定的iid节点,然后根据该iid节点的接口计数给需要返回的接口数组分配内存空间,再将用该iid注册的所有接口指针拷贝到数组中,最后将数组地址赋给out_interface_array,并返回该iid下注册的接口个数。

以下是该函数的源程序:

参数:si:数据库指针
   iid:指定的iid
   out_interface_array:返回的接口数组指针
OSKIT_COMDECL services_lookup(oskit_services_t *si,
      const oskit_guid_t *iid, void ***out_interface_array)
{
  /*将服务接口的指针转换为数据库指针*/
  struct db *s = (struct db *) si;
  struct iidnode *in;
  struct objnode *on;
  void **arr; /*定义存放数组地址的变量*/
  int i;
  /* 找到相应的iid节点*/
  for (in = s->iids; ; in = in->next) {
    if (in == NULL) {
      *out_interface_array = NULL; /*没找到,返回空指针*/
      return 0;
    }
    if (memcmp(&in->iid, iid, sizeof(*iid)) == 0)
      break;
  }
  if (in->objcount == 0) {
    *out_interface_array = NULL; /*若接口数为0,返回空指针*/
    return 0;
  }
  /* 给用于返回接口指针的数组分配内存空间,调用者负责释放该数组所占用
  的内存空间 , 按照接口数分配内存空间 */
  arr = malloc(sizeof(*arr)*in->objcount);
  if (arr == NULL)
    return OSKIT_E_OUTOFMEMORY; /*内存不足,返回错误*/
  /* 将接口填入数组中*/
  for (i = 0, on = in->objs; i < in->objcount; i++, on = on->next){
    assert(on != NULL);
    arr[i] = on->intf; /*给数组赋值*/
    oskit_iunknown_addref(on->intf); /*增添对该接口的引用*/
  }
  assert(on == NULL);
  *out_interface_array = arr; /*将数组地址传递给调用者*/
  return in->objcount; /*返回以该iid注册的接口数*/
}

4.4.6 克隆数据库

OSKIT还提供了整个数据库拷贝的功能,也就是所谓的克隆。该函数首先创建一个数据库,然后在新数据库中将在原数据库中注册的所有接口重新注册一遍,最后返回新数据库对象的服务接口指针,以下是这个函数的实现过程:

参数:si:源数据库对象服务接口指针
   out_intf:新数据库服务接口指针
OSKIT_COMDECL services_clone(oskit_services_t *si,
      oskit_services_t **out_intf)
{
  struct db *s = (struct db *) si; /*获得源数据库对象指针*/
  struct db *ns; /*新数据库对象指针*/
  struct iidnode *in; /*定义iid节点指针*/
  struct objnode *on; /*定义接口节点指针*/
  oskit_error_t rc;
  /*使用源数据库的存储对象给新数据库对象分配内存空间*/
  ns = oskit_mem_alloc(s->memi, sizeof(*ns), 0);
  if (ns == NULL)
    return OSKIT_E_OUTOFMEMORY; /*返回内存不足错误*/
  ns->count = 1;
  ns->memi = s->memi;
  /*将服务接口函数表地址赋给新数据库*/
  ns->servi.ops = &services_ops;
  ns->iids = 0;
  oskit_mem_addref(ns->memi); /*增加对该内存对象的引用计数*/
  in = s->iids;
  while (in) {
    on = in->objs;
    while (on) {
    /*对在源数据库中注册的接口在新数据库中重新注册*/
      if((rc = services_addservice(&ns->servi,&in->iid,
        on->intf))!= NULL) {
          panic("services_clone");
      }
    on = on->next;
    }
  in = in->next;
  }
  *out_intf = &ns->servi; /*返回新数据库的服务接口指针*/
  return 0;
}