如何从 golang 正确地 IOCTL

How to IOCTL properly from golang

我正在尝试将 raspberrypi's userspace 代码的一部分从 C 移植到 golang,并且我已经 运行 进入一个涉及 ioctl() 的程序。

我在执行 C 代码时遇到了问题

#define MAJOR_NUM 100
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
static int mbox_property(int file_desc, void *buf){

   int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf);
   return ret_val;
}

我的 go 等价物是

func mBoxProperty(f *os.File, buf [256]int64) {
        err := Ioctl(f.Fd(), IOWR(100, 0, 8), uintptr(unsafe.Pointer(&buf[0])))

        if err != nil {
                log.Fatalln("mBoxProperty() : ", err)
        }

}

func Ioctl(fd, op, arg uintptr) error {
        _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg)
        if ep != 0 {
                return syscall.Errno(ep)
        }
        return nil
}

func IOWR(t, nr, size uintptr) uintptr {
        return IOC(IocRead|IocWrite, t, nr, size)
}
func IOC(dir, t, nr, size uintptr) uintptr {
        return (dir << IocDirshift) | (t << IocTypeshift) | (nr << IocNrshift) | (size << IocSizeshift)
}

但是每当我 运行 这个,我得到 invalid argument 错误,我认为这可能是由于我如何调用 IOCTL() 但我不确定,我该如何解决这个?

ioctl(2) 包装器 "golang.org/x/sys/unix". unix.IoctlSetInt 机械上可能满足您的需要。

看起来您正在将对一个小内存缓冲区的控制权移交给内核。你需要小心这样做:Go 垃圾收集器释放它认为没有被使用的内存对象,即使某些东西正在使用,它也可以移动它。内核不会知道这一点并将继续使用旧指针。 unsafe.Pointer 文档在这个主题上有很多话要说,即使是关于不那么奇特的系统调用。我不知道有什么可以 "lock" 内存中的 Go 对象可以防止它被移动或释放(例如,runtime 包中没有任何东西跳出)。

您可以考虑使用 cgo 编写一个非常小的扩展,malloc() 编辑一个适当的缓冲区并将其交给 ioctl。 malloc 的内存不是垃圾收集的,因此它不会移动或从您的下方释放;一些低级工具可能认为这看起来像是内存泄漏(保留指针的旧值以便以后能够释放它并避免这种情况并不是一个坏主意)。

您可能还与 uintpr(unsafe.Pointer(...)) 需要在 到 syscall.Syscall 的通话中 发生的细节发生冲突。

这是详细信息,来自 https://golang.org/pkg/unsafe/#Pointer

(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.

The Syscall functions in package syscall pass their uintptr arguments directly to the operating system, which then may, depending on the details of the call, reinterpret some of them as pointers. That is, the system call implementation is implicitly converting certain arguments back from uintptr to pointer.

If a pointer argument must be converted to uintptr for use as an argument, that conversion must appear in the call expression itself:

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

The compiler handles a Pointer converted to a uintptr in the argument list of a call to a function implemented in assembly by arranging that the referenced allocated object, if any, is retained and not moved until the call completes, even though from the types alone it would appear that the object is no longer needed during the call.

For the compiler to recognize this pattern, the conversion must appear in the argument list:

// INVALID: uintptr cannot be stored in variable
// before implicit conversion back to Pointer during system call.
u := uintptr(unsafe.Pointer(p))
syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

unsafe 的这种用法是让您 "lock" Dave Maze 在上面寻找的 Go 对象的技巧。