Golang中Rdev参数在mips架构下定义为uint32类型而在其他架构下为uint64类型的探讨
Golang中Rdev参数在mips架构下定义为uint32类型而在其他架构下为uint64类型的探讨 你好,
我需要编译依赖 golang/sys 的 docker,但其中有一个来自 golang/sys 的参数 s.Rdev,我必须强制将其转换为 uint64 类型,否则编译无法继续。
错误信息是:“cannot use s.Rdev (type uint32) as type uint64 in field value”。
我阅读了 golang/sys 的部分源代码,发现该参数在 mips 架构中被定义为 uint32 类型,而在其他架构中则是 uint64 类型。我的机器基于 mips64 架构,所以我想知道为什么在 mips 架构中它被实现为 uint32。
mips64: https://github.com/golang/sys/blob/master/unix/ztypes_linux_mips64.go#L92 amd64: https://github.com/golang/sys/blob/master/unix/ztypes_linux_amd64.go#L92
谢谢,祝好。
更多关于Golang中Rdev参数在mips架构下定义为uint32类型而在其他架构下为uint64类型的探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢。我对内核不太熟悉。你的意思是我操作系统内核中的 rdev 字段是 u32 类型。如果是这样,那问题会不会出在 Docker 上?
更多关于Golang中Rdev参数在mips架构下定义为uint32类型而在其他架构下为uint64类型的探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我看到cherrymui的回复,说Go使用了内核的布局。内核是32位的,这有点出乎意料,但他说的有道理。
所以,如果这不是Go的bug,那一定是Docker的bug……
从C程序的输出来看,您操作系统中 rdev 的大小是8字节,因此它是 uint64 类型。
这意味着问题出在Go标准库中……
我认为这值得在Go主仓库上提一个issue。
func main() {
fmt.Println("hello world")
}
在你编译Docker的mips64平台上,输出结果是什么?
$ cat dev.c
#include <sys/stat.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("%lu\n", sizeof(dev_t));
struct stat s;
printf("%lu\n", sizeof(s.st_dev));
printf("%lu\n", sizeof(s.st_rdev));
}
$ gcc --version
$ gcc dev.c -o dev && ./dev
$
非常感谢,我已经在Go仓库提交了一个问题。
x/sys: sizeof st_rdev is 64bits on mips64
你使用的是哪个Go版本 (go version)?
$ go version
go version go1.13.9 linux/mips64le
你使用的操作系统和处理器架构是…
感谢您的回复,这是在mips64上的输出:
[root@node001 git]# gcc --version
gcc (GCC) 7.3.1 20180303 (Loongson 7.3.1-5)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[root@node001 git]# gcc dev.c -o dev && ./dev
8
8
8
limeidan:
我阅读了golang/sys的部分源代码,发现在mips架构中参数被定义为uint32类型,而在其他架构中则是uint64。我的机器基于mips64架构,所以我想知道为什么在mips中它被实现为uint32。
这些头文件似乎是自动从内核C头文件生成的,如果我没记错的话。
至少在我提出的关于某些字段被重命名的问题中是这样说明的:
x/sys/unix: go1.11 on mips64le fails at TestFstatat on Debian build
截至go1.11.4(已测试1.11.2和1.11.4),TestFstatat在Debian构建机上失败:
--- FAIL: TestFstatat (0.00s) syscall_unix_test.go:516: Fstatat: returned stat does not match Stat syscall_unix_test.go:535: Fstatat: returned stat does not match Lstat FAIL FAIL cmd/vendor/golang.org/x/sys/unix 0.405s构建日志请参见 https://buildd.debian.org/status/logs.php?pkg=golang-1.11&arch=mips64el。
抱歉,我还没有时间通过登录Debian的远程mips64el(mips64le)机器来深入研究这个问题,但我想我应该先在这里试试运气。:-) 非常感谢!
limeidan:
错误信息:“cannot use s.Rdev (type uint32) as type uint64 in field value”
如果s.Rdev可以是uint32,那么它无法编译很可能是docker中的一个bug。
在Go语言的golang.org/x/sys/unix包中,Stat_t结构体的Rdev字段类型确实存在架构差异。这是由Linux内核头文件的底层C结构体定义决定的。
查看Linux内核源码,在arch/mips/include/uapi/asm/stat.h中可以看到:
struct stat {
...
unsigned int st_rdev;
...
};
而在x86_64架构的arch/x86/include/uapi/asm/stat.h中:
struct stat {
...
unsigned long st_rdev;
...
};
MIPS架构使用unsigned int(32位),而x86_64使用unsigned long(64位)。Go的sys/unix包直接映射这些底层类型。
对于你的情况,在MIPS64架构上编译时需要进行类型转换。以下是正确处理跨架构兼容性的示例:
package main
import (
"fmt"
"golang.org/x/sys/unix"
"syscall"
)
func getRdev() uint64 {
var stat unix.Stat_t
// 假设有获取stat的代码
// unix.Stat("/path/to/file", &stat)
// 跨架构安全的类型转换
return uint64(stat.Rdev)
}
// 或者使用条件编译
func getRdevSafe() uint64 {
var stat unix.Stat_t
// 使用syscall.Stat_t作为中间类型
var sysStat syscall.Stat_t
// syscall.Stat("/path/to/file", &sysStat)
// 通过interface{}进行类型断言
switch v := interface{}(stat.Rdev).(type) {
case uint32:
return uint64(v)
case uint64:
return v
default:
return 0
}
}
如果你需要编写跨架构兼容的代码,建议使用类型转换或条件编译:
// +build mips mipsle mips64 mips64le
package main
func convertRdev(rdev uint32) uint64 {
return uint64(rdev)
}
// +build !mips,!mipsle,!mips64,!mips64le
package main
func convertRdev(rdev uint64) uint64 {
return rdev
}
这种差异是历史遗留问题,MIPS架构在设计时使用了32位的设备号,而其他架构如x86_64使用了64位。在实际使用中,直接进行uint64()类型转换是安全的解决方案。

