我正在处理一个小应用程序,我需要找到给定其主窗口或子窗口的X11窗口ID的进程的PID。我看到了使用_NET_WM_PID
进行此类转换的示例,但我无法弄清楚如何在不使用它的情况下执行此操作。不使用_NET_WM_PID
的原因是它没有在所有可用的窗口管理器中实现,并且我的应用程序需要处理它们中的任何一个(或者至少在大多数窗口管理器上)。有人可以帮助我,并就如何解决这个问题给我一些建议/指示?谢谢!
答案 0 :(得分:7)
除非您的X-server支持X-Resource v1.2 extension XResQueryClientIds
,否则我不知道轻松方式可靠地请求进程ID。不过还有其他方法。
如果你面前有一个窗口而且还不知道它的ID - 很容易找到它。只需打开相关窗口旁边的终端,在那里运行xwininfo
,然后单击该窗口。 xwininfo
会显示window-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。这种方式适用于真正的黑客。 :)
您知道的低位(即0x1600000)的窗口ID 0x1600045是“客户端”。并且为该客户端分配的所有资源ID都基于它(0x1600001,0x1600002,0x1600003等)。 X-server在clients []数组中存储有关其客户端的信息,对于每个客户端,其“base”存储在clients [i] - > clientAsMask变量中。要查找对应于该客户端的X-socket,需要使用gdb
附加到X-server,遍历clients []数组,查找具有该clientAsMask
的客户端并打印其存储的套接字描述符((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
当你找到套接字时,你可以检查,谁连接到它,最后找到该过程。
警告:请勿从X-server的INSIDE将gdb附加到X-server。 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-server并查找该客户端的客户端套接字描述符。您需要调试信息 安装用于X-server(用于rpm分发的-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”是它的pid,“root”是它运行的用户,“31u”是套接字描述符)
在那里,您可能会看到客户端通过TCP连接,然后您可以转到它所连接的计算机并检查netstat -nap
那里以查找该过程。但很可能你会在那里看到一个unix socket,如上所示,这意味着它是一个本地客户端。
要查找该unix套接字的对,可以使用MvG's technique (您还需要安装内核的调试信息):
$ 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”,进程号为7725
答案 1 :(得分:6)
通常,无法找到创建窗口的进程的PID。可能是进程在一台机器上远程运行,可能是机器甚至没有进程和PID的概念。
如果您在最初创建客户端时不信任任何人存储此信息,则您需要自己跟踪连接。找出客户端使用的连接类型(套接字等),找出连接结束的位置,并找出结束的过程。如何做到(以及是否可能)是高度依赖操作系统的。
答案 2 :(得分:1)
早在2004年,Harald Welte发布了一个代码片段,它通过LD_PRELOAD包装XCreateWindow()调用,并将进程ID存储在_NET_WM_PID中。这可以确保创建的每个窗口都有一个有用的_NET_WM_PID条目。