简体   繁体   English

如何优雅地从`syscall.Syscall()`的使用中迁移?

[英]How to elegantly migrate from uses of `syscall.Syscall()`?

The syscall package is deprecated.不推荐使用syscall包。 Assume I have the following code which I would like to migrate to something that is not deprecated:假设我有以下代码,我想将其迁移到未弃用的内容:

someGoObject := &struct{int; float32}{5, 45.4}
syscall.Syscall6(someProc.Addr(), 1, uintptr(unsafe.Pointer(someGoObject)), 0, 0, 0, 0, 0)

whereby someProc is of type *syscall.LazyProc ( Windows ).其中someProc*syscall.LazyProc ( Windows ) 类型。

The sys subrepository , which the syscall docs recommend to use instead, no longer offers a function akin to syscall.Syscall , and if the desired function is not implemented over there, one might attempt to solve the problem the following way, and believe the job is done: syscall文档推荐使用的sys subrepository不再提供类似于syscall.Syscall的功能,如果所需的功能没有在那里实现,人们可能会尝试通过以下方式解决问题,并相信这项工作已经完成了:

someProc.Call(uintptr(unsafe.Pointer(someGoObject)))

whereby now someProc is of type *windows.LazyProc .现在someProc*windows.LazyProc类型。

However, if we did this, we would not get one of syscall.Syscall 's (and friends') guarantees, because LazyProc.Call() is not implemented in assembly :但是,如果我们这样做,我们将无法获得syscall.Syscall的(和朋友的)保证之一,因为LazyProc.Call() 未在程序LazyProc.Call() 实现

(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall. (4) 调用 syscall.Syscall 时将 Pointer 转换为 uintptr。

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. syscall 包中的 Syscall 函数将它们的 uintptr 参数直接传递给操作系统,然后操作系统可能会根据调用的详细信息将其中一些参数重新解释为指针。 That is, the system call implementation is implicitly converting certain arguments back from uintptr to pointer.也就是说,系统调用实现隐式地将某些参数从 uintptr 转换回指针。

If a pointer argument must be converted to uintptr for use as an argument, that conversion must appear in the call expression itself:如果必须将指针参数转换为 uintptr 以用作参数,则该转换必须出现在调用表达式本身中:

 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.编译器通过安排引用的分配对象(如果有)被保留并且在调用完成之前不会移动,即使仅从类型来看,编译器处理在调用的参数列表中转换为 uintptr 的指针看起来在调用过程中不再需要该对象。

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))

(Taken from https://golang.org/pkg/unsafe/ ) (摘自https://golang.org/pkg/unsafe/

The way I see it, the only way to be safe is to alloc the memory using C. However, this means we need to copy our data and increases the amount of code which needs to be written by quite a bit.在我看来,唯一安全的方法是使用 C 分配内存。然而,这意味着我们需要复制数据并增加需要编写的代码量。

someObject := (*struct{int; float32})(C.calloc(1, unsafe.Sizeof(struct{int; float32}{}))) // Alloc
*someObject = struct{int; float32}{123, 456.789} // Fill with desired data
someProc.Call(uintptr(unsafe.Pointer(someObject))) // The actual call
C.free(unsafe.Pointer(someObject)) // Clean up

Is there a better way?有没有更好的办法?

I think the correct way to handle this, is to use the mkwinsyscall tool.我认为处理这个问题的正确方法是使用mkwinsyscall工具。 You can create a Go file like this:你可以像这样创建一个 Go 文件:

package main
//go:generate mkwinsyscall -output zmsi.go msi.go
//sys MsiOpenDatabase(dbPath string, persist int, db *windows.Handle) (e error) = msi.MsiOpenDatabaseW

Then run go generate , and you get a result file like this (some parts removed for brevity):然后运行go generate ,你会得到一个这样的结果文件(为简洁起见,删除了一些部分):

func MsiOpenDatabase(dbPath string, persist int, db *windows.Handle) (e error) {
    var _p0 *uint16
    _p0, e = syscall.UTF16PtrFromString(dbPath)
    if e != nil {
        return
    }
    return _MsiOpenDatabase(_p0, persist, db)
}

func _MsiOpenDatabase(dbPath *uint16, persist int, db *windows.Handle) (e error) {
    r0, _, _ := syscall.Syscall(procMsiOpenDatabaseW.Addr(), 3, uintptr(unsafe.Pointer(dbPath)), uintptr(persist), uintptr(unsafe.Pointer(db)))
    if r0 != 0 {
        e = syscall.Errno(r0)
    }
    return
}

As you can see, it automatically takes care of the string conversion, as well as the Syscall code, and even the error handling.如您所见,它会自动处理字符串转换、系统调用代码,甚至错误处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM