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

OSKit的部件對象模型 第三章


第三章 OSKIT中COM機制的實現

3.1 OSKIT中COM的基本定義

在OSKIT中有關COM的基本定義在com.h中,在該文件中定義了OSKIT中的iid,COM的調用規范,以及OSKIT中的Iunknown接口。

OSKIT中GUID的定義如下:
struct oskit_guid {
   oskit_u32_t  data1;
   oskit_u16_t  data2;
   oskit_u16_t  data3;
   oskit_u8_t   data4[8];
};
其中oskit_u32_t、oskit_u16_t、oskit_u8_t分別是32位、16位、8位無符號整形。OSKIT使用這樣的定義而不使用例如win32中的DWORD有兩方面的原因:首先,這樣定義使OSKIT保持一致的風格,向開發人員呈現出一致的命名規則。其次,如果oskit在win32或相似的環境中使用,能避免與實際的WIN32頭文件沖突。

OSKIT中的調用規范如下:
#define OSKIT_COMCALL   OSKIT_STDCALL
#define OSKIT_COMDECL   oskit_error_t  OSKIT_COMCALL
#define OSKIT_COMDECL_U  oskit_u32_t   OSKIT_COMCALL
#define OSKIT_COMDECL_V  void       OSKIT_COMCALL
其中,OSKIT_STDCALL是在compile.h中定義的有關編譯器設置的宏,這個宏在OSKIT安裝前的configure步驟中被賦值(configure步驟請見第一章)。OSKIT這樣定義調用規范,是為了用不同的編譯器都可以編譯OSKIT,而不必在編寫程序時考慮這些調用的細節。而OSKIT_COMDECL就是帶錯誤返回碼的調用,OSKIT_COMDECL_U是返回值為32位無符號整型的調用,OSKIT_COMDECL_V是不帶返回值的調用。

OSKIT中Iunknown接口的定義如下:
struct oskit_iunknown
{
   struct oskit_iunknown_ops *ops;
};
typedef struct oskit_iunknown oskit_iunknown_t;
struct oskit_iunknown_ops
{ / * 查詢對象是否支持某個特定的接口,若支持則返回該接口的指針*/
   OSKIT_COMDECL (*query)(oskit_iunknown_t *obj,
   const oskit_iid_t *iid, void **out_ihandle);
   /* 增加某個接口的引用計數*/
   OSKIT_COMDECL_U (*addref)(oskit_iunknown_t *obj);
   /* 將對某個接口或對象的引用計數減1,若引用計數為0,則釋放該接口或對象 */
   OSKIT_COMDECL_U (*release)(oskit_iunknown_t *obj);
};
其中,query的參數中obj是要查詢的COM對象,iid是要查詢的接口的全局標識符,若查詢成功,out_ihandle返回指向被查詢接口的指針,若查詢失敗,則返回空指針。

上面可以看到,oskit_iunknown結構中存放著指向函數表格的指針,稱為ops,而函數表格是通過一個稱為oskit_iunknown_ops的結構實現的,這個結構中定義了指向該接口所實現的所有成員函數的指針。只要對該結構進行賦值,就可以通過這個結構調用該接口的所有成員函數。

由於Iunknown接口是由各個COM對象自己實現的,因此在COM.H中沒有對該結構進行賦值,後面章節中將會詳細剖析OSKIT中一個COM對象的實例,那時再對該結構的賦值進行討論。

COM.H還通過宏定義對Iunknown接口的成員函數進行了包裝,如下所示:

#define oskit_iunknown_query(obj, iid, out_ihandle) \
 ((obj)->ops->query((oskit_iunknown_t *)(obj), (iid),
 (out_ihandle)))
#define oskit_iunknown_addref(obj) \
 ((obj)->ops->addref((oskit_iunknown_t *)(obj)))
#define oskit_iunknown_release(obj) \
 ((obj)->ops->release((oskit_iunknown_t *)(obj)))

這使開發人員可以對它們的調用更加方便。此外,順便提一句,OSKIT的一大特色就是對宏定義的靈活使用。在這方面值得大家借鑒。

