使用Python Ctypes和C ++时,如何解决分段错误? [英] How can I troubleshoot a segmentation fault when working with Python Ctypes and C++?

查看:131
本文介绍了使用Python Ctypes和C ++时,如何解决分段错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我在C ++中具有以下两个函数签名:

  BYTE * init(BYTE * Options,BYTE * Buffer )

和:

  int next(BYTE *接口,BYTE *缓冲区)

我首先在C ++中初始化 Interface 类,然后从Python调用 next 函数,并引用该类



第一个函数通过以下方式返回指向接口的 BYTE 指针:

 接口*接口; 
//初始化东西
return((BYTE *)interface);

我在Python中这样称呼它:



<前类= lang-py prettyprint-override> class Foo:
def init(self,data):
#省略:设置options_ptr
buf =( c_ubyte * len(data.bytes))。from_buffer_copy(data.bytes)
init_fun = getattr(self.dll,'?init @@ YAPAEPAE0HH @ Z')
init_fun.restype = POINTER(c_ubyte)
self.interface_ptr = init_fun(options_ptr,buf)
#可以正常工作!

def next(self,data):
#从其他数据创建buf
buf =(c_ubyte * len(data.bytes))。from_buffer_copy(data.bytes)
next_fun = getattr(self.dll,'?next @@ YAHPAE0HN @ Z')
ret = next_fun(self.interface_ptr,buf)
#我在这里随机得到分段错误

我从外面这样称呼,例如:

  foo = Foo()
foo.init(some_data)
foo.next(some_other_data)
#...
foo.next(some_additional_data)

现在,当我运行它时,出现分段错误:

  [1] 24712分段错误python -u test.py 

有时在第一次调用 .next()之后发生,有时在第一次调用<$之后发生c $ c> .next() —完全是随机的。



API的C ++测试代码的工作原理如下:

  BYTE缓冲区[500000]; 
UTIN BufSize = 0;
BYTE *接口;

#此处未显示:用
填充缓冲区接口= init(Buffer);
while(true){
#此处未显示:用其他数据填充缓冲区
int ret = next(Interface,Buffer);
}






现在,因为我不能显示确切的代码,因为它更大且更专有,问题是:如何解决这种分段错误?当引发异常时(在使用VS2012进行调试时)我可以中断,但会中断此处:





很显然,这没有用,因为在指示的行上,任何缓冲区实际上什么都没做。而且变量值也是隐秘的:





在我的情况下,数据 BitString 对象。如果C ++代码在缓冲区上进行了内存操作,那可能是问题吗?还是某些数据仍在需要时由Python进行垃圾收集?



更一般地说,在使用Ctypes时如何确保不会出现分段错误?我知道底层的DLL API可以正常工作并且不会崩溃。






更新:当我将 buf 用作实例变量时,例如 self._buf ,我遇到了分段错误,但是在调试过程中它在其他位置中断:



解决方案

有是我的一些误解,所有这些误解都导致了问题:




  • 在Python中创建Ctypes对象并传递时将其转换为C函数,并且不再需要该Python对象,它(可能)已被垃圾回收,并且不再位于C期望的内存堆栈中。



    因此,使缓冲区成为实例变量,例如 self._buf


  • C函数期望数据是可变的。如果C函数实际上没有将数据复制到其他地方而是直接在缓冲区上工作,则它必须是可变的。 Ctypes文档指定了以下内容:



    但是,请务必小心,不要将其传递给期望的函数指向可变内存的指针。如果您需要可变的内存块,则ctypes具有 create_string_buffer()函数,该函数可以通过各种方式创建它们。可以使用 raw 属性访问(或更改)当前存储块的内容;如果要以 NUL 终止的字符串访问它,请使用 value 属性:


    所以,我做了这样的事情:




  self._buf = create_string_buffer(500000)
self._buf.value = startdata.bytes




  • 缓冲区应该像示例代码中所示的普通数组一样在Python中使用,在缓冲区中填充缓冲区并处理内部数据。因此,对于我的 .next()方法,我做到了:



  self._buf.value = nextdata.bytes 

现在我的程序按预期运行。


Let's say I have the following two function signatures in C++:

BYTE* init( BYTE* Options, BYTE* Buffer )

and:

int next( BYTE* interface, BYTE* Buffer )

The idea is that I first initialize an Interface class in C++, then subsequently call the next function from Python, with a reference to that class.

The first function returns a BYTE pointer to the Interface via:

Interface*  interface;
// initialize stuff
return((BYTE*) interface);

I call it in Python like this:

class Foo:
  def init(self, data):
    # left out: setting options_ptr
    buf = (c_ubyte * len(data.bytes)).from_buffer_copy(data.bytes)
    init_fun = getattr(self.dll, '?init@@YAPAEPAE0HH@Z')
    init_fun.restype = POINTER(c_ubyte)
    self.interface_ptr = init_fun(options_ptr, buf)
    # this works fine!

  def next(self, data):
    # create buf from other data
    buf = (c_ubyte * len(data.bytes)).from_buffer_copy(data.bytes)
    next_fun = getattr(self.dll, '?next@@YAHPAE0HN@Z')
    ret = next_fun(self.interface_ptr, buf)
    # I randomly get segmentation faults here

I call this from outside with, e.g.:

foo = Foo()
foo.init(some_data)
foo.next(some_other_data)
# ...
foo.next(some_additional_data)

Now, when I run it, I get segmentation faults:

[1]    24712 segmentation fault  python -u test.py

Sometimes it happens after the first call to .next(), sometimes it happens after the eleventh call to .next()—totally at random.

There is a C++ test code for the API that works something like this:

BYTE Buffer[500000];
UTIN BufSize=0;
BYTE* Interface;

# not shown here: fill buffer with something
Interface = init(Buffer);
while(true) {
    # not shown here: fill buffer with other data
    int ret = next(Interface, Buffer);
}


Now, as I cannot show the exact code, since it's much bigger and proprietary, the question is: How can I troubleshoot such a segmentation fault? I can break when the exception is thrown (when debugging with VS2012), but it breaks here:

Clearly, that's not useful because nothing is actually done with any buffer at the indicated line. And the variable values are cryptic too:

In my case data is a BitString object. Could it be the problem if the C++ code does memory operations on the buffer passed? Or that some data is garbage-collected by Python when it's still needed?

More generally, how can I ensure not getting segmentation faults when working with Ctypes? I know that the underlying DLL API works fine and doesn't crash.


Update: When I make buf an instance variable, e.g. self._buf, I get a segmentation fault, but it breaks at a different location during debugging:

解决方案

There were a few misunderstandings I had, all of which led to the problems:

  • When you create a Ctypes object in Python and pass it to a C function, and that Python object is no longer needed, it is (probably) garbage-collected and no longer in the memory stack where C expects it to be.

    Therefore, make the buffer an instance variable, e.g. self._buf.

  • The C functions expect the data to be mutable. If the C functions do not actually copy the data somewhere else but work on the buffer directly, it needs to be mutable. The Ctypes documentation specifies this:

    Assigning a new value to instances of the pointer types c_char_p, c_wchar_p, and c_void_p changes the memory location they point to, not the contents of the memory block (of course not, because Python strings are immutable).

    You should be careful, however, not to pass them to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a create_string_buffer() function which creates these in various ways. The current memory block contents can be accessed (or changed) with the raw property; if you want to access it as NUL terminated string, use the value property:

    So, I did something like this:

    self._buf = create_string_buffer(500000)
    self._buf.value = startdata.bytes

  • The buffer should be used in Python like a normal array as shown in the example code, where it's filled and data inside is manipulated. So, for my .next() method, I did this:

    self._buf.value = nextdata.bytes

Now my program runs as expected.

这篇关于使用Python Ctypes和C ++时,如何解决分段错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