內核對象,也就是Kobj,為操作系統內核提供了一種面向對象的C語言編程方式。被操作的數據也承載操作它的方法。這使得在不破壞二進制兼容性的前提下,某一個接口能夠增/減相應的操作。Kobj工作時,產生方法的描述。每個描述有一個唯一的標識和一個缺省函數。某個描述的地址被用來在一個類的方法表裡唯一的標識方法。 構建一個類,就是要建立一張方法表,並將這張表關聯到一個或多個函數(方法);這些函數(方法)都帶有方法描述。使用前,類要被編譯。編譯時要為這個類分配一些緩存。在方法表中的每個方法描述都會被指派一個唯一的標識,除非已經被其它引用它的類在編譯時指派了標識。對於每個將要被使用的方法,都會由腳本生成一個函數(方法查找函數),以解析外來參數,並在被查詢時給出方法描述的地址。被生成的函數(方法查找函數)憑著那個方法描述的唯一標識按Hash的方法查找對象的類的緩存。如果這個方法不在緩存中,函數會查找使用類的方法表。如果這個方法被找到了,類裡的相關函數(也就是某個方法的實現代碼)就會被使用。否則,這個方法描述的缺省函數將被使用。 這些過程可被表示如下: 對象->緩存<->類
如何使用Kobj
結構
struct kobj_method
函數
void kobj_class_compile(kobj_class_t cls);
void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);
void kobj_class_free(kobj_class_t cls);
kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);
void kobj_init(kobj_t obj, kobj_class_t cls);
void kobj_delete(kobj_t obj, struct malloc_type *mtype);
宏
KOBJ_CLASS_FIELDS
KOBJ_FIELDS
DEFINE_CLASS(name, methods, size)
KOBJMETHOD(NAME, FUNC)
頭文件
建立一個接口的模板
使用Kobj的第一步是建立一個接口。建立接口包括建立模板的工作。建立模板可用腳本src/sys/kern/makeobjops.pl完成,它會產生申明方法的頭文件和代碼,腳本還會生成方法查找函數。 在這個模板中如下關鍵詞會被使用: #include, INTERFACE, CODE, METHOD, STATICMETHOD, 和 DEFAULT. #include語句的整行內容將被一字不差的復制到被生成的代碼文件的頭部。
例如: #include 關鍵詞INTERFACE用來定義接口名。這個名字將與每個方法名接合在一起,形成 [interface name]_[method name]。語法是:INTERFACE [接口名]; 例如: INTERFACE foo; 關鍵詞CODE會將它的參數一字不差的復制到代碼文件中。語法是CODE { [任何代碼] }; 例如:
CODE {
struct foo * foo_alloc_null(struct bar *)
{
return NULL;
}
};
關鍵詞METHOD用來描述一個方法。語法是: METHOD [返回值類型] [方法名] { [對象 [, 參數若干]] }; 例如:
METHOD int bar {
struct object *;
struct foo *;
struct bar;
};
關鍵詞DEFAULT跟在關鍵詞METHOD之後,是對關鍵詞METHOD的補充。它給這個方法補充上缺省函數。語法是: METHOD [返回值類型] [方法名] { [對象; [其它參數]] }DEFAULT [缺省函數]; 例如:
METHOD int bar {
struct object *;
struct foo *;
int bar;
} DEFAULT foo_hack;
關鍵詞STATICMETHOD類似關鍵詞METHOD。對於每個Kobj對象,一般其頭部都有一些Kobj專有的數據。METHOD定義的方法就假設這些專有數據位於對象頭部;假如對象頭部沒有這些專有數據,這些方法對這個對象的訪問就可能出錯。而STATICMETHOD定義的對象可以不受這個限制:這樣描述出的方法,其操作的數據不由這個類的某個對象實例給出,而是全都由調用這個方法時的操作數(譯者注:即參數)給出。這也對於在某個類的方法表之外調用這個方法有用。
其它完整的例子:
src/sys/kern/bus_if.m
src/sys/kern/device_if.m
建立一個類
使用Kobj的第二步是建立一個類。一個類的組有名字、方法表;假如使用了Kobj的“對象管理工具”(Object Handling Facilities),類中還包含對象的大小。建立類時使用宏DEFINE_CLASS()。建立方法表時,須建立一個kobj_method_t數組,用NULL項結尾。每個非NULL項可用宏KOBJMETHOD()建立。 例如:
DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));
kobj_method_t foomethods[] = {
KOBJMETHOD(bar_doo, foo_doo),
KOBJMETHOD(bar_foo, foo_foo),
{ NULL, NULL}
};
類須被“編譯”。根據該類被初始化時系統的狀態,將要用到一個靜態分配的緩存和“操作數表”(ops table,譯者注:即“參數表”)。這些操作可通過聲明一個結構體struct kobj_ops並使用kobj_class_compile_static(),或是只使用kobj_class_compile()來完成。
建立一個對象
使用Kobj的第三步是定義對象。Kobj對象建立程序假定Kobj專有數據在一個對象的頭部。如果不是如此,應當先自行分配對象,再使用kobj_init()初始化對象中的Kobj專有數據;其實可以使用kobj_create()分配對象,並自動初始化對象中的Kobj專有內容。kobj_init()也可以用來改變一個對象所使用的類。 將Kobj的數據集成到對象中要使用宏KOBJ_FIELDS。 例如:
struct foo_data {
KOBJ_FIELDS;
foo_foo;
foo_bar;
};
調用方法
使用Kobj的最後一部就是通過生成的函數調用對象類中的方法。調用時,接口名與方法名用'_'接合,而且全部使用大寫字母。 例如,接口名為foo,方法為bar,調用就是: [返回值 = ] FOO_BAR(對象 [, 其它參數]);
當一個用kobj_create()不再需要被使用時,可對這個對象調用kobj_delete()。當一個類不再需要被使用時,可對這個類調用kobj_class_free()。