errno 到 Go 错误
errno to Go errors
我正在尝试从 Go 代码发出 KVM ioctls。目前我有以下内容:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := (*C.struct_kvm_irqfd)(C.calloc(1, C.sizeof_struct_kvm_irqfd))
defer C.free(unsafe.Pointer(irqfd))
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
ioctl函数实现如下:
func Ioctl(fd uintptr, cmd C.uint, arg uintptr) (uintptr, error) {
ret, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
uintptr(cmd),
uintptr(arg),
)
if int64(ret) == -1 {
return ret, ErrnoToErr(errno)
}
return ret, nil
}
而ErrnoToErr函数实现如下:
func ErrnoToErr(errno syscall.Errno) error {
return fmt.Errorf("%v", errno.Error())
}
- 在 GetSupportedCpuid 内部,ioctl 的参数
cpuid
是使用 C.calloc
分配的。如果这是 C 代码 cpuid
就可以在堆栈上分配。有什么办法可以绕过使用 calloc 和使用 free 进行分配吗?有没有更惯用的替代方案?
- 有没有办法让 ErrnoToErr return 与 errno 对应的错误类型?目前它使用 syscall/syscall_unix.go 中的
(e Errno) Error()
;所以它只是取消引用字符串列表和 returns 字符串。最好有一个类型,这样我就可以在我的单元测试中测试特定的错误。
阅读文档syscall.Errno
所以可以这样转换
func ErrnoToErr(errno syscall.Errno) error {
if errno != 0 {
return errno
}
return nil
}
对于平等,它只说明
Errno values can be tested against error values from the os package
using errors.Is.
这里已经考虑了比较误差的问题How to compare Go errors
仅回复更新的示例,我们可以这样做:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := C.struct_kvm_irqfd{}
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(&irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
我 认为 这是安全的,但我们可能会发现 irqfd escapes to heap
无论如何,因为我们把它的地址放在这里,在这种情况下并没有多少节省。即便如此,只要这不违反 Go 指针规则(我不认为它违反),阅读起来会更好。
我正在尝试从 Go 代码发出 KVM ioctls。目前我有以下内容:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := (*C.struct_kvm_irqfd)(C.calloc(1, C.sizeof_struct_kvm_irqfd))
defer C.free(unsafe.Pointer(irqfd))
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
ioctl函数实现如下:
func Ioctl(fd uintptr, cmd C.uint, arg uintptr) (uintptr, error) {
ret, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
uintptr(cmd),
uintptr(arg),
)
if int64(ret) == -1 {
return ret, ErrnoToErr(errno)
}
return ret, nil
}
而ErrnoToErr函数实现如下:
func ErrnoToErr(errno syscall.Errno) error {
return fmt.Errorf("%v", errno.Error())
}
- 在 GetSupportedCpuid 内部,ioctl 的参数
cpuid
是使用C.calloc
分配的。如果这是 C 代码cpuid
就可以在堆栈上分配。有什么办法可以绕过使用 calloc 和使用 free 进行分配吗?有没有更惯用的替代方案? - 有没有办法让 ErrnoToErr return 与 errno 对应的错误类型?目前它使用 syscall/syscall_unix.go 中的
(e Errno) Error()
;所以它只是取消引用字符串列表和 returns 字符串。最好有一个类型,这样我就可以在我的单元测试中测试特定的错误。
阅读文档syscall.Errno
所以可以这样转换
func ErrnoToErr(errno syscall.Errno) error {
if errno != 0 {
return errno
}
return nil
}
对于平等,它只说明
Errno values can be tested against error values from the os package using errors.Is.
这里已经考虑了比较误差的问题How to compare Go errors
仅回复更新的示例,我们可以这样做:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := C.struct_kvm_irqfd{}
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(&irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
我 认为 这是安全的,但我们可能会发现 irqfd escapes to heap
无论如何,因为我们把它的地址放在这里,在这种情况下并没有多少节省。即便如此,只要这不违反 Go 指针规则(我不认为它违反),阅读起来会更好。