如何从golang正确地进行IOCTL

时间:2019-01-27 12:24:47

标签: c linux go raspberry-pi ioctl

我正在尝试将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;
}

和我的等效条件是

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()而引起的,但是我不确定如何解决此问题?

2 个答案:

答案 0 :(得分:3)

"golang.org/x/sys/unix"中有 ioctl (2)个包装器。 unix.IoctlSetInt可能会满足您的需求。

看起来您正在将一个较小的内存缓冲区的控制权交给内核。您需要注意以下几点:Go垃圾回收器释放了它认为未使用的内存对象,即使正在使用某些东西,它也可以将其移动。内核对此一无所知,并将继续使用旧指针。 unsafe.Pointer文档在这个主题上有很多话要说,即使涉及较少的外来系统调用也是如此。我不知道有什么可以“锁定”内存中的Go对象的对象,以防止它被移动或释放(例如,runtime程序包中什么都不会跳出来)。

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

答案 1 :(得分:1)

您还可能会误以为uintpr(unsafe.Pointer(...))需要在呼叫中发生 syscall.Syscall的细节。

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

  

(4)调用syscall.Syscall时将指针转换为uintptr。

     

syscall软件包中的Syscall函数将其uintptr参数直接传递给操作系统,然后,操作系统可以根据调用的详细信息将其中一些参数重新解释为指针。也就是说,系统调用实现正在将某些参数从uintptr隐式转换回指针。

     

如果必须将指针参数转换为uintptr用作参数,则该转换必须出现在调用表达式本身中:

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

编译器通过安排引用的分配对象(如果有的话)被保留并且直到调用完成(即使从调用完成)才移动,来处理在汇编实现的函数的调用的参数列表中转换为uintptr的Pointer。单独键入,似乎在调用过程中不再需要该对象。

     

为使编译器能够识别此模式,转换必须出现在参数列表中:

     
// 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是一种技巧,可让您“锁定” Dave Maze在上面寻找的Go对象。