StackOverflow 上有一个很好的答案为守护进程提供更好的锁(合成自爱德华多·弗勒里),不依赖于守护进程的通用 PID 文件锁定机制。关于为什么 PID 锁定文件有时会导致问题,有很多很好的评论,所以我不会在这里重复它们。
简而言之,该解决方案依赖于 Linux 抽象名称空间域套接字,它可以按名称跟踪套接字,而不是依赖于文件,文件在守护进程被 SIGKILL 后仍会保留下来。该示例表明,一旦进程死亡,Linux 似乎就会释放套接字。
但我在 Linux 中找不到明确的文档来说明当绑定进程被 SIGKILL 标记时 Linux 到底对抽象套接字做了什么。有人知道吗?
换句话说,抽象套接字到底什么时候被释放以供再次使用?
我不想用抽象套接字替换 PID 文件机制,除非它能彻底解决问题。
答案1
我一年多前发布了这个问题,并且对缺乏明确的文档一直不太满意。我想我应该再次检查 Linux 文档以获取任何更新,并且很高兴看到这个:
抽象套接字
套接字权限对于抽象套接字没有任何意义:绑定抽象套接字时,进程 umask(2) 不起作用,并且更改对象的所有权和权限(通过 fchown(2) 和 fchmod(2))对抽象套接字没有影响。套接字的可访问性。
当所有对套接字的打开引用都关闭时,抽象套接字会自动消失。
还,Linux 编程接口经过迈克尔·克里克涵盖了问题(交叉发布自这个另一个答案):
57.6 Linux 抽象套接字命名空间
所谓的抽象命名空间是 Linux 特有的功能,它允许我们将 UNIX 域套接字绑定到一个名称,而无需在文件系统中创建该名称。这提供了一些潜在的优势:
- 我们不需要担心与文件系统中现有名称可能发生冲突。
- 当我们使用完套接字后,没有必要取消套接字路径名的链接。当套接字关闭时,抽象名称将自动删除。
- 我们不需要为套接字创建文件系统路径名。这在 chroot 环境中或者如果我们没有对文件系统的写访问权限时可能很有用。
要创建抽象绑定,我们指定第一个字节 太阳路径字段为空字节 (\0)。 [...]
我认为,连同@user3188445的回答,这非常准确地澄清了问题。
也就是说,这里仍然有一个假设,即 SIGKILL 的进程将关闭所有打开的套接字。这似乎是一个合理的假设,但我没有定义该行为的文档。
答案2
是的,Linux 会自动“清理”抽象套接字,甚至清理到有意义的程度。这是一个最小的工作示例,您可以用它来验证这一点:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
int
main(int argc, char **argv)
{
int s;
struct sockaddr_un sun;
if (argc != 2 || strlen(argv[1]) + 1 > sizeof(sun.sun_path)) {
fprintf(stderr, "usage: %s abstract-path\n", argv[0]);
exit(1);
}
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
exit(1);
}
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path + 1, argv[1]);
if (bind(s, (struct sockaddr *) &sun, sizeof(sun))) {
perror("bind");
exit(1);
}
pause();
}
运行该程序./a.out /test-socket &
,然后运行ss -ax | grep test-socket
,您将看到正在使用的套接字。然后kill %./a.out
, 和ss -ax
将显示套接字已消失。
然而,您在任何文档中都找不到此清理的原因是,它并不是像非抽象 unix 域套接字需要清理那样真正进行清理。非抽象套接字实际上会分配一个索引节点并在目录中创建一个条目,该条目需要在底层文件系统中进行清理。相比之下,抽象套接字更像是 TCP 或 UDP 端口号。当然,如果您绑定了一个 TCP 端口然后退出,该 TCP 端口将再次释放。但无论你使用什么 16 位数字仍然抽象地存在并且始终存在。端口号的命名空间是1-65535,永远不会改变或需要清理。
因此,只需将抽象套接字名称想象为 TCP 或 UDP 端口号,只是从一组更大的可能端口号中选取,这些端口号碰巧看起来像路径名,但实际上并非如此。您不能两次绑定相同的端口号(禁止SO_REUSEADDR
或SO_REUSEPORT
)。但是关闭套接字(通过终止显式或隐式)会释放端口,而无需清理任何内容。