将 “errno” 转换为 Go 语言的错误处理方式。

huangapple go评论73阅读模式
英文:

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())
}
  1. 在GetSupportedCpuid函数中,ioctl的参数cpuid是使用C.calloc分配的。如果这是C代码,cpuid可以直接在堆栈上分配。有没有办法避免使用calloc并且不使用free?有没有更符合惯用法的替代方法?
  2. 是否有办法让ErrnoToErr返回与errno对应的Go错误类型?目前它使用syscall/syscall_unix.go中的(e Errno) Error(),因此它只是解引用字符串列表并返回字符串。如果我能够使用类型,那么在单元测试中就可以测试特定的错误了。
英文:

I'm trying to issue KVM ioctls from Go code. At the moment I have something as follows:

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
}

The Ioctl function is implemented as follows :

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
}

And the ErrnoToErr function is implemented as follows :

func ErrnoToErr(errno syscall.Errno) error {
	return fmt.Errorf("%v", errno.Error())
}
  1. Inside GetSupportedCpuid, the argument for the ioctl, cpuid is allocated using C.calloc. If this were C code cpuid could just be allocated on the stack. Is there any way I can get around allocating using calloc and using free? Any alternatives that would be more idiomatic?
  2. Would there be a way to have ErrnoToErr return a go error type corresponding to the errno? Currently it uses the (e Errno) Error() inside syscall/syscall_unix.go; so it just dereferences a list of strings and returns the string. It would be nice to have a type, so that I can test for specific errors in my unit tests.

答案1

得分: 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指针规则(我认为不会),这样写起来会更好看。

英文:

Replying just to the updated example, we could do this:

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
}

I think this is safe, but we might find that irqfd escapes to heap anyway since we take its address here, in which case there's not much savings. Even so, as long as this doesn't violate Go pointer rules (I don't think it does) it's a lot nicer to read.

答案2

得分: -1

阅读文档 syscall.Errno

因此,可以像这样执行转换

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

对于相等性,它只说明

可以使用 errors.Is 将 Errno 值与来自 os 包的错误值进行比较。

有关比较错误的问题已经在这里考虑过 How to compare Go errors

英文:

Read the documentation syscall.Errno

So convertion may be performed like this

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

For equality it only states

> Errno values can be tested against error values from the os package
> using errors.Is.

The question of comparing errors was already considered here How to compare Go errors

huangapple
  • 本文由 发表于 2021年8月8日 04:13:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/68695805.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定