如何在CGO中向含多结构体的Union传递字节数组?
问题描述
尝试通过Go语言向C中包含多结构体的Union传递值时出现异常:原本只想为Union内某一个结构体的成员赋值,结果Union里所有结构体起始位置的成员都被设为了同一个值。比如示例中仅想为start_id赋值5,但C程序里的map_id、start_id和prog_fd均被设为了5。
相关代码
example.h
#ifdef __cplusplus extern "C" { #endif typedef union bpf_attr { // BPF_MAP_GET_FD_BY_ID unsigned map_id; ///< ID of map for BPF_MAP_GET_FD_BY_ID to find. struct { unsigned start_id; ///< ID to look for an ID after. The start_id need not exist. unsigned next_id; ///< On return, contains the next ID. }bpf_next_id; ///< Attributes used by BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, and BPF_LINK_GET_NEXT_ID. // BPF_PROG_TEST_RUN struct { unsigned prog_fd; ///< File descriptor of program to run. unsigned retval; ///< On return, contains the return value of the program. unsigned data_size_in; ///< Size in bytes of input data. unsigned data_size_out; ///< Size in bytes of output data. unsigned long long data_in; ///< Pointer to input data. unsigned long long data_out; ///< Pointer to output data. unsigned duration; ///< Duration in milliseconds to run the program. unsigned ctx_size_in; ///< Size in bytes of input context. unsigned ctx_size_out; ///< Size in bytes of output context. unsigned long long ctx_in; ///< Pointer to input context. unsigned long long ctx_out; ///< Pointer to output context. unsigned flags; ///< Flags (currently 0). unsigned cpu; ///< CPU to run the program on. unsigned batch_size; ///< Number of times to run the program in a batch. unsigned repeat; ///< Number of times to repeat the program. } test; ///< Attributes used by BPF_PROG_TEST_RUN. }bpf_attr1; int display(union bpf_attr* attr, unsigned int size); #ifdef __cplusplus } #endif
example.cpp
#include "example.h" #include <stdio.h> int display(union bpf_attr* attr, unsigned int size) { printf("Map id %d \n", attr->map_id); printf("start_id %d \n", attr->bpf_next_id.start_id); printf("prog_fd %d \n", attr->test.prog_fd); attr->bpf_next_id.next_id = 25; return 0; }
main.go
package main /* #cgo CFLAGS: -I /cwrapper #cgo LDFLAGS: -L. -lexample -L /cwrapper/out #include "example.h" */ import "C" import ( "fmt" "unsafe" ) func main() { bpfAttr := new(C.union_bpf_attr) bpfAttr[0] = 5 bpfAttrSize := C.uint(unsafe.Sizeof(bpfAttr)) C.display(bpfAttr, bpfAttrSize) }
问题原因与解决方法
原因分析
C语言的Union特性是所有成员共享同一块内存空间,起始地址完全相同。map_id、bpf_next_id.start_id、test.prog_fd都是Union的第一个成员(unsigned类型,占4字节),所以直接通过bpfAttr[0] =5修改内存起始的4字节时,这三个变量都会读取到同一个值。
另外,unsafe.Sizeof(bpfAttr)计算的是Go指针的大小(64位系统为8字节),而非Union本身的实际大小,这也是一个潜在问题。
修正后的Go代码
直接通过Go生成的C类型成员访问赋值,而非操作内存数组下标:
package main /* #cgo CFLAGS: -I /cwrapper #cgo LDFLAGS: -L. -lexample -L /cwrapper/out #include "example.h" */ import "C" import ( "unsafe" ) func main() { bpfAttr := new(C.union_bpf_attr) // 直接为目标结构体成员赋值 bpfAttr.bpf_next_id.start_id = C.uint(5) // 计算Union的实际大小,需取指针指向的变量的大小 bpfAttrSize := C.uint(unsafe.Sizeof(*bpfAttr)) C.display(bpfAttr, bpfAttrSize) }
这样修改后,只有bpf_next_id.start_id会被设为5,其他Union成员的起始值不会被意外覆盖(默认会是0或内存初始值)。
内容的提问来源于stack exchange,提问作者Binoj
相关产品推荐
相关产品推荐