3.2 實例分析Iunknown接口的實現

OSKIT中並不是每個接口都允許被引用多次,比如內存接口,當系統初始化時便對定義好的靜態內存結構進行賦值,然後所有對內存的操作都依賴於該內存接口,也就是說,在該內存接口中不存在實現Iunknown接口的問題。但也有可以存在可以引用多次的接口,如流接口(stream),C環境接口(OSKIT為核心提供了一個最小的C庫,開發這也可以使用自己定義的C庫加以替換)。本節就以C環境接口為例分析OSKIT中Iunknown接口的實現。

3.2.1 C環境對象的結構

OSKIT中C環境對象的結構如下:
struct genv {
   oskit_libcenv_t   libcenvi; /* C環境接口 */
   int         count; /* 引用計數 */
   oskit_fsnamespace_t *fsn;
   char         hostname[256];
   oskit_ttystream_t  *console;
   void         (*exit)(int);
   void         (*siginit)(int (*func)(int, int, void *));
   #ifndef PTHREADS
   oskit_timer_t    *sleep_timer;
   oskit_osenv_sleep_t *sleep_iface;
   osenv_sleeprec_t   *sleeprec;
   #endif
};

3.2.2 C環境接口及其對Iunknown接口的實現

C環境接口是在libcenv.h中定義的,為了讓大家對OSKIT中COM接口有一個完整的認識,現將整個接口的說明列出:

struct oskit_libcenv {
   struct oskit_libcenv_ops *ops; /* 指向函數表格的指針 */
};
typedef struct oskit_libcenv oskit_libcenv_t;

下面是函數表格:
struct oskit_libcenv_ops {
/* 對Iunknown接口的繼承 */
OSKIT_COMDECL_IUNKNOWN(oskit_libcenv_t)
/* 獲得及設置根文件系統 */
OSKIT_COMDECL (*getfsnamespace)(oskit_libcenv_t *s,
    oskit_fsnamespace_t **out_dir);
OSKIT_COMDECL (*setfsnamespace)(oskit_libcenv_t *s,
    oskit_fsnamespace_t *dir);
/* 設置及獲得主機名 */
OSKIT_COMDECL (*gethostname)(oskit_libcenv_t *s,
    char *hostname, int len);
OSKIT_COMDECL (*sethostname)(oskit_libcenv_t *s,
    const char *hostname, int len);
/* 調用及設置退出函數 */
OSKIT_COMDECL_V (*exit)(oskit_libcenv_t *s,
oskit_u32_t exitval);
OSKIT_COMDECL (*setexit)(oskit_libcenv_t *s,
void (*exitfunc)(int));
/* 設置及獲得終端對象 */
OSKIT_COMDECL (*getconsole)(oskit_libcenv_t *s,
    oskit_ttystream_t **out_ttystream);
OSKIT_COMDECL (*setconsole)(oskit_libcenv_t *s,
    oskit_ttystream_t *ttystream);
/* 初始化信號庫 */
OSKIT_COMDECL (*signals_init)(oskit_libcenv_t *s,
    int (*func)(int, int, void *));
OSKIT_COMDECL (*setsiginit)(oskit_libcenv_t *s,
    void (*sigfunc)(int (*func)(int,int,void *)));
/* 睡眠/喚醒接口 */
OSKIT_COMDECL_V (*sleep_init)(oskit_libcenv_t *s,
    osenv_sleeprec_t *sleeprec);
OSKIT_COMDECL_U (*sleep)(oskit_libcenv_t *s,
osenv_sleeprec_t *sleeprec,
struct oskit_timespec *timeout);
OSKIT_COMDECL_V (*wakeup)(oskit_libcenv_t *s,
    osenv_sleeprec_t *sleeprec);
/* 克隆整個對象 */
OSKIT_COMDECL (*clone)(oskit_libcenv_t *s,
oskit_libcenv_t **intf);
};

