给定一个X11窗口ID,是否可以找到创建它的进程的ID?
当然,这并非总是可能的,例如,如果窗口是通过TCP连接来的。对于这种情况,我想要与远端相关联的IP和端口。
之前在Stack Overflow上问过这个问题,一种建议的方法是使用该_NET_WM_PID
属性。但这是由应用程序设置的。如果应用程序运行不正常,有没有办法做到这一点?
给定一个X11窗口ID,是否可以找到创建它的进程的ID?
当然,这并非总是可能的,例如,如果窗口是通过TCP连接来的。对于这种情况,我想要与远端相关联的IP和端口。
之前在Stack Overflow上问过这个问题,一种建议的方法是使用该_NET_WM_PID
属性。但这是由应用程序设置的。如果应用程序运行不正常,有没有办法做到这一点?
Answers:
除非你的X-服务器支持XResQueryClientIds
来自X-资源V1.2扩展我知道有没有简单的方法来可靠地请求进程ID。但是还有其他方法。
如果您前面只有一个窗口并且还不知道其ID,那么很容易找到它。只需打开相关窗口旁边的终端,然后在该窗口中运行xwininfo
并单击该窗口即可。xwininfo
将显示窗口ID。
因此,假设您知道一个窗口ID,例如0x1600045,并想查找拥有它的进程是什么。
检查该窗口属于谁的最简单方法是为其运行XKillClient,即:
xkill -id 0x1600045
看看哪个进程死了。但是只有在您不介意杀死它的情况下,才可以!
另一种简单但不可靠的方法是检查其_NET_WM_PID
和WM_CLIENT_MACHINE
属性:
xprop -id 0x1600045
这就是喜欢xlsclients
和xrestop
做的工具。
不幸的是,这些信息可能是不正确的,不仅是因为该过程是邪恶的并改变了它们,还因为它是越野车。例如,在某些Firefox崩溃/重新启动后,我已经看到孤立的窗口(我想是来自Flash插件)_NET_WM_PID
指向一个进程的,该进程很久以前就死了。
另一种方法是运行
xwininfo -root -tree
并检查相关窗口的父级的属性。这也可能给您一些有关窗口起源的提示。
但!尽管您可能找不到创建该窗口的进程,但是仍然可以找到该进程从何处连接到X-server的方法。而且这种方式适用于真正的黑客。:)
您知道的低位为零的窗口ID 0x1600045(即0x1600000)是“客户端库”。并且为该客户端分配的所有资源ID均基于该客户端(0x1600001、0x1600002、0x1600003等)。X服务器将有关其客户端的信息存储在clients []数组中,并且对于每个客户端,其“基础”都存储在clients [i]-> clientAsMask变量中。要查找与该客户端相对应的X-socket,您需要使用附加到X-server gdb
,遍历client []数组,找到具有该客户端的客户端clientAsMask
并打印其套接字描述符,该描述符存储在((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
找到套接字后,您可以检查谁连接到该套接字,并最终找到该进程。
警告:请勿从X服务器将gdb附加到X服务器。gdb会挂起它所附加的进程,因此,如果从X-session内部对其进行附加,则将冻结X服务器,并且将无法与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
窗口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
现在您知道客户端已连接到服务器套接字31。lsof
用于查找该套接字是什么:
$ sudo lsof -n | grep 1237 | grep 31
X 1237 root 31u unix 0xffff810008339340 8512422 socket
(此处“ X”是进程名称,“ 1237”是其进程号,“ 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
现在您知道了客户端套接字,使用lsof
它来查找持有它的PID:
$ sudo lsof -n | grep 0xffff810008339600
firefox 7725 username 146u unix 0xffff810008339600 8512421 socket
而已。保留该窗口的进程是带有进程ID 7725的“ firefox”
2017编辑:现在有更多选项,如谁在此unix套接字对的另一端?。在Linux 3.3或更高版本以及lsof
4.89或更高版本中,您可以将上述第3点到第5点替换为:
lsof +E -a -p 1237 -d 31
找出谁在ID为1237的X-server进程的fd 31套接字的另一端。
xdotool对我不起作用。这样做:
跑
xprop _NET_WM_PID
然后点击窗口。
这是基于http://www.linuxquestions.org/questions/linux-software-2/advanced-question-finding-pid-of-an-x-window-328983/上的答案
kill $(xprop _NET_WM_PID|cut -d " " -f 3)
在_NET_WM_PID
不被窗口管理器设置(如只是一个X11客户端,如何将它知道吗?)。
取而代之的是,X11兼容的客户端(应用程序)预计将设置_NET_WM_PID
并WM_CLIENT_MACHINE
在自己的窗口。假设应用程序运行良好,则无论窗口管理器是否正在运行,这都是正确的。
如果WM_CLIENT_MACHINE
是您自己的主机名,则PID应该有意义。
否则,“我想要与远端相关联的IP和端口” —我不确定这意味着什么。例如,如果您打开了一个启用了X转发的ssh会话,则转发的应用程序打开的窗口将被标记为远程PID和主机名,但是您不一定有任何方法可以连接回该远程主机。
_NET_WM_PID
由应用程序设置:正确,这更有意义!但这不是X11协议,而是相对较新的FreeDesktop规范。
_NET_WM_PID
似乎已设置为远程PID和WM_CLIENT_MACHINE
远程连接(已通过xterm测试)。