什么进程创建了这个 X11 窗口?

什么进程创建了这个 X11 窗口?

给定一个 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_PIDWM_CLIENT_MACHINE属性:

xprop -id 0x1600045

这就是工具喜欢xlsclientsxrestop做。

不幸的是,这些信息可能不正确,不仅因为该过程是邪恶的并改变了这些,而且还因为它有错误。例如,在一些 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 连接到您的计算机。

例子:

  1. 找到你的 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
    
  2. 窗口 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
    
  3. 现在您知道客户端已连接到服务器套接字 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 套接字,如上所示,这意味着它是本地客户端。

  4. 要为该 unix 套接字找到一对,您可以使用MvG的技术 (您还需要安装的内核的调试信息):

    $ sudo gdb -c /proc/kcore
    (gdb) print ((struct unix_sock*)0xffff810008339340)->peer
    $1 = (struct sock *) 0xffff810008339600
    (gdb) quit
    
  5. 现在您知道了客户端套接字,可以使用它lsof来查找持有它的 PID:

    $ sudo lsof -n | grep 0xffff810008339600
    firefox  7725  username  146u   unix 0xffff810008339600       8512421 socket
    

就是这样。保留该窗口的进程是“firefox”,进程 ID 为 7725


2017年 编辑:现在有更多选项,如下所示谁有这个 unix 套接字对的另一端?。对于 Linux 3.3 或更高版本以及lsof4.89 或更高版本,您可以将上面的第 3 点到第 5 点替换为:

lsof +E -a -p 1237 -d 31

找出 ID 为 1237 的 X 服务器进程的 fd 31 上套接字的另一端是谁。

答案2

xdotool不适合我。这做到了:

跑步

xprop _NET_WM_PID

并单击窗口。

这是基于在 Linux 问题中查找 X 窗口的 PID

答案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>

相关内容