下面是定義C環境接口的iid
extern const struct oskit_guid oskit_libcenv_iid;
#define OSKIT_LIBCENV_IID OSKIT_GUID(0x4aa7dfe9, 0x7c74, 0x11cf, 0xb5, 0x00, 0x08, 0x00, 0x09, 0x53, 0xad, 0xc2)

下面是對C庫接口成員函數的包裝,因為長度緣故,只寫出部分:
#define oskit_libcenv_query(s, iid, out_ihandle) \
((s)->ops->query((oskit_libcenv_t *)(s), (iid), (out_ihandle)))
#define oskit_libcenv_addref(s) \
((s)->ops->addref((oskit_libcenv_t *)(s)))
#define oskit_libcenv_release(s) \
((s)->ops->release((oskit_libcenv_t *)(s)))

下面是C庫接口的實現:
static struct genv default_libcenv;
/* 定義靜態的C環境對象 */

在OSKIT中,經常在初始化時大量使用靜態定義,因為這樣定義出來的結構在編譯時就分配好了空間,使系統在啟動時就可以使用這些結構,而不需要等到內存對象初始化完之後再動態分配空間。

Query 成員函數的流程圖見下一頁:



下面是Query成員函數的實現:
static OSKIT_COMDECL
libcenv_query(oskit_libcenv_t *s, const oskit_iid_t *iid,
void **out_ihandle)
{
struct genv *g = (struct genv *) s;
/* 將指向C庫環境接口的指針轉化為指向C庫環境對象的指 */
if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 ||
    memcmp(iid, &oskit_libcenv_iid, sizeof(*iid)) == 0)
/* 通過比較要查詢的iid和iunknown接口的iid以及c庫環境接口的iid
相比較,如果相等,則表明該C庫環境對象支持該接口 */
  { *out_ihandle = &g->libcenvi; /*返回指向C環境接口的指針*/
    g->count++; /*將引用計數加1*/
    return 0;
  }
  *out_ihandle = 0;
  return OSKIT_E_NOINTERFACE; /* 如果找不到,則返回出錯信息 */
};
/* 增加引用計數 */
static OSKIT_COMDECL_U libcenv_addref(oskit_libcenv_t *s)
{ /* 將C庫環境的接口指針轉化為C庫環境對象指針 */
  struct genv *g = (struct genv *) s;
  assert(g->count);
  return ++g->count; /*增加引用計數*/
}

Release 函數的流程見下一頁:



/* 釋放引用計數 */
libcenv_release(oskit_libcenv_t *s)
{ /* 將C庫環境接口指針轉化為C庫環境對象指針 */
  struct genv g = (struct genv ) s;
  assert(g->count);
/* 以下語句先將對象的引用計數減1,然後判斷計數是否為0,若是則釋
放對與其相關的接口的引用*/
  if (--g->count == 0) {
    if (g->fsn) oskit_fsnamespace_release(g->fsn);
    if (g->console) oskit_ttystream_release(g->console);
  #ifndef PTHREADS
    if (g->sleep_timer)
      oskit_timer_release(g->sleep_timer);
    if (g->sleep_iface)
      oskit_osenv_sleep_release(g->sleep_iface);
  #endif
    sfree(g, sizeof(g));
  }
  return g->count; /*返回該對象的引用計數*/
}

現在對上面Iunknown接口的三個成員函數的實現思路作一定的解釋。在OSKIT中,客戶所能使用的只是對象提供給用戶的接口指針,當用戶在該接口中調用Query方法時,例如libcenv_query,並對該對象中的某個接口進行查詢,這時,由於對象是否實現所查詢接口是在對象結構中定義的,因此首先把接口指針進行強制類型轉換,變為指向接口所屬對象的指針,然後再將所要查詢的接口iid與對象所實現的所有接口的iid逐一比較,如果與其中某一個接口的iid相等,則返回指向該接口的指針,如果無一相等,則返回錯誤信息。當用戶使用addref方法時,同樣也是先進行類型轉換,然後將這個對象中的引用計數加1,若用戶調用release方法,在進行類型轉換後,將對象的引用計數減1,如果發現引用計數位0,則將釋放整個對象的內存空間。


Copyright © Linux教程網 All Rights Reserved