如何通过Linux shell命令关闭文件描述符

16
/proc/pid/fd/ 中,文件描述符过多。我可以使用shell命令关闭这些文件描述符吗?

这是哪个进程?是你的程序吗?你能发一些源代码吗? - MarkR
我曾经遇到这样一个情况,某个商业应用程序使用了超过1000个文件描述符打开同一个文件,结果导致文件描述符耗尽。该应用程序甚至无法通过自己的命令终止,所以我不得不强制结束它。如果我能够关闭其中一些文件描述符,程序可能会更加干净地终止。 - U. Windl
5个回答

41

只要您有权限,就可以关闭其他正在运行的进程的fd。

首先,找到PID。

然后,启动gdb并附加到该进程:

gdb -p 1598

然后,调用close系统调用来关闭你想要关闭的文件描述符:

(gdb) call close(999)
$1 = 0
如果文件描述符是一个泄露的,那么程序永远不会再次尝试使用它,并且不应该引起任何问题。然而,这个程序很可能有一个错误。

12

你可以在Bash中通过以下方式关闭当前进程的文件描述符 n

exec n<&-

4
谢谢。但它用于关闭当前进程(bash进程)的文件描述符。您知道关闭特定进程文件描述符的方法吗? - Eric

4

@Thomas的回答只有在安装了close()调用的调试信息时才有效。

如果没有安装调试信息,gdb将拒绝调用close()

(gdb) call close(3)
'close' has unknown return type; cast the call to its declared return type

在这种情况下,使gdb调用close()的最简单方法是将调用强制转换为close()返回类型:
(gdb) call (int)close(3)
$1 = 0

请参考 gdb 文档
有时,您想要调用的函数缺少调试信息。在这种情况下,GDB不知道函数的类型,包括函数参数的类型。为避免错误调用被调用的函数,从而导致被调用的函数出现错误甚至崩溃,除非告诉GDB函数的类型,否则GDB将拒绝调用该函数。
对于原型化(即ANSI / ISO风格)函数,有两种方法可以实现。最简单的方法是将调用强制转换为函数声明的返回类型。

可能是由于过于激进的现代符号表剥离,close()作为系统调用应该为使用共享C库的任何进程所知。 - U. Windl

3
我曾经遇到过类似的情况,但是使用会破坏我的应用程序的实时约束并扭曲我的测试结果,所以我想出了一个快速的iptables规则。方括号中的可选参数([ opt ])。
  1. Find your destination address and port:

    netstat --program [ --numeric-host --numeric-ports ] | grep [<pid>]/[<appname>]

    $ netstat --program --numeric-ports | grep 8812/
    tcp        0      0 ysc.xxx:54055          10.56.1.152:30000           ESTABLISHED 8812/my-application
    tcp        0      0 ysc.xxx:46786          postgres.xxx:5432           ESTABLISHED 8812/my-application
    tcp        0      0 ysc.xxx:36090          10.56.4.79:57000            ESTABLISHED 8812/my-application
                                          ...
    unix  2      [ ]         DGRAM                    7177020 8812/my-application
    

    Here, I'd like to cut 10.56.4.79:57000.

  2. Create an iptables rule to cut the socket:

    iptables -A OUTPUT [ --out-interface <if> --protocol <tcp|udp|unix> ] --destination <addr> --dport <port> --jump DROP

    $ iptables -A OUTPUT --destination 10.56.4.79 --dport 57000 --jump DROP
    $
    
  3. At this stage, your program can't send packets to the distant host. In most cases, the TCP connection is closed. You can proceed with your tests if there is some.

    $ netstat --program --numeric-ports | grep 8812/
    tcp        0      0 ysc.xxx:54055          10.56.1.152:30000           ESTABLISHED 8812/my-application
    tcp        0      0 ysc.xxx:46786          postgres.xxx:5432           ESTABLISHED 8812/my-application
                                          ...
    unix  2      [ ]         DGRAM                    7177020 8812/my-application
    
  4. Remove the iptables rule:

    You just type in the same iptables rule replacing the A by a D.

    $ iptables -D OUTPUT --destination 10.56.4.79 --dport 57000 --jump DROP
    $
    

请注意,我的意图是测试一下如果我的应用程序失去与关键服务的连接,它会如何反应。 - YSC
这个问题是关于文件描述符的通用问题;而你的回答只适用于网络连接。最重要的是,它并没有关闭文件描述符,只是丢弃了网络流量。 - U. Windl
@U.Windl 如果您的应用程序行为正确,这将导致TCP连接和文件描述符的结束。正如我在我的答案中所说:“在大多数情况下,TCP连接被关闭。”虽然它不是万能药,但如果它能帮助人们... - YSC
只有在TCP中使用了“keepalive”选项时才会生效:否则,死连接可能会一直保持,直到关闭或被重置。 - U. Windl
1
这可能不是完全回答了问题,但它帮助我完成了我想要做的事情。 - John Ilacqua

-6

你不能随意关闭其他进程的文件描述符并期望它们继续工作。

修复打开了太多文件的程序,使其打开较少的文件。这可能是配置更改或修改源代码等。你不能只是关闭它的文件。


11
当然可以。这种情况通常是由于程序泄漏了文件描述符(fd),因此它将永远不会再使用这些fd。虽然这是一个错误,但仍然有人想要绕过它。 - Thomas Vander Stichele
在我的情况下,我有一个长时间运行的挂起批处理,因为从http套接字读取的内容已经失效。如@ThomasVanderStichele所描述的那样关闭fd解决了我的问题 :-) - Chris Withers
2
如果你没有程序的源代码,那该怎么办呢? - klutt
你不能告诉Unix黑客他们做不了某件事。这不是它的工作方式。 - DexterHaxxor
你不能告诉一个Unix黑客他们做不到某事。这不是它的工作方式。 - DexterHaxxor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接