项目中需要获取网卡的一些硬件信息,但遇到一点问题,现在用另一种方式解决了;但还是想搞懂为什么会报这个错误,基础太差,希望得到大佬的指导,非常感谢。
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 40EaE5uJO3Xt1Va 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 无效的原因大概是破坏了栈内容吧。。 |