go - 如何在 go 中正确使用系统调用(Go unsafe.Sizeof 与 C sizeof 的结果不同)
问题描述
Go'sunsafe.Sizeof
返回的结果与 C's 不同sizeof
。
main.go:
package main
import (
"unsafe"
)
type gpioeventdata struct {
Timestamp uint64
ID uint32
}
func main() {
eventdata := gpioeventdata{}
println("Size", unsafe.Sizeof(eventdata))
}
12
在 macOS 上编译env GOOS=linux GOARCH=arm GOARM=6 go build
并在 Raspberry Pi Zero 上运行时打印。
gpio.c:
#include <stdio.h>
#include <linux/gpio.h>
int main() {
printf("sizeof gpioevent_data %zu\n", sizeof(struct gpioevent_data));
}
16
在 Raspberry 上编译和运行时打印(带有gcc
)。
gpio.h 中的结构定义:
struct gpioevent_data {
__u64 timestamp;
__u32 id;
};
编辑
我已经认为这是由于对齐,但是很多人将 Go 结构传递给syscall.Syscall
(例如https://github.com/stapelberg/hmgo/blob/master/internal/gpio/reset.go#L49)。所以这基本上是错误的,你不应该这样做吗?
如果那是错误的,那么使用 go 调用系统调用的正确方法是什么,以便在不同的体系结构中正常工作。例如 GPIO ioctl 调用:
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);
...
struct gpioevent_data event;
ret = read(req.fd, &event, sizeof(event));
解决方案
编译器go
和C
编译器处理对齐方式不同。
在 C 中,该结构已对齐为 16 字节(在其之后id
或之前添加 4 字节的松弛空间)。go
编译器反而在不添加任何松弛空间的情况下打包字段。
您的错误是认为具有不同编译器的不同语言的两个“结构”应该具有相同的内存布局。
请注意,没有办法“计算”C 或 C++ 结构中的填充,因为填充是实现者的选择。相同架构的两个不同的符合标准的 C 编译器很可能会生成不同的填充(甚至是具有不同编译选项的相同编译器)。
使填充正确的唯一方法是检查特定情况,手动或通过编写调用编译器的脚本传递相同的编译选项并检查结果(例如,通过输出offsetof
所有成员的结果)然后生成解析该输出后所需的go
源代码。
推荐阅读
- javascript - 我应该读取 cookie、本地、会话什么存储?
- javascript - 在 NodeJS 中更改 ReadStream 的文件名
- opencv - 使用 Tensorflow Api 和 Opencv 在视频上裁剪检测到的对象
- intellij-idea - IntelliJ 在重构示例包时创建错误
- python - PyQt Designer 创建的菜单栏
- powerapps - 更新 Powerapps 全局变量属性
- excel - Excel - 电源查询 2016
- linux - 打印所有变量值大于某个数字的行
- web - 如何在 Xamarin android 和 iPhone 中隐藏操作栏
- git - Git 提交删除换行符