【问题标题】:Calling a shared library from Haskell via FFI blocks, while it doesn't when linked from a C program通过 FFI 块从 Haskell 调用共享库,而从 C 程序链接时不会
【发布时间】:2016-08-24 10:59:47
【问题描述】:

我正在尝试通过 Haskell 应用程序连接 Basler USB3 相机,但遇到了一些困难。该相机带有一个 C++ 库,使其相当简单。以下代码可用于获取相机源:

extern "C" {
  void basler_init() {
    PylonAutoInitTerm pylon;
    CInstantCamera camera( CTlFactory::GetInstance().CreateFirstDevice());
    camera.RegisterConfiguration( (CConfigurationEventHandler*) NULL, RegistrationMode_ReplaceAll, Cleanup_None);
    cout << "Using device " << camera.GetDeviceInfo().GetModelName() << endl;
  }
}

我已使用此源代码构建了一个共享库 - libbasler.so。为了确认它可以正常工作,这里有一个基本的 C 程序链接它并确认一切正常:

void basler_init();

int main () {
  basler_init();
}

我将其编译并运行为:

$ gcc Test2.c -lbasler -Llib -Wl,--enable-new-dtags -Wl,-rpath,pylon5/lib64 -Wl,-E -lpylonbase -o Test2-c

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-c
Using device Emulation

这是预期的输出。

但是,当我尝试将它与 Haskell 一起使用时,行为会发生变化,并且程序会无限期地阻塞。这是 Haskell 源代码:

{-# LANGUAGE ForeignFunctionInterface #-}

foreign import ccall "basler_init" baslerInit :: IO ()

main :: IO ()
main = baslerInit

我将其编译并运行为:

$ ghc --make Test2.hs -o Test2-haskell -Llib -lbasler -optl-Wl,--enable-new-dtags -optl-Wl,-rpath,pylon5/lib64 -optl-Wl,-E -lpylonbase

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-haskell

应用程序现在无限期挂起。

我已经通过strace 尝试了解正在发生的事情,但我无法真正理解它。输出太长,这里添加,不过请看这两个贴:

除此之外,我还使用gdb 尝试确定 Haskell 应用程序卡住的位置:

$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib gdb Test2-haskell 
GNU gdb (GDB) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from Test2-haskell...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-haskell 
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
^C
Program received signal SIGINT, Interrupt.
0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
#1  0x00007fffedb885c2 in GxImp::CEnumCollector::OnReady(unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#2  0x00007fffedb8d54d in CCollector::Collect(GxImp::CSocket*, unsigned int, unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#3  0x00007fffedb8817b in CBroadcastSocketCollection::Collect(CCollector&, unsigned int) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#4  0x00007fffedb889ab in Gx::Enumerator::Discover(Gx::Enumerator::Callee*, unsigned int, unsigned int, sockaddr const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#5  0x00007fffeddeaca0 in Pylon::CBaslerGigETl::DoDeviceEnumeration(Pylon::DeviceInfoList&, bool, sockaddr const*) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#6  0x00007fffeddeaebc in Pylon::CBaslerGigETl::InternalEnumerateDevices(Pylon::DeviceInfoList&) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#7  0x00007fffeddf3c99 in Pylon::CTransportLayerBase<Pylon::IGigETransportLayer>::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#8  0x00007ffff7949669 in Pylon::CTlFactory::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#9  0x00007ffff7949c8f in Pylon::CTlFactory::InternalCreateDevice(Pylon::CDeviceInfo const&, GenICam_3_0_Basler_pylon_v5_0::gcstring_vector const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#10 0x00007ffff794a655 in Pylon::CTlFactory::CreateFirstDevice(Pylon::CDeviceInfo const&) () from pylon5/lib64/libpylonbase-5.0.1.so
#11 0x00007ffff7bd7dc5 in basler_init () from lib/libbasler.so
#12 0x0000000000438415 in rFl_info ()
#13 0x0000000000000000 in ?? ()

对于 C 程序:

Reading symbols from Test2-c...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-c 
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
[New Thread 0x7fffed4ae700 (LWP 13792)]
[New Thread 0x7fffeccad700 (LWP 13793)]
Using device Emulation
[Thread 0x7fffeccad700 (LWP 13793) exited]
[Thread 0x7fffed4ae700 (LWP 13792) exited]
[Inferior 1 (process 13788) exited normally]

我的猜测是 GHC 的运行时正在做一些导致 pthread 具有不同行为的事情,但我不确定那可能是什么。

【问题讨论】:

  • @J.J.Hakala 并没有什么不同。我也可以在basler_init 之前使用cout,没有任何问题。

标签: c++ c haskell ffi


【解决方案1】:

我相信这条 TRAC 评论是相关的:

https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals

差异发生在 C strace 输出中的第 437 行开始 与 Haskell strace 输出中的第 495 行相比。

此时库创建两个 UDP 套接字并发送两个 UDP 数据报 (第 448-449 C 行 / 第 506-507 行 Haskell)。数据包被广播到 两个本地网络:192.168.1.0/24 和 192.168.56.0/24。

然后它等待这些套接字中的任何一个上的响应,超时时间为(显然) 25 微秒(第 450 行 C / 第 508 行 Haskell)。在 C 情况下,选择调用 超时。在 Haskell 案例中,选择调用被反复中断 GHC RTS 使用的 SIGVTALRM 信号。这是相同的模式 上面的 TRAC 评论中显示了这一点。

对于潜在的修复,请查看 mysql 包如何实现和使用 block_rts_signals()unblock_rts_signals() 宏:

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-08
  • 1970-01-01
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多