当X启动时,它会搜索最低的未使用的VT,并附加到它。我的问题是,当有多个正在运行的 X 进程时,我需要能够识别哪一个是当前活动的进程。
这是一个 *BSD 问题,因为在 Linux 上很简单:X 将其控制终端设置为ttyN
,或者,在非常旧的发行版上,在命令行上将其指定为vtN
。因此,我正在运行一项服务,我看到当前活动的 VT 是tty7
,并且有两个 X 服务器正在运行,很容易判断哪一个对应于当前终端。 (这是一个合理的情况:也许用户使用了 GNOME/KDE 的“切换用户”功能或使用 运行两个服务器startx
。)可能想要遵循活动 X 服务器的示例应用程序是x11vnc
(它是从我正在开发的软件中分叉出来的) )。
但在 FreeBSD 上,控制终端不会告诉您任何信息。当 X 从 ttyv1 启动时,它仍然是控制终端。
更新
我已经做了尽职调查并阅读了 X 代码。经过一番搜寻后,我现在更清楚发生了什么。
在lnx_init.c,X 服务器会setsid
为自己创建一个新的会话,然后直接打开一个 fd以对其ttyN
进行ioctl。VT_ACTIVATE
相当标准;从没有控制终端的进程向没有控制进程的终端打开 fd 会将两者关联起来,并且服务器保持 fd 打开,因此可以保证该终端仍然是 X 服务器的控制终端。
现在,在bsd_init.c,打开 tty 的 fd 以用作帧缓冲区不会使其成为控制终端(事实上,如果没有setsid
,从 ttyv2 上启动的 BSD Xserverxinit
会将 ttyv2 保留为其 ctty!)。
问题于 2012 年 4 月 9 日进一步更新和清理。
答案1
有一个更通用的方法。在所有具有虚拟终端的平台上,包括 Linux 和 BSD,Xserver 都会为其运行的终端保留一个开放的 fd。在 Linux 上,检查 X 进程的控制终端以区分多个 X 进程(使用 的第七个字段/proc/<..>/stat
)仍然是一个很好的解决方案。但更一般地说,查看 X 进程的打开 fd 列表,只需要一些简单的过滤即可获取 Xserver 正在运行的终端。 (不幸的是,获取打开的文件描述符列表又与平台相关......)对于sysctl
像 BSD 这样的平台,代码将类似于此,加上一些错误处理:
int ttyByOpenFds(int curPid) {
int ctl[4] = { CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC, curPid };
size_t sizeGuess = 50*sizeof(kinfo_file);
char* buf = malloc(sizeGuess);
int rv = sysctl(ctl, 4, buf, &sizeGuess, 0, 0);
if (rv < 0 && errno == ESRCH) return 0;
else if (rv < 0 && errno == ENOMEM) { /* try again */ }
else if (rv < 0) throw SystemException("unexpected error getting args", errno);
char* position = buf;
while (position < buf + sizeGuess) {
kinfo_file* kfp = reinterpret_cast<kinfo_file*>(position);
position += kfp->kf_structsize;
if (kfp->kf_type != KF_TYPE_VNODE) continue;
if (kfp->kf_vnode_type != KF_VTYPE_VCHR) continue;
if (kfp->kf_fd < 0) continue;
char* name = devname(kfp->kf_un.kf_file.kf_file_rdev, S_IFCHR);
if (!name) continue;
unsigned int ttynum = 0;
if (sscanf(name, "ttyv%u", &ttynum) != 1) continue;
if (ttynum < 8 && kfp->kf_fd <= 2) continue; // stderr going to a console
return ttynum;
}
return 0;
}