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

2021-08-05 11:48:17 +08:00
 Aumujun

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

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 {}
}
2223 次点击
所在节点    Go 编程语言
16 条回复
yanzhiling2001
2021-08-05 12:14:08 +08:00
另一种方法是不是直接执行个 sh 脚本获取返回值。
Nitroethane
2021-08-05 12:41:24 +08:00
close 返回 EBADF 说明你要关闭的文件描述符是一个无效的文件描述符。你的代码有问题,关闭 fd 的 defer 应该放到错误处理的后面,因为如果 unix.Socket() 调用失败的话,fd 的值就是一个无效的文件描述符,你关闭它当然返回 EBADF
Nitroethane
2021-08-05 12:42:07 +08:00
还有,现在都用 netlink
wellsc
2021-08-05 13:15:12 +08:00
老铁,你把报错信息贴上来啊
ch2
2021-08-05 13:25:20 +08:00
查一下 syscall.SYS_IOCTL 的手册
Aumujun
2021-08-05 13:28:15 +08:00
@Nitroethane 跟 defer 放的位置无关;这个 fd 在被执行 ioctl 后才会关闭失败,不知道什么原因造成的。
Aumujun
2021-08-05 13:29:14 +08:00
Nitroethane
2021-08-05 14:02:31 +08:00
@Aumujun 第一,你的 defer 放在错误处理前面就是有问题。第二,我跑了一下你的代码,ioctl 调用报错,但是 close 没有问题
qieqie
2021-08-05 14:10:56 +08:00
你这个 goroutine 为什么要放在 for{}里面,瞬间 fd 数量都爆了吧
另外用 SIOCGIFNAME 不行吗,你这个 SIOCETHTOOL 在<bits/ioctls.h>都找不到定义
Aumujun
2021-08-05 14:13:37 +08:00
@qieqie 就是一个示例,顺手就写上去了;和问题无关;
Srar
2021-08-05 14:15:04 +08:00
尝试下 runtime.KeepAlive? 可能是被 gc 收走了
Aumujun
2021-08-05 14:16:16 +08:00
问题找到了,是我的 et 结构体不全,我在 c 源码里抄了一个完整的 ethtool_cmd 结构体就可以了。
qieqie
2021-08-05 14:29:46 +08:00
正确做法是先 include c 的 header 然后 import "C",初始化 C 的结构体
其实 2 楼指出的问题也是正确的,我觉得楼主 golang 应该还没入门,有些问题别急着下结论,以后水平长进了再回头看看吧。
Aumujun
2021-08-05 15:28:04 +08:00
@qieqie 二楼说的 defer close 我没 get 到点,fd 无效会关闭抛出 bad fd 这个我知道,但我的问题是 fd 为什么无效;这相当于没说。另外 import C 这种方式我不喜欢用;还不如纯 C 编译 so 直接给 go 用。
www5070504
2021-08-05 16:53:44 +08:00
二楼的意思是先检查错误 如果打开错误的话 fd 就是无内容的 defer 就出错了 如果先检查错误再 defer 至少保证 defer 起到应有的关闭 fd 的作用
codehz
2021-08-06 15:44:37 +08:00
无效的原因大概是破坏了栈内容吧。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/793809

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX