给定一个 X11 窗口 ID,有没有办法找到创建它的进程的 ID?
当然,这并不总是可能的,例如,如果窗口来自 TCP 连接。对于这种情况,我想要与远程端关联的 IP 和端口。
之前在 Stack Overflow 上问过这个问题,建议的方法是使用该_NET_WM_PID属性。但这是由应用程序设置的。如果应用程序运行不佳,有没有办法做到这一点?
小智 90
除非你的X服务器支持XResQueryClientIds来自X-资源V1.2扩展我知道有没有简单的方法来可靠地请求进程ID。不过还有其他办法。
如果您面前只有一个窗口并且还不知道它的 ID — 很容易找到它。只需在相关窗口旁边打开一个终端,在xwininfo那里运行并单击该窗口。xwininfo将向您显示窗口 ID。
因此,让我们假设您知道一个窗口 ID,例如 0x1600045,并且想要查找拥有它的进程是什么。
检查该窗口属于谁的最简单方法是为其运行 XKillClient ,即:
xkill -id 0x1600045
Run Code Online (Sandbox Code Playgroud)
并查看哪个进程刚刚死亡。但前提是你当然不介意杀死它!
另一种简单但不可靠的方法是检查它的_NET_WM_PID和WM_CLIENT_MACHINE属性:
xprop -id 0x1600045
Run Code Online (Sandbox Code Playgroud)
那是什么样的工具xlsclients和xrestop做。
不幸的是,这些信息可能是不正确的,不仅因为该过程是邪恶的并改变了这些,还因为它有缺陷。例如,在一些 firefox 崩溃/重启之后,我看到孤立的窗口(我猜是来自 flash 插件)_NET_WM_PID指向一个进程,该进程很久以前就死了。
另一种方法是运行
xwininfo -root -tree
Run Code Online (Sandbox Code Playgroud)
并检查相关窗口的父项的属性。这也可能会给你一些关于窗口起源的提示。
但!虽然您可能找不到创建该窗口的进程,但仍有一种方法可以找到该进程从何处连接到 X-server。这种方式适用于真正的黑客。:)
您知道的低位清零(即 0x1600000)的窗口 ID 0x1600045 是“客户群”。为该客户端分配的所有资源 ID 都是“基于”它的(0x1600001、0x1600002、0x1600003 等)。X-server 将有关其客户端的信息存储在 clients[] 数组中,对于每个客户端,其“基础”存储在 clients[i]->clientAsMask 变量中。要找到X插槽,对应于客户端,你需要重视的X服务器gdb,走在客户的[]数组中,发现客户端与clientAsMask和打印其socket描述符,存储在((OsCommPtr)(客户端[I] - >osPrivate))->fd。
可能有很多 X 客户端连接,所以为了不手动检查它们,让我们使用 gdb 函数:
define findclient
set $ii = 0
while ($ii < currentMaxClients)
if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0)
print ((OsCommPtr)(clients[$ii]->osPrivate))->fd
end
set $ii = $ii + 1
end
end
Run Code Online (Sandbox Code Playgroud)
找到socket后,可以查看,谁连接上了,最后找到进程。
警告:不要从 X 服务器内部将 gdb 附加到 X 服务器。gdb 挂起它附加到的进程,因此如果您从 X-session 内部附加到它,您将冻结您的 X-server 并且将无法与 gdb 交互。您必须切换到文本终端 ( Ctrl+Alt+F2) 或通过 ssh 连接到您的机器。
找到您的 X 服务器的 PID:
$ ps ax | grep X
1237 tty1 Ssl+ 11:36 /usr/bin/X :0 vt1 -nr -nolisten tcp -auth /var/run/kdm/A:0-h6syCa
Run Code Online (Sandbox Code Playgroud)窗口 id 是 0x1600045,所以客户群是 0x1600000。附加到 X 服务器并找到该客户端的客户端套接字描述符。您需要为 X-server 安装调试信息(rpm-distributions 的 -debuginfo 包或 deb 的 -dbg 包)。
$ sudo gdb
(gdb) define findclient
Type commands for definition of "findclient".
End with a line saying just "end".
> set $ii = 0
> while ($ii < currentMaxClients)
> if (clients[$ii] != 0 && clients[$ii]->clientAsMask == $arg0 && clients[$ii]->osPrivate != 0)
> print ((OsCommPtr)(clients[$ii]->osPrivate))->fd
> end
> set $ii = $ii + 1
> end
> end
(gdb) attach 1237
(gdb) findclient 0x1600000
$1 = 31
(gdb) detach
(gdb) quit
Run Code Online (Sandbox Code Playgroud)现在您知道客户端已连接到服务器套接字 31。使用lsof来查找该套接字是什么:
$ sudo lsof -n | grep 1237 | grep 31
X 1237 root 31u unix 0xffff810008339340 8512422 socket
Run Code Online (Sandbox Code Playgroud)
(这里“X”是进程名,“1237”是它的pid,“root”是运行它的用户,“31u”是一个套接字描述符)
在那里您可能会看到客户端通过 TCP 连接,然后您可以转到它所连接的机器并检查netstat -nap那里以查找进程。但很可能你会在那里看到一个 unix 套接字,如上所示,这意味着它是一个本地客户端。
要为该 unix 套接字找到一对,您可以使用MvG 的技术 (您还需要安装内核的调试信息):
$ sudo gdb -c /proc/kcore
(gdb) print ((struct unix_sock*)0xffff810008339340)->peer
$1 = (struct sock *) 0xffff810008339600
(gdb) quit
Run Code Online (Sandbox Code Playgroud)既然您知道客户端套接字,请使用lsof查找持有它的 PID:
$ sudo lsof -n | grep 0xffff810008339600
firefox 7725 username 146u unix 0xffff810008339600 8512421 socket
Run Code Online (Sandbox Code Playgroud)就是这样。保持该窗口的进程是“firefox”,进程 ID 为 7725
2017 年编辑:现在有更多选项,如谁拥有此 unix socketpair 的另一端?. 对于 Linux 3.3 或更高版本和lsof4.89 或更高版本,您可以将上面的第 3 至 5 点替换为:
lsof +E -a -p 1237 -d 31
Run Code Online (Sandbox Code Playgroud)
找出 ID 为 1237 的 X 服务器进程的 fd 31 上套接字的另一端是谁。
fra*_*ous 15
如果你安装了xdotool,那么
xdotool selectwindow getwindowpid
然后单击有问题的窗口将返回 PID。
(还有其他选择有问题的窗口的方法,例如,如果你有它的窗口 ID,你可以这样做xdotool getwindowpid <number>。你也可以按名称或类等选择。)
我确实认为这需要代表 WM 表现得很好。我没有做过太多实验,或者不需要。
eph*_*ent 13
在_NET_WM_PID不被窗口管理器设置(如只是一个X11客户端,它怎么会知道?)。
相反,兼容的 X11 客户端(应用程序)应该在它们自己的窗口上设置_NET_WM_PID和WM_CLIENT_MACHINE。假设一个行为良好的应用程序,无论窗口管理器是否正在运行,这都是正确的。
如果WM_CLIENT_MACHINE是您自己的主机名,那么 PID 应该是有意义的。
否则,“我想要与远程端关联的 IP 和端口”——我不确定这意味着什么。例如,如果您在启用 X 转发的情况下打开 ssh 会话,被转发的应用程序打开的窗口将被标记为远程 PID 和主机名,但您不一定有任何方法可以连接回该远程主机。
小智 5
我能够xdotool在 Ubuntu 11.04 beta 下使用,但selectwindow不是一个有效的命令,我不得不使用以下命令破解脚本:
$ while true; do sleep 1; xdotool getactivewindow; done
Run Code Online (Sandbox Code Playgroud)
然后在我选择我想要的窗口时观察窗口 ID,然后使用以下方法解码负责的 PID:
$ xdotool getwindowpid <the-window-id>
Run Code Online (Sandbox Code Playgroud)