如何在cgo中使用包装器释放unsafe.Pointer?

huangapple go评论83阅读模式
英文:

How to free unsafe.Pointer using a wrapper in cgo?

问题

你可以在Go代码中使用C.free函数来释放unsafe.Pointer指向的内存。在你的代码中,你可以在FreeCByte函数中调用C.free函数来释放内存。这样,你就可以在Python代码中调用lib.FreeCByte来释放内存。

在你的Python代码中,你可以取消注释#free(ptr)这一行,然后调用lib.FreeCByte(ptr)来释放内存。这样,你就可以在Python中释放Go代码中分配的内存。

总结一下,你需要在Go代码中使用C.free函数来释放内存,并在Python代码中调用lib.FreeCByte来释放内存。这样可以确保你在Python中释放Go代码中分配的内存,避免悬空指针的问题。

英文:

How would I go about creating a wrapper that frees the unsafe.Pointer in my code?
Here's my cgo code:

//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))
}

It seems like I cannot free the memory in my python code. I am creating a wrapper so that I can free the memory in python instead of inside of go so that I won't have a dangling pointer.

Here's my python code:

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

How can I free the unsafe pointer in go?

答案1

得分: 2

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

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

> 评论:我仍然遇到内存泄漏。- JJ Cauton

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

$ 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() {}

$


----------

```python
$ 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=[])

$
英文:

Free the unsafe.Pointer in Go, not a pointer to the unsafe.Pointer.

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

> Comment: I am still getting memory leaks. – JJ Cauton

The code in your question does not compile and run. I fixed your code to compile and run, and added some debugging code. Clearly, memory is being freed.

$ 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 (
&quot;encoding/binary&quot;
&quot;fmt&quot;
&quot;os&quot;
&quot;unsafe&quot;
)
/*
#include &lt;stdlib.h&gt;
*/
import &quot;C&quot;
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, &quot;Go: cclientpy: C.CBytes:&quot;, cbytes)
}
return cbytes
}
//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
if debug {
fmt.Fprintln(os.Stderr, &quot;Go: FreeCByte:&quot;, 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, &quot;Go: FreeCByte: double free&quot;, 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(&quot;./test.so&quot;)
lib.cclientpy.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
lib.cclientpy.restype = ctypes.POINTER(ctypes.c_ubyte * 8)
&quot;&quot;&quot; THERE IS A BIG MEMORY LEAK, BEWARE &quot;&quot;&quot;
free = lib.free
free.argtypes = [ctypes.POINTER(ctypes.c_ubyte * 8)]
def newrequest(path, lister={}, proxy=[]):
try:
print(f&quot;proxy: {proxy}&quot;)
ptr = lib.cclientpy(path.encode(&quot;utf-8&quot;), str(lister).encode(&quot;utf-8&quot;), str(proxy).encode(&quot;utf-8&quot;))
length = int.from_bytes(ptr.contents, byteorder=&quot;little&quot;)
data = bytes(ctypes.cast(ptr,
ctypes.POINTER(ctypes.c_ubyte * (8 + length))
).contents[8:])
#free(ptr)
lib.FreeCByte(ptr)
print(f&#39;bytes: {bytes(ctypes.cast(ptr,ctypes.POINTER(ctypes.c_ubyte * (8 + length))).contents[8:])}&#39;)
return data
except:
pass
newrequest(path=&#39;argpath&#39;, lister={}, proxy=[])
$ 

huangapple
  • 本文由 发表于 2021年9月6日 18:03:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/69072700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定