实例代码分析
可执行文件 hello.mo
和 库文件 hello.so
的源码在非常简单基本一看即懂,但是其中有一些特殊的点需要特别说明:
可执行文件 .mo
-
rt-thread API 的调用
rt_thread_create
、rt_thread_startup
类似的 API 默认已经使用RTM_EXPORT()
声明,可以直接调用 luban-lite/packages/artinchip/aic-dm-apps/hello/rtt_api_test.c:#include <rtthread.h> void my_thread_entry(void* parameter) { int index = 0; while (1) { rt_kprintf("index => %d\n", index ++); rt_thread_delay(RT_TICK_PER_SECOND); } } int my_thread_init(void) { rt_thread_t tid; tid = rt_thread_create("tMyTask", my_thread_entry, RT_NULL, 2048, 20, 20); if (tid != RT_NULL) rt_thread_startup(tid); return 0; }
- 模块初始化和退出函数如果 dm-app 定义了 module_init() 和 module_cleanup() 函数,会在模块初始化和退出时被自动调用 luban-lite/packages/artinchip/aic-dm-apps/hello/main.c:
void module_init(struct rt_dlmodule *module) { printf("[AIC-DM-APP] init!\n"); } void module_cleanup(struct rt_dlmodule *module) { printf("[AIC-DM-APP] exit!\n"); }
用户可以利用该机制来做一些初始化和清理的工作。如果不需要就不用实现这两个函数。
- 查看
hello.mo
创建的子线程从代码可知我们运行
hello.mo
以后,会创建tMyTask
线程。但是我们使用ps
或者list_thread
命令,却无法看到该线程。这是为什么呢?因为该方式下启动的线程会被链接到模块本身的进程链表
module->object_list
,而上述命令只能查看全局链表information->object_list
中的线程。目前模块本身的进程链表
module->object_list
不支持命令查看,在模块退出时会停止掉module->object_list
中模块启动的所有子进程。 - 后台进程保活
承接上面话题,
hello.mo
的 main() 函数返回后,系统马上会执行模块退出动作,main() 函数创建的所有子进程也会被全部清理。怎么样能让模块的子进程作为后台进程继续运行呢?我们给hello.mo
的 main() 函数定义了一个特殊返回值RT_DLMODULE_DEAMON
,如果返回该值,则 main() 函数返回后系统不会执行模块退出动作luban-lite/packages/artinchip/aic-dm-apps/hello/main.c:#define RT_API_TEST int main(int argc, char *argv[]) { printf("[AIC-DM-APP] Hello, world!\n"); #ifdef RT_API_TEST my_thread_init(); return RT_DLMODULE_DEAMON; #endif return 0; }
库文件 .so
dlopen()、dlsym()
实例test_dm_lib
命令的基本原理是使用dlopen()
函数动态加载hello.so
到系统内存,再使用dlsym()
函数查找到hello.so
中的my_thread_init()
函数并调用 luban-lite/bsp/examples/test-dm-lib/test_dm_lib.c:#define DM_LIB_PATH "/sdcard/hello.so" #define DM_LIB_FUNC "my_thread_init" #define DEAMON_THREAD static void cmd_test_dm_lib(int argc, char **argv) { struct rt_dlmodule *module = NULL; int (*func)(void) = NULL; module = dlopen(DM_LIB_PATH, 0); if (!module) { printf("dlopen %s fail!\n", DM_LIB_PATH); return; } func = dlsym(module, DM_LIB_FUNC); if (!func) { printf("dlsym %s fail!\n", DM_LIB_FUNC); return; } func(); #ifndef DEAMON_THREAD dlclose(module); #endif }
- 查看
hello.so
创建的子线程我们通过
test_dm_lib
命令动态加载hello.so
并调用my_thread_init()
函数,同样会会创建tMyTask
线程。但是我们使用ps
或者list_thread
命令,这次却可以看到该线程。这是为什么呢?这是因为系统通过
dlmodule_self()
判断当前进程非模块执行进程,对应的进程链表就加入到了全局链表information->object_list
中。 - 后台进程保活
同样的问题,当我们调用
my_thread_init()
函数返回后,常规情况是执行dlclose(module)
来清理动态加载的模块。如果my_thread_init()
函数创建的所有子进程希望作为后台进程运行,则不能调用dlclose(module)
。这种情况下如果调用
dlclose(module)
,后台进程还能短暂执行,但是一旦有新的内存分配就会覆盖原动态模块的数据,触发 CPU 异常。