【问题标题】:errno to Go errorsGo 错误的 errno
【发布时间】:2021-10-12 04:30:13
【问题描述】:

我正在尝试从 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())
}
  1. 在 GetSupportedCpuid 内部,ioctl 的参数cpuid 使用C.calloc 分配。如果这是 C 代码 cpuid 可以分配在堆栈上。有什么办法可以绕过使用 calloc 和使用 free 进行分配?有什么更惯用的替代方法吗?
  2. 有没有办法让 ErrnoToErr 返回与 errno 对应的 go 错误类型?目前它在 syscall/syscall_unix.go 中使用(e Errno) Error();所以它只是取消引用字符串列表并返回字符串。最好有一个类型,这样我就可以在单元测试中测试特定错误。

【问题讨论】:

  • 如果您确实在堆栈上分配了 cpuid 数据,则无法从函数返回(指向)它的指针,因为从函数返回会使堆栈存储无效.我不太清楚的是,在从以 C 为中心(ioctl,填充大小在运行时确定但仅限于MaxKvmCpuidEntries)到以 Go 为中心(你在哪里可能需要一片 Go-ized 数据)。
  • @torek 谢谢——是的,我选择了错误的 ioctl 作为示例。假设cpuid不需要从函数中返回,有没有办法在堆栈上分配它?我会继续更新示例。

标签: c go kvm cgo


【解决方案1】:

仅回复更新后的示例,我们可以这样做:

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 指针规则(我认为不违反),阅读起来会更好。

【讨论】:

  • 谢谢!我认为它需要是“irqfd := C.struct_kvm_irqfd{}”
  • @learnlearnlearn:哎呀,是的。也完全未经测试。 :-)
【解决方案2】:

阅读文档syscall.Errno

所以转换可以这样进行

func ErrnoToErr(errno syscall.Errno) error {
    if errno != 0 {
        return errno
    }
    return nil
}

为了平等,它只声明

可以根据 os 包中的错误值测试 Errno 值 使用错误。是。

这里已经考虑了比较错误的问题How to compare Go errors

【讨论】:

    猜你喜欢
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 2013-06-05
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 2011-10-10
    • 2019-09-20
    相关资源
    最近更新 更多