歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> Linux文化

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;
}


Copyright © Linux教程網 All Rights Reserved