第四章 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;
}