谁有这个unix socketpair的另一端?

Jon*_*art 68 process lsof open-files socket

我想确定哪个进程具有 UNIX 套接字的另一端。

具体来说,我问的是用 来创建的socketpair(),尽管任何 UNIX 套接字的问题都是一样的。

我有一个程序parent可以创建一个socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 和fork()s。父进程关闭fds[1]并保持fds[0]通信。孩子反其道而行之,close(fds[0]); s=fds[1]。然后孩子exec()的另一个程序,child1。两者可以通过这个 socketpair 来回通信。

现在,假设我知道parent是谁,但我想弄清楚是谁child1。我该怎么做呢?

有几种工具可供我使用,但没有一个可以告诉我套接字的另一端是哪个进程。我试过了:

  • lsof -c progname
  • lsof -c parent -c child1
  • ls -l /proc/$(pidof server)/fd
  • cat /proc/net/unix

基本上,我可以看到两个套接字以及关于它们的所有内容,但无法判断它们是否已连接。我试图确定父进程中的哪个 FD 正在与哪个子进程通信。

Sté*_*las 52

注意:我现在维护一个lsof包装器,它结合了此处描述的两种方法,并在https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc 上添加了环回 TCP 连接对等点的信息

Linux-3.3 及以上。

在 Linux 上,由于内核版本 3.3(并且如果该UNIX_DIAG功能内置在内核中),可以使用新的基于netlink的 API获取给定 unix 域套接字(包括 socketpairs)的对等点。

lsof 因为 4.89 版可以使用该 API:

lsof +E -aUc Xorg
Run Code Online (Sandbox Code Playgroud)

将列出所有具有名称Xorg以任一端开头的进程的 Unix 域套接字,格式类似于:

Xorg       2777       root   56u  unix 0xffff8802419a7c00      0t0   34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Run Code Online (Sandbox Code Playgroud)

如果您的版本lsof太旧,还有更多选择。

ss(从实用程序iproute2)利用相同的API来检索和UNIX域套接字的系统,其包括对等体的信息的列表的显示信息的。

套接字由它们的inode 编号标识。请注意,它与套接字文件的文件系统 inode 无关。

例如在:

$ ss -x
[...]
u_str  ESTAB    0    0   @/tmp/.X11-unix/X0 3435997     * 3435996
Run Code Online (Sandbox Code Playgroud)

