如何知道任何进程是否绑定到Unix域套接字?

时间:2011-09-13 17:25:27

标签: sockets unix-socket

我正在为Linux编写一个Unix域套接字服务器。

我很快发现Unix域套接字的一个特点是,在创建一个侦听Unix套接字创建匹配的文件系统条目时,关闭套接字不会删除它。此外,在手动删除文件系统条目之前,不可能再次bind()套接字到同一路径:bind()如果给出的路径已存在于文件系统中,则EADDRINUSE会失败并显示unlink()

因此,在服务器关闭时,套接字的文件系统条目需要EADDRINUSE,以避免在服务器重启时获得unlink()。但是,这不能总是这样做(即:服务器崩溃)。我发现的大多数常见问题解答,论坛帖子,Q& A网站仅在致电bind()之前建议unlink()套接字作为解决方法。但是,在这种情况下,需要在unlink()之前知道进程是否绑定到此套接字。

实际上,当一个进程仍然绑定到它时,flock()使用一个Unix套接字,然后重新创建监听套接字不会引发任何错误。但是,结果是,旧的服务器进程仍在运行但无法访问:旧的侦听套接字被新的监听套接字“屏蔽”。必须避免这种行为。

理想情况下,使用Unix域套接字时,套接字API应该暴露出绑定TCP或UDP套接字时暴露的相同“互斥”行为:“我想将套接字S绑定到地址A;如果是进程已经绑定了这个地址,只是抱怨!“不幸的是情况并非如此......

有没有办法强制执行这种“互斥”行为?或者,给定文件系统路径,有没有办法知道通过套接字API,系统上的任何进程是否都有绑定到此路径的Unix域套接字?我应该使用套接字API外部的同步原语(unlink(),...)?或者我错过了什么?

感谢您的建议。

注意:Linux的抽象命名空间Unix套接字似乎解决了这个问题,因为{{1}}没有文件系统条目。但是,我正在编写的服务器旨在通用:它必须对两种类型的Unix域套接字都是健壮的,因为我不负责选择监听地址。

2 个答案:

答案 0 :(得分:21)

我知道我参加派对已经很晚了,很久以前就回答了这个问题,但我刚刚遇到了这个问题,我还有其他建议。

当您从EADDRINUSE遇到bind()返回时,您可以输入连接到套接字的错误检查例程。如果连接成功,则有一个运行进程至少足够活跃以完成accept()。这让我觉得这是实现您想要实现的目标的最简单,最便携的方式。它的缺点在于,首先创建UDS的服务器实际上可能仍在运行但是以某种方式“卡住”而无法执行accept(),所以这个解决方案当然不是万无一失的,但它是我认为这是朝着正确方向迈出的一步。

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

答案 1 :(得分:9)

除了你已经考虑过的事情之外,我认为还有很多工作要做。你似乎已经很好地研究了它。

有一些方法可以确定套接字是否绑定到unix套接字(显然是lsof和netstat这样做)但它们很复杂并且系统依赖性足以让我怀疑它们是否值得努力处理你引发的问题。

您实际上提出了两个问题 - 处理与其他应用程序的名称冲突以及处理您自己的应用程序的先前实例。

根据定义,pgm的多个实例不应该尝试绑定到同一个路径,这可能意味着您一次只想运行一个实例。如果是这种情况,您可以使用标准的pid文件锁定技术,因此两个实例不会同时运行。如果无法获得锁定,则不应取消现有套接字的链接,甚至不应运行。这也会处理服务器崩溃情况。如果您可以获得锁定,那么您知道可以在绑定之前取消链接现有套接字路径。

没有太多可以做AFAIK来控制其他创建碰撞的程序。文件权限并不完美,但如果您可以使用该选项,则可以将应用程序放在自己的用户/组中。如果存在现有的套接字路径并且您不拥有它,则不要取消链接并发出错误消息并让用户或系统管理员对其进行排序。使用配置文件使其易于更改 - 并且可供客户端使用 - 可能会起作用。除此之外,您几乎不得不采用某种发现服务,除非这是一个非常关键的应用程序,否则这似乎是一种大规模的过度杀伤。

总的来说,你可以放心一点,这实际上并不经常发生。

相关问题