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

Gil*_*il' 96 process x11

给定一个 X11 窗口 ID,有没有办法找到创建它的进程的 ID?

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

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

小智 90

除非你的X服务器支持XResQueryClientIds来自X-资源V1.2扩展我知道有没有简单的方法来可靠地请求进程ID。不过还有其他办法。

如果您面前只有一个窗口并且还不知道它的 ID — 很容易找到它。只需在相关窗口旁边打开一个终端,在xwininfo那里运行并单击该窗口。xwininfo将向您显示窗口 ID。

因此,让我们假设您知道一个窗口 ID,例如 0x1600045,并且想要查找拥有它的进程是什么。

检查该窗口属于谁的最简单方法是为其运行 XKillClient ,即:

xkill -id 0x1600045
Run Code Online (Sandbox Code Playgroud)

并查看哪个进程刚刚死亡。但前提是你当然不介意杀死它!

另一种简单但不可靠的方法是检查它的_NET_WM_PIDWM_CLIENT_MACHINE属性:

xprop -id 0x1600045
Run Code Online (Sandbox Code Playgroud)

那是什么样的工具xlsclientsxrestop做。

不幸的是,这些信息可能是不正确的,不仅因为该过程是邪恶的并改变了这些,还因为它有缺陷。例如,在一些 firefox 崩溃/重启之后,我看到孤立的窗口(我猜是来自 flash 插件)_NET_WM_PID指向一个进程,该进程很久以前就死了。

另一种方法是运行

xwininfo -root -tree
Run Code Online (Sandbox Code Playgroud)

并检查相关窗口的父项的属性。这也可能会给你一些关于窗口起源的提示。

但!虽然您可能找不到创建该窗口的进程,但仍有一种方法可以找到该进程从何处连接到 X-server。这种方式适用于真正的黑客。:)

您知道的低位清零(即 0x1600000)的窗口 ID 0x1600045 是“客户群”。为该客户端分配的所有资源 ID 都是“基于”它的(0x1600001、0x1600002、0x1600003 等)。X-server 将有关其客户端的信息存储在 clients[] 数组中,对于每个客户端,其“基础”存储在 clients[i]->clientAsMask 变量中。要找到X插槽,对应于客户端,你需要重视的X服务器gdb,走在客户的[]数组中,发现客户端与clientAsMask和打印其socket描述符,存储在((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
Run Code Online (Sandbox Code Playgroud)

找到socket后,可以查看,谁连接上了,最后找到进程。

警告:不要从 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
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在您知道客户端已连接到服务器套接字 31。使用lsof来查找该套接字是什么:

    $ sudo lsof -n | grep 1237 | grep 31
    X        1237    root   31u   unix 0xffff810008339340       8512422 socket
    
    Run Code Online (Sandbox Code Playgroud)

    (这里“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
    
    Run Code Online (Sandbox Code Playgroud)
  5. 既然您知道客户端套接字,请使用lsof查找持有它的 PID:

    $ sudo lsof -n | grep 0xffff810008339600
    firefox  7725  username  146u   unix 0xffff810008339600       8512421 socket
    
    Run Code Online (Sandbox Code Playgroud)

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


2017 年编辑:现在有更多选项,如谁拥有此 unix socketpair 的另一端?. 对于 Linux 3.3 或更高版本和lsof4.89 或更高版本,您可以将上面的第 3 至 5 点替换为:

lsof +E -a -p 1237 -d 31
Run Code Online (Sandbox Code Playgroud)

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

  • 欢迎使用 Unix 和 Linux Stack Exchange!你对这个问题的回答非常好。我希望你回来回答更多的问题。 (8认同)

小智 42

xdotool对我不起作用。这做到了:

xprop _NET_WM_PID
Run Code Online (Sandbox Code Playgroud)

并点击窗口。

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


fra*_*ous 15

如果你安装了xdotool,那么

xdotool selectwindow getwindowpid

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

(还有其他选择有问题的窗口的方法,例如,如果你有它的窗口 ID,你可以这样做xdotool getwindowpid <number>。你也可以按名称或类等选择。)

我确实认为这需要代表 WM 表现得很好。我没有做过太多实验,或者不需要。

  • `xdo_getwinprop(xdo, window, atom_NET_WM_PID, &amp;nitems, &amp;type, &amp;size)` ⇒ 它只是一个读取 `_NET_WM_PID` 的外壳包装器(有用,但不是我要求的)。 (2认同)

eph*_*ent 13

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

相反,兼容的 X11 客户端(应用程序)应该在它们自己的窗口上设置_NET_WM_PIDWM_CLIENT_MACHINE。假设一个行为良好的应用程序,无论窗口管理器是否正在运行,这都是正确的。

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

  • `_NET_WM_PID` 由应用程序设置:是的,这确实更有意义!但这不是 X11 协议,而是相对较新的 [FreeDesktop](http://standards.freedesktop.org/wm-spec/1.3/ar01s05.html) 规范。 (2认同)

小智 5

我能够xdotool在 Ubuntu 11.04 beta 下使用,但selectwindow不是一个有效的命令,我不得不使用以下命令破解脚本:

$ while true; do sleep 1; xdotool getactivewindow; done
Run Code Online (Sandbox Code Playgroud)

然后在我选择我想要的窗口时观察窗口 ID,然后使用以下方法解码负责的 PID:

$ xdotool getwindowpid <the-window-id>
Run Code Online (Sandbox Code Playgroud)