10

我正在开发一个小型应用程序,我需要在给定主窗口或子窗口的 X11 窗口 ID 的情况下找到进程的 PID。我看到了使用 进行此类转换的示例_NET_WM_PID,但如果不使用它,我无法弄清楚如何进行。不使用的原因_NET_WM_PID是它没有在所有可用的窗口管理器中实现,我的应用程序需要在其中任何一个(或至少在其中大多数)上工作。有人可以帮我,给我一些关于如何解决这个问题的建议/指导吗?谢谢!

4

3 回答 3

8

除非您的 X-server 支持X-Resource v1.2 扩展XResQueryClientIds,否则我知道没有简单的方法可以可靠地请求进程 ID。不过还有其他方法。

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

所以让我们假设你知道一个window-id,例如0x1600045,并且想要找到拥有它的进程是什么。

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

xkill -id 0x1600045

看看什么进程刚刚死掉。当然,如果你不介意它死。

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

xprop -id 0x1600045

这就是工具喜欢xlsclientsxrestop做的事情。

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

另一种方法是运行

xwininfo -root -tree

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

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

低位归零(即 0x1600000)的窗口 id 0x1600045 是“客户群”。并且为该客户端分配的所有资源 ID 都是“基于”它的(0x1600001、0x1600002、0x1600003 等)。X-server 将有关其客户端的信息存储在 clients[] 数组中,并且对于每个客户端,其“基础”存储在 clients[i]->clientAsMask 变量中。要找到对应于该客户端的 X-socket,您需要使用 连接到 X-server gdb,遍历 clients[] 数组,找到带有该客户端的客户端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

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

警告:不要从 X-server 内部将 gdb 附加到 X-server。gdb 挂起它附加的进程,因此如果您从 X-session 内部附加到它,您将冻结您的 X-server 并且将无法与 gdb 交互。您必须切换到文本终端 ( Ctrl+Alt+F2) 或通过 ssh 连接到您的机器。

例子:

  1. 查找 X-server 的 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-server 并找到该客户端的客户端套接字描述符。您需要为 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”是它的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

于 2013-07-30T23:24:40.013 回答
6

通常,不可能找出创建窗口的进程的 PID。可能是进程在机器上远程运行,也可能是机器甚至没有进程和 PID 的概念。

如果您不信任在最初创建客户端时存储此信息的任何人,您将需要自己跟踪连接。找出客户端正在使用哪种连接(套接字等),找出该连接在哪里结束,并找出哪个进程持有该端。如何做到这一点(以及是否有可能)高度依赖于操作系统。

于 2009-07-15T13:50:51.370 回答
2

早在 2004 年,Harald Welte 发布了一个代码片段,该代码片段通过 LD_PRELOAD 包装了 XCreateWindow() 调用并将进程 ID 存储在 _NET_WM_PID 中。这确保创建的每个窗口都有一个有用的 _NET_WM_PID 条目。

http://www.mail-archive.com/devel@xfree86.org/msg05806.html

于 2012-11-20T21:08:40.150 回答