除非你的X服务器支持XResQueryClientIds
from X-Resource v1.2 扩展 http://www.x.org/releases/X11R7.7/doc/resourceproto/resproto.txt我知道没有easy way to reliably请求进程ID。不过还有其他方法。
如果您面前只有一个窗口并且还不知道它的 ID,那么很容易找到它。只需打开相关窗口旁边的终端,然后运行xwininfo
在那里并单击该窗口。xwininfo
将显示窗口 ID。
因此,我们假设您知道一个窗口 ID,例如0x1600045,想找到拥有它的进程是什么。
检查该窗口属于谁的最简单方法是为其运行 XKillClient,即:
xkill -id 0x1600045
看看哪个进程刚刚死掉了。当然,如果你不介意去死的话。
另一种简单但不可靠的方法是检查其_NET_WM_PID
and WM_CLIENT_MACHINE
特性:
xprop -id 0x1600045
这就是工具之类的xlsclients
and xrestop http://www.freedesktop.org/wiki/Software/xrestop/ do.
不幸的是,这些信息可能不正确,不仅因为该过程是邪恶的并改变了这些,而且还因为它有错误。例如,在一些 Firefox 崩溃/重新启动后,我看到了孤立的窗口(我猜是来自 flash 插件)_NET_WM_PID
指向一个很久以前就死掉的进程。
另一种方法是运行
xwininfo -root -tree
并检查相关窗口的父窗口的属性。这也可能会给您一些有关窗口起源的提示。
但!虽然您可能找不到哪个进程创建了该窗口,但仍然有一种方法可以找到该进程从何处连接到 X-server。这种方式适用于真正的黑客。 :)
您所知道的窗口 ID 0x1600045 的低位清零(即 0x1600000)是“客户端基础”。为该客户端分配的所有资源 ID 都是“基于”它的(0x1600001、0x1600002、0x1600003 等)。 X-server 在 client[] 数组中存储有关其客户端的信息,并且对于每个客户端,其“基础”存储在 client[i]->clientAsMask 变量中。要找到与该客户端对应的 X-socket,您需要使用以下命令连接到 X-servergdb
,遍历clients[]数组,找到clientclientAsMask
并打印其套接字描述符,存储在 ((OsCommPtr)(clients[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
当找到套接字时,您可以检查谁连接到它,并最终找到进程。
WARNING:不要从 X 服务器内部将 gdb 连接到 X 服务器。 gdb 会挂起它附加的进程,因此如果您从 X-session 内部附加它,您将冻结您的 X-server 并且无法与 gdb 交互。您必须切换到文本终端(Ctrl+Alt+F2
)或通过 ssh 连接到您的计算机。
Example:
-
找到你的 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
-
窗口 ID 为 0x1600045,因此客户端基数为 0x1600000。连接到 X 服务器并查找该客户端库的客户端套接字描述符。您将需要调试信息
为 X-server 安装(-debuginfo 软件包用于 rpm-distributions 或 -dbg 软件包用于 deb)。
$ 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
-
现在您知道客户端已连接到服务器套接字 31。使用lsof
查找该套接字是什么:
$ sudo lsof -n | grep 1237 | grep 31
X 1237 root 31u unix 0xffff810008339340 8512422 socket
(这里“X”是进程名称,“1237”是它的pid,“root”是它运行的用户,“31u”是套接字描述符)
在那里你可能会看到客户端是通过 TCP 连接的,然后你可以转到它连接的机器并检查netstat -nap
在那里找到进程。但很可能您会在那里看到一个 unix 套接字,如上所示,这意味着它是本地客户端。
-
要为该 unix 套接字找到一对,您可以使用MvG的技术 https://serverfault.com/a/417946(您还需要安装的内核的调试信息):
$ sudo gdb -c /proc/kcore
(gdb) print ((struct unix_sock*)0xffff810008339340)->peer
$1 = (struct sock *) 0xffff810008339600
(gdb) quit
-
现在您已经了解了客户端套接字,请使用lsof
找到持有它的PID:
$ sudo lsof -n | grep 0xffff810008339600
firefox 7725 username 146u unix 0xffff810008339600 8512421 socket
就是这样。保留该窗口的进程是“firefox”,进程 ID 为 7725