--------- driver篇 ---------------
driver一般通过erl_ddll.erl中的load_driver/2函数来实现.
load_driver(Path, Driver) ->
do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
...
do_load_driver(Path, Driver, DriverFlags) ->
case erl_ddll:try_load(Path, Driver,[{monitor,pending_driver}]++DriverFlags) of
注意其中try_load的参数, OptionList为[{monitor,pending_driver}]++DriverFlags, 即 [{monitor,pending_driver}, {driver_options, [kill_ports]}].
详细意义见otp文档的erl_ddll:try_load/3注释.
...
由此可以看出最后还是委托到erl_ddll:try_load/3函数. 这个函数由bif实现, 具体实现在emulator/beam/erl_bif_ddll.c:139行
/*
* try_load(Path, Name, OptionList) -> {ok,Status} |
* {ok, PendingStatus, Ref} |
* {error, ErrorDesc}
* Path = Name = string() | atom()
* OptionList = [ Option ]
* Option = {driver_options, DriverOptionList} |
* {monitor,MonitorOption} |
* {reload, ReloadOption}
* DriverOptionList = [ DriverOption ]
* DriverOption = kill_ports
* MonitorOption = pending_driver | pending
* ReloadOption = pending_driver | pending
* Status = loaded | already_loaded | PendingStatus
* PendingStatus = pending_driver | pending_process
* Ref = ref()
* ErrorDesc = ErrorAtom | OpaqueError
* ErrorAtom = linked_in_driver | inconsistent |
* permanent | pending
*/
/*
* Try to load. If the driver is OK, add as LOADED. If the driver is
* UNLOAD, possibly change to reload and add as LOADED,
* there should be no other
* LOADED tagged pid's. If the driver is RELOAD then add/increment as
* LOADED (should be some LOADED pid). If the driver is not present,
* really load and add as LOADED {ok,loaded} {ok,pending_driver}
* {error, permanent} {error,load_error()}
*/
BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term,
Eterm name_term, Eterm options)
以上是定义.
步骤:
1. 处理options列表, 获取driver名称以及路径.
2. if ((de = lookup_driver(name)) != NULL) {...} 判断是否存在此driver,如果存在则reload它.
3.
3.1 加载driver .
res = load_driver_entry(&dh, path, name)
res = do_load_driver_entry(dh, path, name)
res = erts_sys_ddll_open(path, &(dh->handle)))
此函数通过dlopen调用了一个动态链接库*.so,返回操作此so文件的句柄handle. 以example1_drv为例,此处就是/root/erlang/book_code/src/ports/example1_drv.so
3.2 初始化driver
res = erts_sys_ddll_load_driver_init(dh->handle,
&init_handle)
res = erts_sys_ddll_sym(handle, "driver_init", &fn)
此函数通过 dlsym(handle, func_name)取得了so文件的driver_init函数的函数指针,在此例中即取得了example_lid.c中的
DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
return &example_driver_entry;
}
此函数的指针.
3.3 调用driver_init函数
dp = erts_sys_ddll_call_init(init_handle);
在此即返回了&example_driver_entry这个driver_entry结构体,赋给dp. 然后就可以用dp来干活啦.
3.4 判断driver版本后,把driver加入到driver_list链表中去.
erts_add_driver_entry(dp,1); /* io.c */
4.把当前进程proc加到driver handle的属性中,并重新分配一下堆空间后,再清理些用到的临时资源.返回.
说白了,driver的工作就是调一个*.so,然后找到这个so中的driver_init函数,调用它. 调用后,会返回一driver_entry结构体的实例. 这个结构体定义了driver的启动,停止,处理port command的函数指针, driver名称,超时时间, i/o操作等各种东西.
以后调用driver就是通过这个结构体的属性来指定了.
driver一般通过erl_ddll.erl中的load_driver/2函数来实现.
load_driver(Path, Driver) ->
do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
...
do_load_driver(Path, Driver, DriverFlags) ->
case erl_ddll:try_load(Path, Driver,[{monitor,pending_driver}]++DriverFlags) of
注意其中try_load的参数, OptionList为[{monitor,pending_driver}]++DriverFlags, 即 [{monitor,pending_driver}, {driver_options, [kill_ports]}].
详细意义见otp文档的erl_ddll:try_load/3注释.
...
由此可以看出最后还是委托到erl_ddll:try_load/3函数. 这个函数由bif实现, 具体实现在emulator/beam/erl_bif_ddll.c:139行
/*
* try_load(Path, Name, OptionList) -> {ok,Status} |
* {ok, PendingStatus, Ref} |
* {error, ErrorDesc}
* Path = Name = string() | atom()
* OptionList = [ Option ]
* Option = {driver_options, DriverOptionList} |
* {monitor,MonitorOption} |
* {reload, ReloadOption}
* DriverOptionList = [ DriverOption ]
* DriverOption = kill_ports
* MonitorOption = pending_driver | pending
* ReloadOption = pending_driver | pending
* Status = loaded | already_loaded | PendingStatus
* PendingStatus = pending_driver | pending_process
* Ref = ref()
* ErrorDesc = ErrorAtom | OpaqueError
* ErrorAtom = linked_in_driver | inconsistent |
* permanent | pending
*/
/*
* Try to load. If the driver is OK, add as LOADED. If the driver is
* UNLOAD, possibly change to reload and add as LOADED,
* there should be no other
* LOADED tagged pid's. If the driver is RELOAD then add/increment as
* LOADED (should be some LOADED pid). If the driver is not present,
* really load and add as LOADED {ok,loaded} {ok,pending_driver}
* {error, permanent} {error,load_error()}
*/
BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term,
Eterm name_term, Eterm options)
以上是定义.
步骤:
1. 处理options列表, 获取driver名称以及路径.
2. if ((de = lookup_driver(name)) != NULL) {...} 判断是否存在此driver,如果存在则reload它.
3.
3.1 加载driver .
res = load_driver_entry(&dh, path, name)
res = do_load_driver_entry(dh, path, name)
res = erts_sys_ddll_open(path, &(dh->handle)))
此函数通过dlopen调用了一个动态链接库*.so,返回操作此so文件的句柄handle. 以example1_drv为例,此处就是/root/erlang/book_code/src/ports/example1_drv.so
3.2 初始化driver
res = erts_sys_ddll_load_driver_init(dh->handle,
&init_handle)
res = erts_sys_ddll_sym(handle, "driver_init", &fn)
此函数通过 dlsym(handle, func_name)取得了so文件的driver_init函数的函数指针,在此例中即取得了example_lid.c中的
DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
return &example_driver_entry;
}
此函数的指针.
3.3 调用driver_init函数
dp = erts_sys_ddll_call_init(init_handle);
在此即返回了&example_driver_entry这个driver_entry结构体,赋给dp. 然后就可以用dp来干活啦.
3.4 判断driver版本后,把driver加入到driver_list链表中去.
erts_add_driver_entry(dp,1); /* io.c */
4.把当前进程proc加到driver handle的属性中,并重新分配一下堆空间后,再清理些用到的临时资源.返回.
说白了,driver的工作就是调一个*.so,然后找到这个so中的driver_init函数,调用它. 调用后,会返回一driver_entry结构体的实例. 这个结构体定义了driver的启动,停止,处理port command的函数指针, driver名称,超时时间, i/o操作等各种东西.
以后调用driver就是通过这个结构体的属性来指定了.