项目中需要获取网卡的一些硬件信息,但遇到一点问题,现在用另一种方式解决了;但还是想搞懂为什么会报这个错误,基础太差,希望得到大佬的指导,非常感谢。
package main
import (
"fmt"
"golang.org/x/sys/unix"
"syscall"
"unsafe"
)
func main() {
for {
go func() {
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
defer func() {
if err := unix.Close(fd); err != nil {
fmt.Println("fd 关闭失败:", err)
}
}()
if err != nil {
fmt.Println(err)
return
}
var et struct {
Cmd uint32
Supported uint32
}
const GSET = 0x1
et.Cmd = GSET
var ifr struct {
Name [16]byte
Data uintptr
}
copy(ifr.Name[:], []byte("网卡名称"))
ifr.Data = uintptr(unsafe.Pointer(&et))
const SIOCETHTOOL = 0x8946
// 执行这一步后,fd 就无法关闭,close 返回 bad file descriptor
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), uintptr(unsafe.Pointer(&ifr)))
if errno != 0 {
fmt.Println(errno)
}
}()
}
select {}
}
已解决,结帖;ethtool_cmd结构体不全导致,从c源码抄一份完整的结构就解决了
package main
import (
"fmt"
"golang.org/x/sys/unix"
"syscall"
"time"
"unsafe"
)
type EthtoolCmd struct {
Cmd uint32
Supported uint32
Advertising uint32
Speed uint16
Duplex uint8
Port uint8
Phy_address uint8
Transceiver uint8
Autoneg uint8
Mdio_support uint8
Maxtxpkt uint32
Maxrxpkt uint32
Speed_hi uint16
Eth_tp_mdix uint8
Reserved2 uint8
Lp_advertising uint32
Reserved [2]uint32
}
type EthtoolValue struct {
Cmd uint32
Data uint32
}
type EthtoolDrvInfo struct {
cmd uint32
driver [32]byte
version [32]byte
fw_version [32]byte
bus_info [32]byte
erom_version [32]byte
reserved2 [12]byte
n_priv_flags uint32
n_stats uint32
testinfo_len uint32
eedump_len uint32
regdump_len uint32
}
const IFNAMSIZ = 16
type Ifreq struct {
ifr_name [IFNAMSIZ]byte
ifr_data uintptr
}
func main() {
for {
func() {
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
defer func() {
if err := unix.Close(fd); err != nil {
fmt.Println("fd关闭失败:", err)
}
}()
if err != nil {
fmt.Println(err)
return
}
var ifr Ifreq
//var drvinfo EthtoolDrvInfo
var ecmd EthtoolCmd
//var edata EthtoolValue
copy(ifr.ifr_name[:], []byte("eno2"))
ecmd.Cmd = 0x1
ifr.ifr_data = uintptr(unsafe.Pointer(&ecmd))
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(0x8946), uintptr(unsafe.Pointer(&ifr)))
if errno != 0 {
panic("ioctl 发送失败")
}
time.Sleep(1 * time.Second)
}()
}
select {}
1
40EaE5uJO3Xt1VVa 2021-08-05 12:14:08 +08:00
另一种方法是不是直接执行个 sh 脚本获取返回值。
|
2
Nitroethane 2021-08-05 12:41:24 +08:00
close 返回 EBADF 说明你要关闭的文件描述符是一个无效的文件描述符。你的代码有问题,关闭 fd 的 defer 应该放到错误处理的后面,因为如果 unix.Socket() 调用失败的话,fd 的值就是一个无效的文件描述符,你关闭它当然返回 EBADF
|
3
Nitroethane 2021-08-05 12:42:07 +08:00
还有,现在都用 netlink
|
4
wellsc 2021-08-05 13:15:12 +08:00
老铁,你把报错信息贴上来啊
|
5
ch2 2021-08-05 13:25:20 +08:00
查一下 syscall.SYS_IOCTL 的手册
|
6
Aumujun OP @Nitroethane 跟 defer 放的位置无关;这个 fd 在被执行 ioctl 后才会关闭失败,不知道什么原因造成的。
|
7
Aumujun OP @yanzhiling2001 不是
|
8
Nitroethane 2021-08-05 14:02:31 +08:00 via iPhone
@Aumujun 第一,你的 defer 放在错误处理前面就是有问题。第二,我跑了一下你的代码,ioctl 调用报错,但是 close 没有问题
|
9
qieqie 2021-08-05 14:10:56 +08:00
你这个 goroutine 为什么要放在 for{}里面,瞬间 fd 数量都爆了吧
另外用 SIOCGIFNAME 不行吗,你这个 SIOCETHTOOL 在<bits/ioctls.h>都找不到定义 |
11
Srar 2021-08-05 14:15:04 +08:00
尝试下 runtime.KeepAlive? 可能是被 gc 收走了
|
12
Aumujun OP 问题找到了,是我的 et 结构体不全,我在 c 源码里抄了一个完整的 ethtool_cmd 结构体就可以了。
|
13
qieqie 2021-08-05 14:29:46 +08:00
正确做法是先 include c 的 header 然后 import "C",初始化 C 的结构体
其实 2 楼指出的问题也是正确的,我觉得楼主 golang 应该还没入门,有些问题别急着下结论,以后水平长进了再回头看看吧。 |
14
Aumujun OP @qieqie 二楼说的 defer close 我没 get 到点,fd 无效会关闭抛出 bad fd 这个我知道,但我的问题是 fd 为什么无效;这相当于没说。另外 import C 这种方式我不喜欢用;还不如纯 C 编译 so 直接给 go 用。
|
15
www5070504 2021-08-05 16:53:44 +08:00
二楼的意思是先检查错误 如果打开错误的话 fd 就是无内容的 defer 就出错了 如果先检查错误再 defer 至少保证 defer 起到应有的关闭 fd 的作用
|
16
codehz 2021-08-06 15:44:37 +08:00
无效的原因大概是破坏了栈内容吧。。
|