AF_UNIX 套接字路径中“\0hidden”的目的是啥?

Posted

技术标签:

【中文标题】AF_UNIX 套接字路径中“\\0hidden”的目的是啥?【英文标题】:What is the purpose of "\0hidden" in an AF_UNIX socket path?AF_UNIX 套接字路径中“\0hidden”的目的是什么? 【发布时间】:2022-01-06 12:22:23 【问题描述】:

我遵循了一个关于如何使用 Linux Sockets API 使 Linux 上的两个进程进行通信的教程,这就是它显示的实现它的代码:

连接代码:

char* socket_path = "\0hidden";
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
memset(&addr, 0x0, sizeof(addr));
addr.sun_family = AF_UNIX;
*addr.sun_path = '\0';
strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);
connect(fd, (struct sockaddr*)&addr, sizeof(addr));

监听代码:

char* socket_path = "\0hidden";
struct sockaddr_un addr;
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
memset(&addr, 0x0, sizeof(addr));
addr.sun_family = AF_UNIX;
*addr.sun_path = '\0';
strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 5);

基本上,我用 C 为网站编写了一个 Web 服务器,并用 C++ 编写了一个数据库管理系统,并让它们进行通信(在用户的浏览器向我的 Web 服务器发送 HTTP 请求后,它正在使用 AF_INET 进行监听家庭套接字,但这在这里并不重要,只是一些上下文)使用这种机制。数据库系统正在使用它的套接字进行侦听,并且 Web 服务器使用自己的套接字连接到它。它一直运行良好。

但是,我从来不明白套接字路径开头的空字节的目的是什么。比如,"\0hidden" 到底是什么意思,或者它有什么作用?我阅读了有关套接字的联机帮助页,它说明了有关虚拟套接字的一些内容,但它对我来说太技术性了,无法了解正在发生的事情。我对将套接字表示为具有文件描述符的文件的概念也没有清楚的理解。我也不明白strncpy() 的作用。我什至不明白 web 服务器是如何用这个代码块找到数据库系统的,是因为它们的进程都是从同一目录中的可执行文件启动的,还是因为数据库系统是整个系统上唯一监听的进程在 AF_UNIX 套接字上,还是什么?

如果有人能解释一下这个让我困惑了很久的 Linux Sockets API,我将不胜感激。我用谷歌搜索并查看了多个地方,每个人似乎都只是在使用"\0hidden" 而没有解释它,就好像这是每个人都应该知道的一些基本知识。就像,我在这里遗漏了一些理论还是什么?非常感谢任何人提前解释!

【问题讨论】:

来自manpage:“通过 sun_path[0] 是空字节这一事实来区分抽象套接字地址(与路径名套接字)”。这意味着路径名不是文件系统中的路径名,而是套接字的抽象名称。 抽象套接字地址命名空间也被描述为“不可移植的 Linux 扩展”。 "我什至不明白 web 服务器是如何使用这个代码块找到数据库系统的" 两个 sn-ps 中的路径名相同。这就是网络服务器找到数据库服务器的方式。他们必须同意相同的套接字名称。您也可以将其更改为 "\0databaseserver" 因此操作系统保留了一组内部套接字名称(仅使用这部分 API 的套接字)并且它连接一个套接字到另一个 监听 i> 仅当它们在 bind()listen() 调用中使用的 sockaddr_un 结构的 addr.sun_path 成员中具有完全相同的条目时才使用套接字,并且它们都以“\0”开头,仅当您确实想使用时这个socket定位方法? 【参考方案1】:

这是特定于AF_UNIX 本地套接字的Linux 内核实现。如果给出套接字名称的字符数组是一个空字符串,则该名称不引用文件系统命名空间中的任何内容;字符数组的剩余字节被视为位于内核内存中的内部名称。请注意,此名称不是以 null 结尾的;字符数组中的所有字节都是重要的,无论它们的值如何。 (因此,您的示例程序在复制名称之前将结构的memset 处理为零字节是一件好事。)

这允许应用程序命名套接字集合点,这些点不占用文件系统中的节点,因此更类似于 TCP 或 UDP 端口号(也不位于文件系统中)。当所有引用它们的套接字都关闭时,这些集合点会自动消失。

