Linux 会自动清理抽象域套接字吗?

Linux 会自动清理抽象域套接字吗?

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_REUSEADDRSO_REUSEPORT)。但是关闭套接字(通过终止显式或隐式)会释放端口,而无需清理任何内容。

相关内容