[问题] Golang Go语言中调用 Linux 系统接口无法关闭 fd

发布于 1周前 作者 yibo5220 来自 Go语言

[问题] Golang Go语言中调用 Linux 系统接口无法关闭 fd

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

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 {}
}

更多关于[问题] Golang Go语言中调用 Linux 系统接口无法关闭 fd的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

17 回复

另一种方法是不是直接执行个 sh 脚本获取返回值。

更多关于[问题] Golang Go语言中调用 Linux 系统接口无法关闭 fd的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


close 返回 EBADF 说明你要关闭的文件描述符是一个无效的文件描述符。你的代码有问题,关闭 fd 的 defer 应该放到错误处理的后面,因为如果 unix.Socket() 调用失败的话,fd 的值就是一个无效的文件描述符,你关闭它当然返回 EBADF

还有,现在都用 netlink

老铁,你把报错信息贴上来啊

查一下 syscall.SYS_IOCTL 的手册

跟 defer 放的位置无关;这个 fd 在被执行 ioctl 后才会关闭失败,不知道什么原因造成的。

第一,你的 defer 放在错误处理前面就是有问题。第二,我跑了一下你的代码,ioctl 调用报错,但是 close 没有问题

你这个 goroutine 为什么要放在 for{}里面,瞬间 fd 数量都爆了吧
另外用 SIOCGIFNAME 不行吗,你这个 SIOCETHTOOL 在<bits/ioctls.h>都找不到定义

就是一个示例,顺手就写上去了;和问题无关;

尝试下 runtime.KeepAlive? 可能是被 gc 收走了

问题找到了,是我的 et 结构体不全,我在 c 源码里抄了一个完整的 ethtool_cmd 结构体就可以了。

正确做法是先 include c 的 header 然后 import “C”,初始化 C 的结构体
其实 2 楼指出的问题也是正确的,我觉得楼主 golang 应该还没入门,有些问题别急着下结论,以后水平长进了再回头看看吧。

二楼说的 defer close 我没 get 到点,fd 无效会关闭抛出 bad fd 这个我知道,但我的问题是 fd 为什么无效;这相当于没说。另外 import C 这种方式我不喜欢用;还不如纯 C 编译 so 直接给 go 用。

二楼的意思是先检查错误 如果打开错误的话 fd 就是无内容的 defer 就出错了 如果先检查错误再 defer 至少保证 defer 起到应有的关闭 fd 的作用

无效的原因大概是破坏了栈内容吧。。

在Golang中调用Linux系统接口时遇到无法关闭文件描述符(fd)的问题,通常涉及到底层资源管理和系统调用的处理。以下是一些可能的解决步骤和建议:

  1. 检查文件描述符的正确性: 确保你获取的文件描述符是有效的。在调用close(fd int)之前,验证fd是否已被正确分配且未被其他操作意外关闭。

  2. 错误处理: 在调用close函数后,检查其返回值和errno,以确认关闭操作是否成功。如果close返回错误,使用errors.Iserrors.As来判断错误类型,这有助于诊断问题。

  3. 资源泄露检查: 确保没有在程序的其他部分意外保留或复制了文件描述符。这可能导致在尝试关闭时,文件描述符已被其他goroutine使用或关闭。

  4. 使用defer: 在打开文件或套接字后立即使用defer close(fd),这可以确保即使在发生错误时,文件描述符也能被正确关闭。

  5. 系统限制: 检查系统级别的文件描述符限制(ulimit -n),确保你的程序没有超出这个限制。

  6. 调试工具: 使用stracelsof等工具来跟踪系统调用和文件描述符的使用情况,这可以帮助你发现文件描述符管理中的问题。

通过上述步骤,你应该能够定位并解决在Golang中调用Linux系统接口时无法关闭文件描述符的问题。如果问题依旧存在,建议详细审查相关代码和系统日志,或寻求更具体的社区帮助。

回到顶部