文件系统中的节点有一些缺点。创建和访问它们需要一个存储设备。为了防止这种情况,可以在内存中存在的临时文件系统中创建它们,例如 Linux 中的tmpfs;但是tmpfs 条目几乎肯定比AF_UNIX 实现中的专用条目访问速度更慢并且占用更多RAM。如果应用程序崩溃,临时需要的套接字(例如,在应用程序运行时)可能会保留下来,需要外部干预来清理它们。

hidden 可能不是一个好的套接字名称;程序应该利用空间并使用准保证不会与其他任何人发生冲突的东西。该名称允许超过 100 个字符,因此使用某种 UUID 字符串可能是个好主意。

Linux 程序员手册man 页面称这种地址为“抽象”。它与“未命名”截然不同。

任何标准的AF_UNIX 实现都提供“未命名”套接字,可以通过两种方式创建:任何AF_UNIX 套接字已使用socket 创建但未使用bind 指定地址;并且socketpair 创建的这对套接字是未命名的。

有关详细信息,请参阅

man 7 unix

在一些安装了 Linux 手册页的 GNU/Linux 发行版中。

【讨论】:

哇,非常感谢您的解释,您是魔术师!我现在肯定更好地理解了。附带问题,The Linux Programming Interface 2010 年的书值得一​​读吗?再次非常感谢您的详细回答:) 我不知道;早在 2010 年之前到现在,我都没有读过任何此类书籍。如果您从未阅读过任何此类系统编程书籍,那么这可能是个好主意。二十多年前,我读过 APUE(Unix 环境中的高级编程)。在那个时代之后,我停止消费那种东西,“一切都变得一样了”,你在阅读实际文档和找到你需要的东西方面变得更好。【参考方案2】:

\0 只是将NUL 字符放入字符串中。由于NUL 字符用于终止字符串,所以对于所有C 字符串函数socket_path 看起来像一个空字符串,而实际上并非如此,但它们会在第一个字符之后停止处理。

所以我的记忆socket_path实际上是这样的:

char socket_path[] =  `\0`, `h`, `i`, `d`, `d`, `e`, `n`, `\0` ;

由于所有字符串都会自动附加一个终止NUL

线

strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);

socket_path 的字节复制到套接字地址结构addr,但跳过第一个(NUL)字节以及最后一个字节(也是NUL)。因此套接字的地址实际上就是"hidden"这个词。

但是由于addr.sun_path中的第一个字节也被省略了,并且这个字节之前已经被memset初始化为NUL,所以实际路径仍然是\0hidden

那么为什么会有人这样做呢?可能是为了隐藏套接字,因为通常系统将文件系统中的 UNIX 套接字显示为实际路径条目,但我知道没有文件系统可以处理 \0 字符。因此,如果名称有 \0 字符,它不会出现在文件系统中,但这样的字符只允许作为第一个字符,否则系统仍会尝试创建该路径条目并失败,因此套接字创建将失败。仅作为第一个字符,系统甚至不会尝试创建它,这意味着您无法通过在终端中调用 ls 看到该套接字,任何想要连接它的人都需要知道它的名称。

请注意,这不符合 POSIX,因为 POSIX 期望 UNIX 套接字始终出现在文件系统中,因此只有对正在使用的文件系统合法的字符才允许作为套接字名称。这仅适用于 Linux。

【讨论】:

我知道 C 字符串的语义,我的问题是“\0hidden”在 Linux Sockets API 的上下文中是什么意思/做什么。不过,cmets 似乎在解释方面做得很好:) 谢谢! @KevinStefanov 它只是套接字的名称。并且需要\0 来区分这些抽象路径名和真实路径名。它就像一个隐藏在一个空字符串后面的字符串。 我明白了,非常感谢@Gerhardh 你的 cmets 真的为我澄清了 :)

以上是关于AF_UNIX 套接字路径中“\0hidden”的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

客户端/服务器套接字通信 (AF_UNIX)

SOCK_DGRAM 和 SOCK_STREAM 在上下文 AF_UNIX 套接字中的用途是啥?

AF_UNIX 套接字:我可以在进程之间传递套接字句柄吗?

一起talk C栗子吧(第一百五十六回:C语言实例--基于AF_UNIX域的流套接字通信)

适用于 Windows 的 AF_UNIX [重复]

Linux-进程间通信: 域套接字