如何在 cgo 中使用包装器释放 unsafe.Pointer?
How to free unsafe.Pointer using a wrapper in cgo?
我将如何创建一个包装器来释放我代码中的 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中的不安全指针?
释放 Go 中的 unsafe.Pointer
,而不是指向 unsafe.Pointer
的指针。
//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
C.free(b)
}
Comment: I am still getting memory leaks. – 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() {}
$
$ 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=[])
$
我将如何创建一个包装器来释放我代码中的 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中的不安全指针?
释放 Go 中的 unsafe.Pointer
,而不是指向 unsafe.Pointer
的指针。
//export FreeCByte
func FreeCByte(b unsafe.Pointer) {
C.free(b)
}
Comment: I am still getting memory leaks. – 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() {}
$
$ 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=[])
$