它说套接字 3435997(绑定到 ABSTRACT 套接字/tmp/.X11-unix/X0)与套接字 3435996 连接。该-p选项可以告诉您哪个进程打开了该套接字。它通过在readlink上执行一些s 来/proc/$pid/fd/*做到这一点,因此它只能在您拥有的进程上执行此操作(除非您是root)。例如这里:

$ sudo ss -xp
[...]
u_str  ESTAB  0  0  @/tmp/.X11-unix/X0 3435997 * 3435996 users:(("Xorg",pid=3080,fd=83))
[...]
$ sudo ls -l /proc/3080/fd/23
lrwx------ 1 root root 64 Mar 12 16:34 /proc/3080/fd/83 -> socket:[3435997]
Run Code Online (Sandbox Code Playgroud)

要找出哪个进程有 3435996,您可以在以下输出中查找它自己的条目ss -xp

$ ss -xp | awk '$6 == 3435996'
u_str  ESTAB  0  0  * 3435996  * 3435997 users:(("xterm",pid=29215,fd=3))
Run Code Online (Sandbox Code Playgroud)

您还可以使用此脚本作为包装器lsof来轻松显示相关信息:

lsof +E -aUc Xorg
Run Code Online (Sandbox Code Playgroud)

例如:

$ sudo that-lsof-wrapper -ad3 -p 29215
命令 PID 用户 FD 类型 设备大小/关闭节点名称
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type=STREAM <-> 3435997[Xorg,3080,@/tmp/.X11-unix/X0]

在 linux-3.3 之前

用于检索 unix 套接字信息的旧 Linux API 是通过/proc/net/unix文本文件。它列出了所有 Unix 域套接字(包括 socketpairs)。kernel.kptr_restrict正如@Totor 已经解释的那样,其中的第一个字段(如果没有使用sysctl 参数对非超级用户隐藏)包含一个unix_sock结构的内核地址,该结构包含一个peer指向相应peer 的字段unix_sock。这也是Unix 套接字上列的lsof输出DEVICE

现在获取该peer字段的值意味着能够读取内核内存并知道该peer字段相对于unix_sock地址的偏移量。

已经给出了几个gdb基于systemtap基于的解决方案,但它们需要gdb/systemtap和 Linux 内核调试符号来安装正在运行的内核,这在生产系统上通常不是这种情况。

硬编码偏移量并不是一个真正的选项,因为它随内核版本而变化。

现在,我们可以将决定使用启发式方法的偏差:有我们的工具来创建一个虚拟的socketpair(后来我们知道,这两个同龄人的地址),并搜索的地址周围的内存在另一端,以确定偏移。

这是一个概念验证脚本,它使用perl(在 i386 上使用内核 2.4.27 和 2.6.32 以及在 amd64 上使用 3.13 和 3.16 成功测试)。像上面一样,它作为一个包装器工作lsof

例如:

$ that-lsof-wrapper -aUc nm-applet
命令 PID 用户 FD 类型 设备大小/关闭节点名称
纳米小应用程序4183的Stephane 4U UNIX 0xffff8800a055eb40 0t0 36888型= STREAM - > 0xffff8800a055e7c0 [DBUS守护,4190,@ / TMP / DBUS-AiBCXOnuP6]
纳米小程序4183的Stephane 7U UNIX 0xffff8800a055e440 0t0 36890型= STREAM - > 0xffff8800a055e0c0 [Xorg则3080,@ / TMP / .X11-UNIX / X0]
纳米小程序4183的Stephane 8U UNIX 0xffff8800a05c1040 0t0 36201型= STREAM - > 0xffff8800a05c13c0 [DBUS守护,4118,@ / TMP / DBUS-yxxNr1NkYC]
纳米小程序4183的Stephane 11U UNIX 0xffff8800a055d080 0t0 36219型= STREAM - > 0xffff8800a055d400 [DBUS守护,4118,@ / TMP / DBUS-yxxNr1NkYC]
纳米小程序4183的Stephane 12U UNIX 0xffff88022e0dfb80 0t0 36221型= STREAM - > 0xffff88022e0df800 [DBUS守护,2268,/ VAR /run/dbus/system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type=STREAM -> 0xffff88022e29ec00[dbus-daemon,2268,/var/run/dbus/system_bus_socket]

这是脚本:

Xorg       2777       root   56u  unix 0xffff8802419a7c00      0t0   34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Run Code Online (Sandbox Code Playgroud)


Gil*_*il' 34

从内核 3.3 开始,可以使用sslsof-4.89或更高版本 - 请参阅Stéphane Chazelas 的回答

根据作者的说法,在旧版本中lsof是不可能发现这一点的:Linux 内核不会公开这些信息。来源:2003 年 comp.unix.admin 上的线程

中显示/proc/$pid/fd/$fd的数字是虚拟套接字文件系统中套接字的 inode 编号。创建管道或套接字对时,每一端都会连续收到一个 inode 编号。数字按顺序分配,因此数字相差 1 的可能性很高,但这不能保证(因为第一个套接字是N并且N +1 由于包装已经在使用中,或者因为其他一些线程在两个 inode 分配之间调度,并且该线程也创建了一些 inode)。

我查看了内核2.6.39中定义,socketpair socket的两端除了type-specificsocketpair方法外没有关联。对于 unix 套接字,unix_socketpairnet/unix/af_unix.c.

  • 谢谢@吉尔斯。我确实记得前一段时间读过一些关于它的东西,但无法再次找到它。我可能只需要为 /proc/net/unix 编写补丁。 (2认同)

Tot*_*tor 10

从内核 3.3 开始

可以 现在获得这些信息ss

# ss -xp
Run Code Online (Sandbox Code Playgroud)

现在您可以在Peer列中看到一个 ID(inode 编号),它对应于该Local列中的另一个 ID 。匹配的 ID 是套接字的两端。

注意:UNIX_DIAG必须在内核中启用该选项。

内核 3.3 之前

Linux 没有将此信息公开给用户空间。

但是,通过查看内核内存,我们可以访问此信息。

注意:此答案是通过使用 来实现的gdb,但是,请参阅@StéphaneChazelas 的答案,该答案在这方面进行了更详细的说明。

# lsof | grep whatever
mysqld 14450 (...) unix 0xffff8801011e8280 (...) /var/run/mysqld/mysqld.sock
mysqld 14450 (...) unix 0xffff8801011e9600 (...) /var/run/mysqld/mysqld.sock
Run Code Online (Sandbox Code Playgroud)

有 2 个不同的套接字,1 个侦听和 1 个建立。六进制数是对应内核unix_sock结构的地址,有一个peer属性是套接字另一端的地址(也是一个unix_sock结构实例)。

现在我们可以使用gdb来查找peer内核内存:

# gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((struct unix_sock*)0xffff8801011e9600)->peer
$1 = (struct sock *) 0xffff880171f078c0

# lsof | grep 0xffff880171f078c0
mysql 14815 (...) unix 0xffff880171f078c0 (...) socket
Run Code Online (Sandbox Code Playgroud)

在这里,套接字的另一端由mysqlPID 14815 保存。

您的内核必须编译KCORE_ELF为使用/proc/kcore. 此外,您需要一个带有调试符号的内核映像版本。在 Debian 7 上,apt-get install linux-image-3.2.0-4-amd64-dbg将提供此文件。

不需要可调试的内核映像...

如果您没有(或不想保留)系统上的调试内核映像,您可以提供gdb内存偏移量以“手动”访问该peer值。此偏移值通常因内核版本或架构而异。

在我的内核上,我知道偏移量为 680 字节,即 85 乘以 64 位。所以我可以这样做:

# gdb /boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((void**)0xffff8801011e9600)[85]
$1 = (void *) 0xffff880171f078c0
Run Code Online (Sandbox Code Playgroud)

瞧,结果和上面一样。

如果您在多台机器上运行相同的内核,则使用此变体会更容易,因为您不需要调试映像,只需要偏移值。

要(轻松)首先发现此偏移值,您确实需要调试映像:

$ pahole -C unix_sock /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
struct unix_sock {
  (...)
  struct sock *              peer;                 /*   680     8 */
  (...)
}
Run Code Online (Sandbox Code Playgroud)

