Ruby/DL为UNIX的dlopen(3)或Windows的LoadLibrary()等dynamic linker提供接口。
通常使用dl/import.rb的 DL::Importable 模块。其中包含了用来访问库函数的高水准函数。在扩展某模块时, 要像下面这样使用 DL::Importable 。
require "dl/import"
module LIBC
extend DL::Importable
end
此后,就可以使用该模块的dlload和extern方法了。可以像下面这样, 使用dlload来加载库, 然后分别对库函数使用extern来定义封装方法。
module LIBC
extend DL::Importable
dlload "libc.so.6","libm.so.6"
extern "int strlen(char*)"
end
# Note that we should not include the module LIBC from some reason.
使用了LIBC.strlen之后, 就可以使用库函数strlen()了. 若给出的函数名的首字母是大写的话, 所定义的函数名的首字母就是小写.
可以像下例这样, 使用了dl/struct.rb中定义的struct或union函数之后, 就可以生成结构体或共用体的映像(memory image)了.
require "dl/import"
require "dl/struct"
module LIBC
extend DL::Importable
Timeval = struct [ # define timeval structure.
"long tv_sec",
"long tv_uses",
]
end
val = LIBC::Timeval.malloc # allocate memory.
请注意, 上例中分配内存时使用的是LIBC::Timeval.malloc, 而不是LIBC::Timeval.new。LIBC::Timeval.new的作用是将已生成的PtrData 对象封装起来。
可以像下例中这样, 使用模块函数callback来定义回叫(callback)。
module Foo
extend DL::Importable
def my_comp(str1,str2)
str1 <=> str2
end
COMPARE = callback "int my_comp(char*,char*)"
end
这里的Foo::COMPARE是用来启动my_comp方法的Symbol 对象。
DL::Importable 模块非常易用。但有时也不得不直接使用像dlsym()这样的低级函数。这时应该使用DL 模块的函数。我们将在下节中谈到这个问题。
模块DL由3个包含若干模块函数和常数的类构成。Symbol类相当于可调用的符号。PtrData类用来表示像C的指针这样的内存块。由Handle类生成的对象负责操作已打开的库。
- RTLD_GLOBAL
- RTLD_LAZY
- RTLD_NOW
- MAX_ARG
- MAX_CBARG
- MAX_CBENT
- handle = dlopen(lib){|handle| ... }
- sym = set_callback(cbtype, entry){|args| ... }
- sym = set_callback(cbtype, entry, proc)
- 生成用来调用proc或指定块的entry-th pre-defined function。entry-th pre-defined function由cbtype以及entry来指定。cbtype是回叫(callback)的原型。关于cbtype的问题,请参考`Type specifiers' 。
- sym = get_callback(cbtype, entry)
- 返回上面的`set_callback'函数给出的Proc 对象。
- ptr = malloc(size, [free = nil])
- 分配出size字节的内存空间, 以ptr的形式返回PtrData 对象。
- ptr = strdup(str)
- 返回PtrData对象, 它对应于指向str字符串的新拷贝的指针。
- size = sizeof(type)
返回类型的大小。`sizeof("C") + sizeof("L")'并不得等价于`sizeof("CL")'。一般认为后者会为 `struct foo { char c;
long l; }'返回足够的大小, 而该大小可能有别于C语言的`sizeof(foo)'的返回值。
(译注: sizeof方法会自己考虑alignment问题从而决定类型的大小, 这可能有别与C语言中的相关方法)
(译注: 可以像sizeof("L3")这样在类型后面添加数字. 请参考Type specifiers来了解可用的类型)
- handle = Handle.new(lib){|handle| ... }
- 打开lib库, 返回Handle对象--handle。若指定了块的话, (译注:将打开的handle传给块参数, 再执行块的内容) 块运行结束后会自动关闭handle。
- Handle#close
- 关闭上面的Handle.new(lib)打开的handle。
- sym = Handle#sym(func, prototype = "0"),
sym = Handle#[func, prototype = nil]
- 取得指向func函数(译注:或许是全局变量?)的指针, 返回Symbol对象或DataPtr对象。prototype是由类型分类符构成的字符串,它表示函数的原型。请参考Type specifiers。
- sym = Symbol.new(addr, type = nil, name = nil)
- 若type非nil, 则生成类型为type的Symbol对象--sym 。addr是地址。若type为nil, 则返回DataPtr 对象。
- Symbol::char2type(char)
- 取得与类型相应的字符char, 返回C语言的类型分类。
- str = Symbol#proto()
- str = Symbol#name()
- str = Symbol#cproto(),
str = Symbol#to_s()
- str = Symbol#inspect()
- r,rs = Symbol#call(arg1,arg2,...,argN),
r,rs = Symbol#[](arg1,arg2,...,argN)
- 以参数arg1, arg2, ... argN来调用函数。结果由返回值r以及参数rs构成。rs是数组。
- ptr = Symbol#to_ptr
- ptr = PtrData.new(addr, [size = 0, free = nil])
- 返回PtrData对象, 它对应于指向地址addr的指针。GC使用free函数来释放内存空间。
- PtrData#free=(sym)
- 若指定了符号对象sym时, GC使用对应于sym的函数来释放内存空间。
- sym = PtrData#free
- 返回GC释放内存时使用的符号对象sym 。通常情况下, sym 由 `PtrData#free='或`PtrData.new'来设定。
- size = PtrData#size, PtrData#size=(size)
- ary = PtrData#to_a(type, [size])
- 返回由type指定的类型的数组。type可以是 'S','P','I','L','D' 或 'F' 中的任何一个。
- str = PtrData#to_s([len])
- 返回长度为len的字符串。若省略len时, 字符串末尾是'\0'。
- ptr = PtrData#ptr,+@
- 以 PtrData对象ptr来返回指针所指的值。(译注:?)(译注:把PtrData所指的值看做是指针,然后以PtrData返回(?))
- ptr = PtrData#ref,-@
- 以PtrData对象ptr来返回引用。(译注:?)(译注:把PtrData当做指向PtrData的引用,然后返回(?))
(译注:把PtrData当做指向PtrData的指针,然后返回(?))
- ptr = PtrData#+
- 返回PtrData 对象。(译注: 增加了参数的字节数后,返回这个新的PtrData对象)
- ptr = PtrData#-
- 返回PtrData 对象。(译注: 缩减了参数的字节数后,返回这个新的PtrData对象)
- PtrData#struct!(type, *members)
- 定义使用符号访问结构体成员的数据类型(请参考PtrData#[])
- PtrData#union!(type, *members)
- 定义使用符号访问共同体成员的数据类型(请参考PtrData#[])
- val = PtrData#[key], PtrData#[key, num = 0]
- 若key是字符串或符号, 则该方法返回结构体/共同体成员的值。它包含PtrData#{struct!,union!}中定义的类型。若key是整数且该对象对应于指针ptr的话, 则返回`(ptr + key).to_s(num)'的值.
- PtrData#[key,num]=val, PtrData#[key]=val
- 若key是字符串或符号, 则该方法将结构体/共同体成员的值设为val。若key是整数且val是字符串时, 使用memcpy(3)将值中的num 字节拷贝到内存空间ptr中。
原型是由下列类型分类符构成的。原型的首元素表示返回值的类型, 其余元素表示各参数的类型。
C : 字符 (char)
c : 指向字符的指针 (char *)
H : short 整数 (short)
h : 指向short整数的指针 (short *)
I : 整数 (char, short, int)
i : 指向整数的指针 (char *, short *, int *)
L : long 整数 (long)
l : 指向long整数的指针 (long *)
F : 实数 (float)
f : 指向实数的指针 (float *)
D : 实数 (double)
d : 指向实数的指针(double *)
S : 不可变(immutable)字符串 (const char *)
s : 可变(mutable)字符串 (char *)
A : 数组(const type[])
a : 可变(mutable)数组 (type[])
P : 指针 (void *)
p : 可变(mutable)指针 (void *)
0 : void 函数(必须是原型的首字符)
cbtype由类型分类符0, C, I, H, L, F, D, S 以及 P 构成。例如:
DL.callback('IPP'){|ptr1,ptr2|
str1 = ptr1.ptr.to_s
str2 = ptr2.ptr.to_s
str1 <=> str2
}