给定一个 X11 窗口 ID,有没有办法找到创建它的进程的 ID?
当然,这并不总是可行,例如,如果窗口来自 TCP 连接。对于这种情况,我想要与远程端关联的 IP 和端口。
这个问题之前在 Stack Overflow 上被问过,建议的方法是使用该_NET_WM_PID
属性。但这是由应用程序设置的。如果应用程序运行得不好,有什么办法可以做到这一点吗?
答案1
除非你的X服务器XResQueryClientIds
支持X-Resource 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-server 在 client[] 数组中存储有关其客户端的信息,并且对于每个客户端,其“基础”存储在 client[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-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
窗口 ID 为 0x1600045,因此客户端基数为 0x1600000。连接到 X 服务器并查找该客户端库的客户端套接字描述符。您需要为 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 套接字,如上所示,这意味着它是本地客户端。要为该 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
就是这样。保留该窗口的进程是“firefox”,进程 ID 为 7725
2017年 编辑:现在有更多选项,如下所示谁有这个 unix 套接字对的另一端?。对于 Linux 3.3 或更高版本以及lsof
4.89 或更高版本,您可以将上面的第 3 点到第 5 点替换为:
lsof +E -a -p 1237 -d 31
找出 ID 为 1237 的 X 服务器进程的 fd 31 上套接字的另一端是谁。
答案2
答案3
如果你有xdo工具安装完毕,然后
xdotool selectwindow getwindowpid
然后单击相关窗口将返回 PID。
(还有其他方法可以选择相关窗口,例如,如果您有其窗口 ID,则可以执行xdotool getwindowpid <number>
。您还可以按名称或类等进行选择。)
我确实认为这需要 WM 表现得很好。我没有做过太多实验,也没有必要做太多实验。
答案4
我能够xdotool
在 Ubuntu 11.04 beta 下使用,但这selectwindow
不是一个有效的命令,我不得不使用以下命令来破解一个脚本:
$ while true; do sleep 1; xdotool getactivewindow; done
然后在选择我想要的窗口时观察窗口 ID,然后使用以下命令解码负责的 PID:
$ xdotool getwindowpid <the-window-id>