哪个进程创建了X11窗口?


75

给定一个X11窗口ID,是否可以找到创建它的进程的ID?

当然,这并非总是可能的,例如,如果窗口是通过TCP连接来的。对于这种情况,我想要与远端相关联的IP和端口。

之前在Stack Overflow上问过这个问题,一种建议的方法是使用该_NET_WM_PID属性。但这是由应用程序设置的。如果应用程序运行不正常,有没有办法做到这一点?


Answers:


60

除非你的X-服务器支持XResQueryClientIds来自X-资源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服务器将有关其客户端的信息存储在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连接到计算机。

例:

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

  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
    

而已。保留该窗口的进程是带有进程ID 7725的“ firefox”


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

lsof +E -a -p 1237 -d 31

找出谁在ID为1237的X-server进程的fd 31套接字的另一端。


6
欢迎Unix和Linux Stack Exchange!您对这个问题的回答非常好。希望您回来回答更多问题。


13

如果您安装了xdotool,则

xdotool selectwindow getwindowpid

然后单击有问题的窗口将返回PID。

(还有其他选择所涉及窗口的方法,例如,如果您具有其窗口ID,则可以这样做xdotool getwindowpid <number>。也可以按名称或类等进行选择。)

我确实认为这需要代表WM发挥一些作用。我没有做太多尝试,或者需要做。


2
xdo_getwinprop(xdo, window, atom_NET_WM_PID, &nitems, &type, &size)⇒只是要读取的外壳包装_NET_WM_PID(有用,但不是我所要求的)。
吉尔斯

11

_NET_WM_PID不被窗口管理器设置(如只是一个X11客户端,如何将它知道吗?)。

取而代之的是,X11兼容的客户端(应用程序)预计将设置_NET_WM_PIDWM_CLIENT_MACHINE在自己的窗口。假设应用程序运行良好,则无论窗口管理器是否正在运行,这都是正确的。

如果WM_CLIENT_MACHINE是您自己的主机名,则PID应该有意义。
否则,“我想要与远端相关联的IP和端口” —我不确定这意味着什么。例如,如果您打开了一个启用了X转发的ssh会话,则转发的应用程序打开的窗口将被标记为远程PID和主机名,但是您不一定有任何方法可以连接回该远程主机。


2
_NET_WM_PID由应用程序设置:正确,这更有意义!但这不是X11协议,而是相对较新的FreeDesktop规范。
吉尔斯

就ssh而言,就X服务器而言,这是sshd进程的本地连接。尽管_NET_WM_PID似乎已设置为远程PID和WM_CLIENT_MACHINE远程连接(已通过xterm测试)。
吉尔斯

4

我可以使用xdotoolUbuntu 11.04 beta下的版本,但selectwindow不是有效命令,因此必须使用以下方法破解脚本:

$ while true; do sleep 1; xdotool getactivewindow; done

然后在选择所需的窗口时观察窗口ID的经过,然后使用以下命令解码负责的PID:

$ xdotool getwindowpid <the-window-id>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.