#c #go #pointers #garbage-collection #cgo
# #c #Вперед #указатели #сбор мусора #cgo
Вопрос:
Я хотел бы перенести программу go в виде библиотеки, экспортировав некоторые API-интерфейсы C. В C обычно функция принимает буфер (указатель) в качестве параметра (вместе с его размером) и записывает в него выходные данные. Например (из стандартной библиотеки C)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
Это приведет к записи в *ptr
указанное вызывающим объектом не более size*nmemb
байтов. Преимущество такого дизайна заключается в том, что память управляется вызывающим объектом, и, следовательно, функции не нужно иметь дело с выделением памяти. Я хочу, чтобы мой API, экспортированный из go, также выглядел так.
Однако я обнаружил, что нет прямого способа записи в указатель в go. Итак, я создал что-то вроде этого (небольшая модификация случая 6 этой документации), который создает []byte
объект в go, используя базовую память C.
// create a byte array using C memory for internal use
func createBuffer(buf unsafe.Pointer, size int) []byte {
var res []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(amp;res))
hdr.Data = uintptr(unsafe.Pointer(buf))
hdr.Len = size
hdr.Cap = size
return res
}
Затем я экспортировал свою функцию следующим образом ( readers
это фрагмент io.Reader
, unsafe.Pointer
будет иметь тип void*
после экспорта), который будет считывать некоторые данные в память C, содержащую не более size
байтов.
//export read
func read(id int, buf unsafe.Pointer, size C.int) int {
res, err := readers[id].Read(createBuffer(buf, int(size))) // works
//res, err := readers[id].Read(C.GoBytes(buf, size)) // does not work
if err != nil {
return -1
}
return res
}
Это работает так, как ожидалось (т. Е. В моей программе на C буфер действительно содержит нужные данные после вызова read
), но я не уверен, является ли это правильным способом сделать это (т. Е. Есть ли проблемы с памятью, особенно связанные со сборкой мусора (GC)).
Предположим, что результат []byte
, возвращенный из createBuffer
, больше нигде не используется после read
завершения, как память будет освобождена с помощью GC? Будет ли GC также пытаться освободить hdr.Data
, который должен быть частью памяти, управляемой программой C? Официальная документация содержит немало предупреждений об этом. Если это вызовет проблемы с GC, как правильно это сделать? (Я уже пробовал C.GoBytes(...)
, кажется, он копирует базовый буфер и, следовательно, не работает)