通过 FFI 块从 Haskell 调用共享库,而从 C 程序链接时则不会
Calling a shared library from Haskell via FFI blocks, while it doesn't when linked from a C program
我正试图从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
试着了解正在发生的事情,但我无法真正理解它。输出太长,不能在这里添加,但请查看这两个粘贴:
- C应用程序的
strace
输出:https://gist.github.com/ocharles/001b5f42c09229bc7a8482a22cadf486 - Haskell应用程序的
strace
输出:https://gist.github.com/ocharles/4c1c45a9ee78f75cd723f1a2910998f3
最重要的是,我使用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有不同的行为,但我不确定那可能是什么。
我相信这个TRAC评论是相关的:
https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals差异出现在C字符输出的第437行开始对比Haskell字符串输出中的第495行
此时,库创建两个UDP套接字并发送两个UDP数据报(C 448-449行/Haskell 506-507行)。数据包被广播到两个本地网络:192.168.1.0/24和192.168.56.0/24
然后等待其中一个套接字的响应,超时为(显然)25微秒(450 C行/508 Haskell行)。在C的情况下,select调用超时。在Haskell的情况下,select调用被反复中断由GHC RTS使用的SIGVTALRM信号。这是相同的模式如上述TRAC注释所示。
对于一个潜在的修复,看看mysql包是如何实现和使用block_rts_signals()
和unblock_rts_signals()
宏的:
- mysql_signals.c
- 基于boost的程序的静态链接——zlib问题
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 将gsl c++程序与"英特尔MKL"链接
- 将 glade 文件与程序 g++ 链接
- Qt creator 4.11,在应用程序输出面板中创建一个链接
- 在树莓派上用libtorch构建程序时的链接问题
- 我的应用程序无法在安卓上使用OpenSSL进行链接
- C++实用程序::转换在静态链接库中不起作用
- 使用动态链接加载程序 <dlfcn.h> 而不是直接函数调用的目的是什么?
- 如何使用QtCreator将应用程序与"libqsqlpsql.so"链接?
- 为什么当我在 C 程序中链接静态库时会"undefined symbol"?
- 如何在 debian 下正确链接一个 opengl 程序?
- 在发布模式下启动使用库的静态链接编译的应用程序时出错
- C++ 为什么在定义的编译和链接之前引用外部实例的程序
- 检查G 是否可以链接程序
- C链接程序并在主函数上使用其他程序的方法(数独)
- 使用g++链接程序集和C++时未定义的引用
- 如何在c++中链接程序
- 链接C程序到c++共享库
- 无法使用 Boost.File system 链接程序