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


15

在StackOverflow上有一个很好的答案,那就是为守护程序(由Eduardo Fleury合成)提供更好的锁定,而该锁定不依赖于守护程序的通用PID文件锁定机制。关于为什么PID锁定文件有时会引起问题,有很多很好的评论,因此在这里我不会对其进行哈希处理。

简而言之,该解决方案依赖于Linux抽象名称空间域套接字,该套接字为您按名称跟踪套接字,而不是依赖文件,该文件在守护进程被SIGKILL处理后仍会存在。该示例表明,一旦进程终止,Linux似乎可以释放套接字。

但是我找不到在Linux中的权威文档,该文档没有说明当绑定进程进行SIGKILL操作时Linux对抽象套接字的确切作用。有人知道吗?

换句话说,确切地说什么时候抽象套接字可以释放以供再次使用?

我不想用抽象套接字替换PID文件机制,除非它能最终解决问题。


3
我找不到任何可以直接回答这个问题的东西。但是由于没有用于删除抽象套接字的API,因此似乎必须由内核自动对其进行管理。当没有打开套接字的进程时,它应该消失。
Barmar

@Barmar公平。愿意将其添加为答案吗?
CivFan

我希望有更多明确的信息。
Barmar

Answers:


5

是的,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.outss -ax将显示套接字已消失。

但是,在任何文档中都找不到此清理的原因是,它实际上不是以非抽象的unix域套接字需要清理的方式清理。非抽象套接字实际上会分配一个inode并在目录中创建一个条目,需要在基础文件系统中清除该条目。相比之下,将抽象套接字想像成TCP或UDP端口号。当然,如果您绑定一个TCP端口然后退出,则该TCP端口将再次释放。但是您使用的任何16位数字仍然抽象存在并且始终存在。端口号的名称空间是1-65535,并且从不更改或需要清洗。

因此,只需将抽象套接字名称想像为TCP或UDP端口号,就可以从一组更大的可能看起来像路径名而不是路径名的端口号中选择。您不能两次绑定相同的端口号(禁止SO_REUSEADDRSO_REUSEPORT)。但是关闭套接字(显式或隐式终止)可以释放端口,而无需清理任何内容。


它已经通过了鸭子测试。对于某些事情(例如python)来说,这很好,但是我希望Linux内核功能更多。以您的TCP / UDP端口为例-有大量文档准确描述了何时可以重用该端口。
CivFan 2015年

2
为什么该文档不能同样适用于抽象套接字?你有参考吗?一个更好的问题可能是在哪里记录了非抽象Unix域套接字的额外清理复杂性。在我的系统上,它在unix(7)中,它表示“ Linux还支持独立于文件系统的抽象名称空间”。因此对我来说,“独立于文件系统”意味着没有特定于文件系统的清理。
user3188445

5

一年多以前,我曾发布过此问题,但对于缺乏确定性的文档,我从未感到满意。我以为我会再次检查Linux文档以获取任何更新,很高兴看到这一点

抽象套接字

套接字权限对抽象套接字没有意义:进程umask(2)在绑定抽象套接字时不起作用,并且更改对象的所有权和权限(通过fchown(2)和fchmod(2))对套接字无效。套接字的可访问性。

当关闭所有对套接字的打开的引用时,抽象套接字将自动消失。

此外,Linux的编程接口迈克尔·凯里斯克覆盖问题(来自交叉张贴这对方的回答):

57.6 Linux抽象套接字命名空间

所谓的抽象名称空间是一种特定于Linux的功能,它使我们能够将UNIX域套接字绑定到一个名称,而无需在文件系统中创建该名称。这提供了一些潜在的优势:

  • 我们无需担心与文件系统中现有名称可能发生冲突。
  • 完成使用套接字后,无需取消链接套接字路径名。关闭套接字后,抽象名称将自动删除。
  • 我们不需要为套接字创建文件系统路径名。在chroot环境中,或者如果我们没有文件系统的写访问权,这可能很有用。

要创建抽象绑定,我们将sun_path字段的第一个字节指定 为空字节(\ 0)。[...]

我认为,连同@ user3188445的答案一样,这可以非常精确地解决这个问题。

就是说,这里仍然有一个假设,就是SIGKILL将关闭所有打开的套接字的进程。这似乎是一个合理的假设,但是我没有定义该行为的文档。


1
最后一段:套接字是文件描述符,当进程退出时,所有打开的文件描述符都关闭。伊什 更具体地说,套接字是一个打开的文件,打开的文件可以传递,例如由子进程继承。因此SOCK_CLOEXEC,如果任何代码(包括库)执行fork()+ exec(),则应确保使用调用socket 。使用不带exec()的fork()创建额外的子进程的情况并不常见;您可能已经知道您是否正在执行此操作。
sourcejedi

不必取消链接...-嗯,由于没有路径名,因此无法取消链接,而不仅仅是不必要的。
domen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.