Paradoxon:Python上的无声崩溃';s ctypes.CDLL导入时,而不是直接运行时-这怎么可能?
所以,作为一个Linux爱好者,我在Windows上偶然发现了一些我无法解释的令人困惑的事情 我对这个例子有一个项目结构分析:Paradoxon:Python上的无声崩溃';s ctypes.CDLL导入时,而不是直接运行时-这怎么可能?,python,python-import,ctypes,Python,Python Import,Ctypes,所以,作为一个Linux爱好者,我在Windows上偶然发现了一些我无法解释的令人困惑的事情 我对这个例子有一个项目结构分析: D:\PROJECT | | tolkien.py | __init__.py | \---MiddleEarth | gondor.py | isengrad.c | __init__.py | \---lib isengrad.so 问题:我将isengrad.c编译到
D:\PROJECT
|
| tolkien.py
| __init__.py
|
\---MiddleEarth
| gondor.py
| isengrad.c
| __init__.py
|
\---lib
isengrad.so
问题:我将isengrad.c
编译到共享库isengrad.so
,然后将其加载到gondor.py
中。我的目标是将gondor.py
导入tolkien.py
直接运行时,
gondor.py
运行正常,而导入时,代码会在通过ctypes.CDLL
加载共享库时退出,不会出现任何错误消息
复制:
文件的内容(添加了一些“状态消息”以跟踪问题发生的位置):
isengrad.c:
然后,使用
然后在gondor.py中访问共享库:
现在,检查直接使用gondor.py
与在tolkien.py
中导入时发生的情况:
D:\project>python MiddleEarth/gondor.py
started gondor
gondor loads isengrad
gondor loaded isengrad
2
gondor ran
gondor finished
D:\project>python tolkien.py
started tolkien
started gondor
gondor loads isengrad
D:\project>
直接运行它不会产生任何问题。但是导入它会导致整个程序崩溃,而在加载共享库时不会出现任何单词和回溯。这是怎么发生的?我甚至硬编码了共享库的路径,所以不同的工作目录应该不会有问题。。。我对Kubuntu上的同一个项目没有任何问题,所以这可能是一些与Windows相关的东西
环境:
- Python:
Python 3.7.3(默认值,2019年3月27日,17:13:21)[MSC v.1915 64位(AMD64)]::win32上的Anaconda,Inc.
- 操作系统:
(安装在C:)Windows 10.0.17134内部版本17134
- GCC:通过Cygwin安装,版本7.4.0
- 请询问是否需要其他详细信息
- 我将使用3个窗口:
- cmd-Win(32位和64位)
- Cygwin的Mintty:
- 64位
- 32位
- 请注意,即使我将每个内容粘贴到一个块中(以避免分散它们),我也会在运行命令时在它们之间切换
- Cygwin 32位:
[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]>~/sopr.sh ***设置较短的提示,以便粘贴到StackOverflow(或其他)页面时更适合*** [032bit prompt]>gcc-shared-fPIC-o lib/isengrad_cyg_032.dll isengrad.c [032位提示符]>ls lib/*.dll lib/isengrad_cyg_032.dll lib/isengrad_cyg_064.dll lib/isengrad_win_032.dll lib/isengrad_win_064.dll [032位提示]> [032bit prompt]>python3 script0.py cyg 正在尝试加载:./lib/isengrad\u cyg\u 032.dll 加载的DLL Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位 伊森格勒23日返回 完成。 [032位提示]> [032bit prompt]>python3 script1.py cyg 正在尝试加载:./lib/isengrad\u cyg\u 032.dll 加载的DLL Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位 完成。 [032位提示]> [032bit prompt]>python3 script0.py win 正在尝试加载:./lib/isengrad\u win\u 032.dll 加载的DLL Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位 伊森格勒23日返回 完成。 [032位提示]> [032位提示]>python3 script1.py win 正在尝试加载:./lib/isengrad\u win\u 032.dll 加载的DLL Python 3.6.4(默认值,2018年1月7日,17:45:56)[GCC 6.4.0]cygwin上32位 完成。
- Cygwin 64位:
[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]>~/sopr.sh ***设置较短的提示,以便粘贴到StackOverflow(或其他)页面时更适合*** [064位提示符]>gcc-shared-fPIC-o lib/isengrad_cyg_064.dll isengrad.c [064位提示符]>ls lib/*.dll lib/isengrad_cyg_032.dll lib/isengrad_cyg_064.dll lib/isengrad_win_032.dll lib/isengrad_win_064.dll [064位提示]> [064位提示符]>python3 script0.py cyg 正在尝试加载:./lib/isengrad\u cyg\u 064.dll 加载的DLL Python 3.6.8(默认为
D:\project>chdir MiddleEarth D:\project\MiddleEarth>gcc -fPIC -shared -o lib/isengrad.so isengrad.c
print("started gondor") import os, ctypes path_to_isengrad = "D:/project/MiddleEarth/lib/isengrad.so" print("gondor loads isengrad") gondor = ctypes.CDLL(path_to_isengrad) # <--- crashes here when imported, not when ran directly print("gondor loaded isengrad") gondor.isengrad.argtypes = (ctypes.c_int,) def faramir(hobbit): catched_hobbits = gondor.isengrad(hobbit) return catched_hobbits if __name__ == '__main__': print(faramir(5)) print("gondor ran") print("gondor finished")
print("started tolkien") from MiddleEarth import gondor print("tolkien imported gondor") got = gondor.faramir(4) print(got) print("tolkien worked")
D:\project>python MiddleEarth/gondor.py started gondor gondor loads isengrad gondor loaded isengrad 2 gondor ran gondor finished D:\project>python tolkien.py started tolkien started gondor gondor loads isengrad D:\project>
- 从我看到这个问题的那一刻起,我想说这是一种未定义的行为(UB)。Python自带了C运行时(UCRTLib),而Cygwin.dll自带了自己的。在进程中混合使用编译器和C运行时通常会导致灾难
我找到了一份官方声明(重点是我的): 不,您必须使用其中一个,它们是互斥的 有关MSVCRT*.DLL的更多详细信息,请查看 现在,UB的美妙之处在于它描述了一种看似随机的行为 我准备了一个全面的示例(稍微修改一下代码) isengrad.c:
#如果已定义(_WIN32)
#定义ISENGRAD\u导出\u API\u declspec(dllexport)
#否则
#定义ISENGRAD\u导出\u API
#恩迪夫
ISENGRAD_导出API int ISENGRAD(int霍比特人){
返回霍比特/2;
}
script0.py:
#/usr/bin/env蟒蛇3
导入系统
导入ctypes
dll_name=“./lib/isengrad_{0:s}{1:03d}.dll”。格式(sys.argv[1][:3]if sys.argv else sys.platform[:3]。lower(),ctypes.sizeof(ctypes.c_void_p)*8)
打印(“正在尝试加载:{0:s}”。格式(dll_名称))
isengrad_dll=ctypes.CDLL(dll_名称)
打印(“加载DLL”)
def main():
isengrad_func=isengrad_dll.isengrad
isengrad_func.argtypes=[ctypes.c_int]
isengrad_func.restype=ctypes.c_int
res=isengrad_func(46)
打印(“{0:s}返回{1:}”。格式(isengrad_func.\uuu name_uu,res))
如果名称=“\uuuuu main\uuuuuuuu”:
打印(“Python{0:s}{1:d}位在{2:s}\n.format(“.join(sys.version.split(“\n”)中的项的item.strip()),如果sys.maxsize>0x100000000,则为64,否则为32,sys.platform))
main()
打印(“\n完成”)
脚本1.py:
#/usr/bin/env蟒蛇3
导入系统
导入脚本0
def main():
通过
如果名称=“\uuuuu main\uuuuuuuu”:
打印(“Python{0:s}{1:d}位在{2:s}\n.format(“.join(sys.version.split(“\n”)中的项的item.strip()),如果sys.maxsize>0x100000000,则为64,否则为32,sys.platform))
main()
打印(“\n完成”)
输出: