代码之家  ›  专栏  ›  技术社区  ›  Héctor C.

ctypes可能存在内存泄漏

  •  1
  • Héctor C.  · 技术社区  · 7 年前

    我正在使用一些使用ctypes的库。这个库有自己的方法来处理堆内存,所以在C中我会这样做:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "intcall.h"
    
    int main(void) {
        long status = -1;
        char host[10] = "xx.x.x.xx";
        char user[7] = "xxxxxx";
        char pass[7] = "xxxxxx";
        char acc[10] = "xxxxxxxxx";
        long sess = ic_universe_session(host, user, pass, acc, &status, NULL);
        if (status == 0)
        {
            char sub_name[14] = "PRUEBA_XXXXXX";
            long sub_name_len = 13;
            unsigned char param1_txt[5] = "AAAA";
            long size1 = 5;
            long num_params = 2;
            //void * FAR PASCAL ic_malloc ic_proto((LPLONG));
            char* param1 = ic_malloc(&size1);
            memcpy(param1, param1_txt, size1);
            /*  typedef struct icstring {
                    long len;
                    unsigned char * text;
                } ICSTRING; */
            ICSTRING icstr1;
            icstr1.len = size1; icstr1.text = param1;
            ICSTRING icstr2;
            icstr2.len = 0;
            ic_subcall(sub_name, &sub_name_len, &status, &num_params, &icstr1, &icstr2);
            //void FAR PASCAL ic_free ic_proto((void *));
            ic_free(icstr1.text);
            ic_free(icstr2.text);
        }
        return EXIT_SUCCESS;
    }
    

    库可以释放并重新分配我传递给它的指针,所以这就是为什么我需要释放icstr1.text而不是param1,它是ic\u malloc的返回指针。

    在python中,类似的示例如下:

    subrutina = 'PRUEBA_XXXXXX'
    argumento = 'AAAA'
    num_params = 2
    argumentos_tipo = [c_char_p, POINTER(c_long), POINTER(c_long), POINTER(c_long)]
    self.__U2_C_INTERFACE.ic_calloc.argtypes = [POINTER(c_long)]
    self.__U2_C_INTERFACE.ic_free.argtypes = [ctypes.c_voidp]
    self.__U2_C_INTERFACE.ic_subcall.argtypes = argumentos_tipo
    sub_name_in = c_char_p(bytes(subrutina, self.__sistema_codificacion))
    sub_name_len_in = c_long(len(subrutina))
    num_params_len_in = c_long(num_params)
    code = c_long(0)
    p_in = [None] * num_params
    mem_alloc = [c_char_p(0)] * num_params
    byte_param = bytes(argumento, self.__sistema_codificacion)
    byte_size = len(byte_param)
    p_in[0] = ICString(c_long(0))
    p_in[0].len = c_long(byte_size)
    mem_alloc[0] = c_char_p(self.__U2_C_INTERFACE.ic_calloc(byref(c_long(byte_size + 1))))
    tam = byte_size * sizeof(ctypes.c_char)
    ctypes.memmove(mem_alloc[0], byte_param, tam)
    p_in[0].text = mem_alloc[0]
    p_in[1] = ICString(c_long(0))
    self.__U2_C_INTERFACE.ic_subcall(sub_name_in,
                                     byref(sub_name_len_in),
                                     byref(code),
                                     byref(num_params_len_in),
                                     *[byref(n) for n in p_in])
    if int(code.value) != 0:
        err_msg = 'Error subrutina %s: %s' % (subrutina, UniverseConstants.error_message(code.value))
        from pyuniverse.pyuniverse.UniverseExceptions import UniverseCommandException
        raise UniverseCommandException(err_msg)
    # copiar los resultados
    for idx, parametro in enumerate(p_in):
        print(p_in[idx].text)
        # if p_in[idx].len > 0:
        #     temp = ctypes.cast(p_in[idx].text, ctypes.c_void_p)
        #     self.__U2_C_INTERFACE.ic_free(temp)
    

    但是对icu free的调用在堆中造成了麻烦:进程结束,退出代码为-1073740940(0xc000374)。这意味着“堆损坏异常0xc000374”

    我尝试使用pympler跟踪更多的输入参数的内存使用情况。有两个参数,我得到:

                                     types |   # objects |   total size
    ================================================ | =========== | ============
                                        <class 'dict |           4 |    640     B
                      <class '_ctypes.PyCPointerType |           1 |    492     B
      <class 'ctypes.CDLL.__init__.<locals>._FuncPtr |           4 |    480     B
                  <class 'builtin_function_or_method |           7 |    252     B
                                        <class 'list |           5 |    236     B
                                       <class 'tuple |           5 |    184     B
                                     <class 'StgDict |           1 |    148     B
                                        <class 'type |           0 |    144     B
                                         <class 'str |           3 |    127     B
                           <class 'getset_descriptor |           2 |     80     B
                                     <class 'weakref |           1 |     44     B
                                         <class 'int |           1 |     16     B
    

    有十个参数:

                                               types |   # objects |   total size
    ================================================ | =========== | ============
                                        <class 'dict |           4 |    640     B
                      <class '_ctypes.PyCPointerType |           1 |    492     B
      <class 'ctypes.CDLL.__init__.<locals>._FuncPtr |           4 |    480     B
                  <class 'builtin_function_or_method |           7 |    252     B
                                        <class 'list |           5 |    268     B
                                       <class 'tuple |           5 |    184     B
                                     <class 'StgDict |           1 |    148     B
                                        <class 'type |           0 |    144     B
                                         <class 'str |          11 |    401     B
                           <class 'getset_descriptor |           2 |     80     B
                                     <class 'weakref |           1 |     44     B
                                         <class 'int |           1 |     16     B
    

    主要区别在于“str”类:

    Two params: <class 'str |           3 |    127     B
    Ten params: <class 'str |          11 |    401     B
    

    这个内存将由python的垃圾收集器处理吗?我错过什么了吗?是不是有什么我没看到的Pypler的内存泄漏?

    0 回复  |  直到 7 年前