如何判断是否有任何进程绑定到Unix域套接字?

42
我正在为Linux编写一个Unix域套接字服务器,这里有一些关于Unix域套接字的特性需要注意:创建侦听Unix套接字会创建相应的文件系统条目,但关闭套接字并不会删除它。此外,除非手动删除文件系统条目,否则无法将套接字再次绑定到相同的路径:bind()如果给定的路径已在文件系统中存在,则会失败并返回EADDRINUSE错误。
因此,在服务器关闭时,需要使用unlink()操作删除套接字的文件系统条目,以避免在服务器重新启动时出现EADDRINUSE错误。然而,这不能总是实现(例如:服务器崩溃)。我发现大多数FAQ、论坛帖子和问答网站都建议,在调用bind()之前使用unlink()操作来解决此问题。但在这种情况下,最好知道是否有进程已经绑定到该套接字,然后再进行unlink()操作。
当仍有进程绑定到套接字时执行unlink()操作并重新创建侦听套接字不会引发任何错误。但结果是旧的服务器进程仍在运行但无法访问:新套接字掩盖了旧套接字。因此,必须避免这种行为。
理想情况下,使用Unix域套接字时,套接字API应该提供与绑定TCP或UDP套接字时相同的“互斥”行为:“如果有进程已经绑定到地址A,则指示不能将套接字S绑定到地址A!”不幸的是,事实并非如此...
有没有办法强制实现这种“互斥”行为?或者,是否可以通过套接字API知道是否有任何进程在系统上将Unix域套接字绑定到此路径?我应该使用套接字API外部的同步原语(例如flock())吗?还是我遗漏了什么?
感谢您的建议。

注意:Linux的抽象命名空间Unix套接字似乎解决了这个问题,因为没有文件系统条目可以unlink()。但是,我编写的服务器旨在具有普适性:它必须针对两种类型的Unix域套接字具有健壮性,因为我不负责选择监听地址。

3个回答

26

我知道我来晚了,并且这个问题早已有答案,但我在寻找其他内容时遇到了这个问题,我有一个替代方案。

当您遇到bind()返回的EADDRINUSE时,您可以进入一个错误检查例程,该例程连接到套接字。如果连接成功,则至少有一个正在运行的进程已经完成了accept()。对我来说,这似乎是实现您想要实现的最简单和最便携的方法。它的缺点在于,最初创建UDS的服务器可能仍然在运行,但某种方式被“卡住”,无法执行accept(),因此这个解决方案肯定不是万无一失的,但我认为这是朝着正确方向迈出的一步。

如果connect()失败,请继续unlink()端点并再次尝试bind()


1
我已经测试过了,看起来按照宣传的那样能够正常工作。太棒了! - ioquatix

12

我认为除了你已经考虑过的事情,没有太多可以做的了。你似乎已经很好地研究了它。

有一些方法可以确定套接字是否绑定到Unix套接字(显然lsof和netstat可以做到这一点),但它们足够复杂和依赖于系统,以至于我怀疑它们是否值得处理你提出的问题。

你确实提出了两个问题——解决与其他应用程序的名称冲突和处理之前的应用程序实例。

按定义,你的pgm的多个实例不应该尝试绑定到同一路径,因此这可能意味着你只想同时运行一个实例。如果是这样的话,你只需要使用标准的pid文件锁技术,这样两个实例就不会同时运行。如果你不能获得锁,你不应该删除现有的套接字甚至不能运行。这也解决了服务器崩溃的情况。如果你能获得锁,那么你知道在绑定之前可以删除现有的套接字路径。

据我所知,你很难对其他程序创建的冲突进行控制。文件权限并不完美,但如果可行的话,你可以将你的应用程序放在自己的用户/组中。如果存在一个套接字路径而你不拥有它,那就不要删除它,并输出一个错误消息,让用户或系统管理员解决它。使用配置文件使其易于更改-并提供给客户端-可能有效。除此之外,你几乎必须使用某种发现服务,除非这是一个真正关键的应用程序,否则似乎会出现极大的过度处理。

总体上,你可以放心,这种情况实际上并不经常发生。


感谢您的答复。使用传统的锁定文件系统无疑是最安全的方法。此外,关于服务发现系统是否过度:具有讽刺意味的是,该服务器本身计划成为服务发现系统的一部分(服务“注册”系统似乎更合适)。这应该回答了您的问题;-) - Simon Malinge

0

假设您只有一个打开该套接字的服务器程序。

那么这个怎么样:

  • 创建一个文件,其中包含服务器进程的PID(也可能包括套接字路径)
    • 如果成功,则在该文件中写入您的PID(和套接字路径),然后继续创建套接字。
    • 如果失败,则套接字已经被创建(很可能),但是服务器可能已经死亡。因此,请从现有文件中读取PID,然后检查是否仍存在这样的进程(例如使用带有0信号的kill):
      • 如果进程存在,则可能是服务器进程,也可能是不相关的进程
        • (这里可能需要更多步骤)
      • 如果不存在这样的进程,则删除该文件并开始尝试独占创建它。
  • 每当进程终止时,在关闭(和删除)套接字之后删除该文件。
  • 如果将套接字和锁定文件都放在易失性文件系统中(旧时代的/tmp,现代的/run),则重新启动将自动清除旧套接字和锁定文件。
  • 除非管理员喜欢使用kill -9,否则您还可以建立一个信号处理程序,当接收到致命信号时尝试删除锁定文件。

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