[问题] go 调用 Linux 系统接口无法关闭 fd - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Aumujun
V2EX    Go 编程语言

[问题] go 调用 Linux 系统接口无法关闭 fd

  •  
  •   Aumujun
    None 2021-08-05 11:48:17 +08:00 2583 次点击
    这是一个创建于 1530 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目中需要获取网卡的一些硬件信息,但遇到一点问题,现在用另一种方式解决了;但还是想搞懂为什么会报这个错误,基础太差,希望得到大佬的指导,非常感谢。

    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 {} } 
    第 1 条附言    2021-08-05 14:19:26 +08:00

    已解决,结帖;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 {} 
    16 条回复    2021-08-06 15:44:37 +08:00
    40EaE5uJO3Xt1VVa
        1
    40EaE5uJO3Xt1Va  
       2021-08-05 12:14:08 +08:00
    另一种方法是不是直接执行个 sh 脚本获取返回值。
    Nitroethane
        2
    Nitroethane  
       2021-08-05 12:41:24 +08:00
    close 返回 EBADF 说明你要关闭的文件描述符是一个无效的文件描述符。你的代码有问题,关闭 fd 的 defer 应该放到错误处理的后面,因为如果 unix.Socket() 调用失败的话,fd 的值就是一个无效的文件描述符,你关闭它当然返回 EBADF
    Nitroethane
        3
    Nitroethane  
       2021-08-05 12:42:07 +08:00
    还有,现在都用 netlink
    wellsc
        4
    wellsc  
       2021-08-05 13:15:12 +08:00
    老铁,你把报错信息贴上来啊
    ch2
        5
    ch2  
       2021-08-05 13:25:20 +08:00
    查一下 syscall.SYS_IOCTL 的手册
    Aumujun
        6
    Aumujun  
    OP
       2021-08-05 13:28:15 +08:00
    @Nitroethane 跟 defer 放的位置无关;这个 fd 在被执行 ioctl 后才会关闭失败,不知道什么原因造成的。
    Aumujun
        7
    Aumujun  
    OP
       2021-08-05 13:29:14 +08:00
    Nitroethane
        8
    Nitroethane  
       2021-08-05 14:02:31 +08:00 via iPhone
    @Aumujun 第一,你的 defer 放在错误处理前面就是有问题。第二,我跑了一下你的代码,ioctl 调用报错,但是 close 没有问题
    qieqie
        9
    qieqie  
       2021-08-05 14:10:56 +08:00
    你这个 goroutine 为什么要放在 for{}里面,瞬间 fd 数量都爆了吧
    另外用 SIOCGIFNAME 不行吗,你这个 SIOCETHTOOL 在<bits/ioctls.h>都找不到定义
    Aumujun
        10
    Aumujun  
    OP
       2021-08-05 14:13:37 +08:00
    @qieqie 就是一个示例,顺手就写上去了;和问题无关;
    Srar
        11
    Srar  
       2021-08-05 14:15:04 +08:00
    尝试下 runtime.KeepAlive? 可能是被 gc 收走了
    Aumujun
        12
    Aumujun  
    OP
       2021-08-05 14:16:16 +08:00
    问题找到了,是我的 et 结构体不全,我在 c 源码里抄了一个完整的 ethtool_cmd 结构体就可以了。
    qieqie
        13
    qieqie  
       2021-08-05 14:29:46 +08:00
    正确做法是先 include c 的 header 然后 import "C",初始化 C 的结构体
    其实 2 楼指出的问题也是正确的,我觉得楼主 golang 应该还没入门,有些问题别急着下结论,以后水平长进了再回头看看吧。
    Aumujun
        14
    Aumujun  
    OP
       2021-08-05 15:28:04 +08:00
    @qieqie 二楼说的 defer close 我没 get 到点,fd 无效会关闭抛出 bad fd 这个我知道,但我的问题是 fd 为什么无效;这相当于没说。另外 import C 这种方式我不喜欢用;还不如纯 C 编译 so 直接给 go 用。
    www5070504
        15
    www5070504  
       2021-08-05 16:53:44 +08:00
    二楼的意思是先检查错误 如果打开错误的话 fd 就是无内容的 defer 就出错了 如果先检查错误再 defer 至少保证 defer 起到应有的关闭 fd 的作用
    codehz
        16
    codehz  
       2021-08-06 15:44:37 +08:00
    无效的原因大概是破坏了栈内容吧。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1067 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 18:21 PVG 02:21 LAX 11:21 JFK 14:21
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86