【问题标题】:In Android-ndk, How to make _fini( ) to be called when dlclose( ) is called?在 Android-ndk 中,如何在调用 dlclose( ) 时调用 _fini( )?
【发布时间】:2019-06-10 07:19:36
【问题描述】:

我们在 Android-NDK 项目中使用 C 库。我们正在使用 dlopen() 和 dlclose() 来处理我们在 Android NDK 中的库。当我们调用 dlopen() 时,库的 _init() 会按预期被调用。但相反,当我们使用 dlclose() 调用关闭库时,_fini() 函数不会被调用。我们不想在调用 dlclose( ) API 之后显式调用 _fini( ) 函数。我正在 API 24 上测试这些。

根据linux,当我们分别调用dlopen()和dlclose()时,应该调用_init()和_fini()这两个函数。

void func( ){
...
   int ret = -1;
   handle = dlopen( "mylib.so", RTLD_NOW | RTLD_GLOBAL );
   ret = dlclose( handle );    // dlclose() is returning 0.
...
}

void _init( ){
   log("dlopen is loading the library");
}

void _fini( ){
   log("dlclose is unloading the library");
}

我也尝试过如下更改 _fini( ) 的语法,但没有成功。

void _fini() __attribute__((destructor));

即使使用上一行中的“析构函数”属性,_fini() 也不会被 dlclose() 调用。

由于 _init() 函数在 dlopen() 上被调用,我确信 _fini() 也应该在 dlclose() 上被调用,所以我应该遗漏一些东西。我们正在使用 Android Studio 来构建 apk。谁能帮我解决这个问题。提前致谢。

【问题讨论】:

  • 您的 dlclose() 不起作用,因为它不希望库名称作为参数。您应该将 dlopen() 返回的句柄传递给它。
  • @AlexCohn :谢谢 Alex,我确实在我的项目代码中将句柄从 dlopen() 传递给了 dlclose()。我在提问时犯了一个错误。我也正确编辑了我的问题。 dlclose() 的返回值为 0。如果我需要对代码进行任何其他更改,请告诉我。
  • 你确定 dlclose() 有效吗?很容易检查:如果句柄仍可用于 dlsym(),则合法地不调用 _fini()。
  • @AlexCohn :令人惊讶的是,在调用 dlclose() 之后,句柄仍然可以用于 dlsym()。我能够获得一个函数的地址,并且也可以调用该函数,并且在调用 dlclose() 之后所有这一切!!。这意味着 dlclose() 没有按预期工作,但是,dlclose() 调用返回 0。有什么办法,我可以弄清楚为什么对 dlclose() 的调用没有按预期工作。
  • @AlexCohn :另外,当我更改 _init() 的签名如下以包含构造函数属性时, _init() 函数被连续调用两次,所以我删除了构造函数属性:@987654323 @ 这些问题可能是由于我的 CMakeLists.txt 文件中的一些错误配置造成的。只有void _init() {...} _init() 只被调用一次。

标签: android c android-ndk


【解决方案1】:

关于dlclose,我能给出的最好建议是永远不要使用dlclose

dlclose 除了通知运行时它可能卸载库外,没有指定做任何事情;运行时不必对此做任何事情。对于确实实现卸载的系统(包括 Android)并不总是能够这样做,因为像 atexit 处理程序(或线程退出处理程序等)之类的东西在整个程序退出之前无法运行,所以任何时候库注册这样的处理程序它无法卸载。这里似乎就是这种情况。

确切的用例可能会导致答案有所不同,但对于我们已经看到的此类行为无法删除的情况(插件接口,预期插件会卸载,然后重新加载所有状态重置) ,解决方法是将显式 Initialize()ShutDown() 函数添加到库中。这些需要由库用户调用,而不是依赖于dlopen/dlclose

【讨论】:

  • 谢谢 Dan Albert,根据您的建议,我有 2 个问题。 (1) _init() 如我们预期的那样被调用,所以我们可以保留 _init() 调用并对 _fini() 进行必要的更改,还是我们也必须选择 _init() 的替代方法? (2) 应用程序完全退出时,_fini() 中的断点没有命中。那么,_fini() 根本不会被调用吗?如果我遗漏了什么,请告诉我。
  • (1) 如果您需要重新初始化库,请不要依赖_init()。对于保证只发生一次的任何操作,您可以保留_init()
  • (2) 在 Android 中,不能保证您的应用程序会正常关闭。因此,即使库正常卸载,也会出现系统立即终止进程的情况。您不应依赖 _fini()atexit 处理程序或任何已执行的操作。
  • @AlexCohn :感谢您的回复亚历克斯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
相关资源
最近更新 更多