【问题标题】:How to free unsafe.Pointer using a wrapper in cgo?如何在 cgo 中使用包装器释放 unsafe.Pointer?
【发布时间】:2021-11-03 10:26:18
【问题描述】:

我将如何创建一个包装器来释放我的代码中的 unsafe.Pointer? 这是我的 cgo 代码:

//export clientpy
func clientpy(url *C.char, headersfrompy *C.char, proxy *C.char) unsafe.Pointer {
    s := C.GoString(url)
    headers := C.GoString(headersfrompy)
    p := C.GoString(proxy)

    request := UrlGet(s, headers, p)

    length := make([]byte, 8)

    binary.LittleEndian.PutUint64(length, uint64(len(request)))
    return C.CBytes(append(length, request...))
}

//export FreeCByte
func FreeCByte(b *unsafe.Pointer) {
    C.free(unsafe.Pointer(b))
}

似乎我无法释放我的 python 代码中的内存。我正在创建一个包装器,以便我可以在 python 中而不是在 go 中释放内存,这样我就不会有一个悬空指针。

这是我的python代码:

from ctypes import cdll
import ctypes, cchardet, json
from bs4 import BeautifulSoup

lib = cdll.LoadLibrary("./test.so")
lib.cclientpy.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
lib.cclientpy.restype = ctypes.POINTER(ctypes.c_ubyte * 8)
""" THERE IS A BIG MEMORY LEAK, BEWARE """

free = lib.free
free.argtypes = [ctypes.POINTER(ctypes.c_ubyte * 8)]

def newrequest(path, lister={}, proxy=[]):
    try:
        print(f"proxy: {proxy}")
        ptr = lib.cclientpy(path.encode("utf-8"), str(lister).encode("utf-8"), str(proxy).encode("utf-8"))
        length = int.from_bytes(ptr.contents, byteorder="little")
        data = bytes(ctypes.cast(ptr,
                                 ctypes.POINTER(ctypes.c_ubyte * (8 + length))
                                 ).contents[8:])
        #free(ptr)
        lib.FreeCByte(ptr)
        print(f'bytes: {bytes(ctypes.cast(ptr,ctypes.POINTER(ctypes.c_ubyte * (8 + length))).contents[8:])}')

        return data
    except:
        pass

如何释放 go 中的不安全指针?

【问题讨论】:

  • 你正试图释放一个指向unsafe.Pointer的指针,去掉额外的间接。

标签: python c go cgo


【解决方案1】:

在 Go 中释放 unsafe.Pointer,而不是指向 unsafe.Pointer 的指针。

//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
    C.free(b)
}

评论:我仍然遇到内存泄漏。 – JJ考顿

您问题中的代码无法编译和运行。我修复了您的代码以编译和运行,并添加了一些调试代码。显然,内存正在被释放。

$ go version
go version devel go1.18-8214257347 Wed Sep 8 14:51:40 2021 +0000 linux/amd64
$ go build -o test.so -buildmode=c-shared test.go
$ python3 --version
Python 3.9.5
$ python3 test.py
proxy: []
Go: cclientpy: C.CBytes: 0x1a7e420
Go: FreeCByte: 0x1a7e420
Go: FreeCByte: double free 0x1a7e420
free(): double free detected in tcache 2
Aborted (core dumped)
$ 

$ cat test.go

package main

import (
    "encoding/binary"
    "fmt"
    "os"
    "unsafe"
)

/*
#include <stdlib.h>
*/
import "C"

func UrlGet(s, headers, p string) []byte {
    return nil
}

const debug = true

//export cclientpy
func cclientpy(url *C.char, headersfrompy *C.char, proxy *C.char) unsafe.Pointer {
    s := C.GoString(url)
    headers := C.GoString(headersfrompy)
    p := C.GoString(proxy)

    request := UrlGet(s, headers, p)

    length := make([]byte, 8)

    binary.LittleEndian.PutUint64(length, uint64(len(request)))
    cbytes := C.CBytes(append(length, request...))

    if debug {
        fmt.Fprintln(os.Stderr, "Go: cclientpy: C.CBytes:", cbytes)
    }

    return cbytes
}

//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
    if debug {
        fmt.Fprintln(os.Stderr, "Go: FreeCByte:", b)
    }

    C.free(b)

    if debug {
        // tests to see if already freed, should fail
        // free(): double free detected in tcache 2
        // Aborted (core dumped)
        fmt.Fprintln(os.Stderr, "Go: FreeCByte: double free", b)
        C.free(b)
    }
}

func main() {}

$ 

$ cat test.py

from ctypes import cdll
import ctypes, chardet as cchardet, json
from bs4 import BeautifulSoup

lib = cdll.LoadLibrary("./test.so")
lib.cclientpy.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
lib.cclientpy.restype = ctypes.POINTER(ctypes.c_ubyte * 8)
""" THERE IS A BIG MEMORY LEAK, BEWARE """

free = lib.free
free.argtypes = [ctypes.POINTER(ctypes.c_ubyte * 8)]

def newrequest(path, lister={}, proxy=[]):
    try:
        print(f"proxy: {proxy}")
        ptr = lib.cclientpy(path.encode("utf-8"), str(lister).encode("utf-8"), str(proxy).encode("utf-8"))
        length = int.from_bytes(ptr.contents, byteorder="little")
        data = bytes(ctypes.cast(ptr,
                                 ctypes.POINTER(ctypes.c_ubyte * (8 + length))
                                 ).contents[8:])
        #free(ptr)
        lib.FreeCByte(ptr)
        print(f'bytes: {bytes(ctypes.cast(ptr,ctypes.POINTER(ctypes.c_ubyte * (8 + length))).contents[8:])}')

        return data
    except:
        pass
    
newrequest(path='argpath', lister={}, proxy=[])

$ 

【讨论】:

  • 我仍然遇到内存泄漏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-23
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多