给你,680 字节,这是 85 x 64 位,或 170 x 32 位。

这个答案的大部分功劳归功于MvG

  • 另一种检索偏移量的方法是创建一个套接字对,根据 /proc/pif/fd/* 上的 readlinks 中的 inode 编号识别 /proc/net/unix 中的相应条目,并扫描一个套接字地址周围的内存对方的地址。这可以使 lsof 本身实现合理的可移植性(跨 Linux 版本和体系结构)。我会尝试提出一个 PoC。 (2认同)
  • 我现在添加了 [此类 PoC](http://unix.stackexchange.com/a/190606),它似乎在我测试过的系统上运行良好。 (2认同)

imz*_*hev 9

Erkki Seppala 实际上有一个工具可以用 gdb 从 Linux 内核中检索这些信息。它可以在这里找到

  • 非常有用的信息!即使这个工具对我来说不是开箱即用的(它导致了内核糟糕),这个想法帮助我确定了另一端。我在 Stack Overflow 上[描述了我的解决方案](http://stackoverflow.com/a/11900231/1468366)。 (2认同)

Sté*_*las 5

这个解决方案虽然有效,但兴趣有限,因为如果你有一个足够新的 systemtap,你很可能会有一个足够新的内核,你可以在其中使用ss基于 方法,如果你使用的是旧内核,那么其他解决方案,尽管更hacky更有可能工作并且不需要额外的软件。

作为演示如何使用systemtap此类任务仍然很有用。

如果在具有工作 systemtap(1.8 或更高版本)的最新 Linux 系统上,您可以使用下面的脚本对输出进行后处理lsof

例如:

$ lsof -aUc nm-applet | 须藤那个脚本
命令 PID 用户 FD 类型 设备大小/关闭节点名称
纳米小应用程序4183的Stephane 4U UNIX 0xffff8800a055eb40 0t0 36888型= STREAM - > 0xffff8800a055e7c0 [DBUS守护,4190,@ / TMP / DBUS-AiBCXOnuP6]
纳米小程序4183的Stephane 7U UNIX 0xffff8800a055e440 0t0 36890型= STREAM - > 0xffff8800a055e0c0 [Xorg则3080,@ / TMP / .X11-UNIX / X0]
纳米小程序4183的Stephane 8U UNIX 0xffff8800a05c1040 0t0 36201型= STREAM - > 0xffff8800a05c13c0 [DBUS守护,4118,@ / TMP / DBUS-yxxNr1NkYC]
纳米小程序4183的Stephane 11U UNIX 0xffff8800a055d080 0t0 36219型= STREAM - > 0xffff8800a055d400 [DBUS守护,4118,@ / TMP / DBUS-yxxNr1NkYC]
纳米小程序4183的Stephane 12U UNIX 0xffff88022e0dfb80 0t0 36221型= STREAM - > 0xffff88022e0df800 [DBUS守护,2268,/ VAR /run/dbus/system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type=STREAM -> 0xffff88022e29ec00[dbus-daemon,2268,/var/run/dbus/system_bus_socket]

(如果您在上面看到 0x0000000000000000 而不是 0xffff...,那是因为kernel.kptr_restrict系统上设置了sysctl 参数,这会导致内核指针对非特权进程隐藏,在这种情况下,您需要以lsofroot 身份运行才能获得有意义的结果)。

该脚本不会尝试处理带有换行符的套接字文件名,但也不会lsof(也不lsof处理空格或冒号)。

systemtap这里用于转储内核中散列中所有unix_sock结构的地址和对等地址unix_socket_table

仅在带有 systemtap 2.6 的 Linux 3.16 amd64 和带有 2.3 的 3.13 上测试。

$ lsof -aUc nm-applet | sudo that-script
COMMAND    PID     USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
nm-applet 4183 stephane    4u  unix 0xffff8800a055eb40      0t0 36888 type=STREAM -> 0xffff8800a055e7c0[dbus-daemon,4190,@/tmp/dbus-AiBCXOnuP6]
nm-applet 4183 stephane    7u  unix 0xffff8800a055e440      0t0 36890 type=STREAM -> 0xffff8800a055e0c0[Xorg,3080,@/tmp/.X11-unix/X0]
nm-applet 4183 stephane    8u  unix 0xffff8800a05c1040      0t0 36201 type=STREAM -> 0xffff8800a05c13c0[dbus-daemon,4118,@/tmp/dbus-yxxNr1NkYC]
nm-applet 4183 stephane   11u  unix 0xffff8800a055d080      0t0 36219 type=STREAM -> 0xffff8800a055d400[dbus-daemon,4118,@/tmp/dbus-yxxNr1NkYC]
nm-applet 4183 stephane   12u  unix 0xffff88022e0dfb80      0t0 36221 type=STREAM -> 0xffff88022e0df800[dbus-daemon,2268,/var/run/dbus/system_bus_socket]
nm-applet 4183 stephane   13u  unix 0xffff88022e0f80c0      0t0 37025 type=STREAM -> 0xffff88022e29ec00[dbus-daemon,2268,/var/run/dbus/system_bus_socket]