寻找一个免费的X11显示器编号。

13

我有一些需要 X11 显示的单元测试,所以我计划在运行它们之前启动 Xvfb,但是要启动 Xvfb,我需要一个空闲的显示器号码来连接它。 我最好的猜测是查看 /tmp/.X11-unix 中的可用情况,但是我不确定如何处理如果多个测试同时尝试启动的竞争。

sshd 必须这样做,有人知道怎么做吗?

6个回答

16

以下是关于相关问题How high do X11 display numbers go?的答案(原答案)的完整引用:

截至1.13版(包括Xvfb),最近的X服务器支持命令行选项-displayfd <fd>:这将使X服务器自行选择显示并将显示编号写回到文件描述符<fd>。虽然有些繁琐,但这将是一种安全且无竞争条件的方法,可以要求Xvfb使用任何空闲显示器。以下是一个bash示例:

exec 6>display.log
Xvfb -displayfd 6
# The display number of the new Xvfb instance has been written to display.log 
# Kill Xvfb
exec 6>&-

仅供参考:在Debian中,Xvfb v1.14可在jessie中使用,这意味着在平均发行版中应该相当新(2013年下半年或之后)。 - dma_k

11
为什么不利用每个X11服务器都会在/tmp目录下放置一个“Lock”文件的事实呢?
这个文件称为/tmp/.Xn-lock,其中“n”是显示器ID。(还要注意文件名前面的.)
这是Xserver自身用来检查重复的机制,并且在我尝试过的所有*nix平台上都是一致的(HP-UX、Linux等)。
因此,您可以根据需要调整脚本,例如(请原谅我的语法错误,我比Bourne/Korn shell脚本更习惯于C shell):
DISPLAY_NUM=0

do

  if ( -e /tmp/.X$DISPLAY_NUM-lock ) then

     let DISPLAY_NUM=$DISPLAY_NUM+1

  else

     Xvfb :$DISPLAY_NUM -screen 0 1280x1024x24 -ac   (or whatever args take your fancy)

  fi

done

2
问题的一部分是如何避免竞态条件。如果同时运行,您的代码最终也会失败。 - uli42
你可以通过使用额外的自定义锁来避免并发竞争,例如将所有内容放在另一个锁内。这对我来说很有效(比实际被接受的答案更好)。 - Karel Bílek
我在下面添加了一个新的答案,对我有效。 - Karel Bílek

5

试图找到免费的显示号码是没有意义的。正如你所猜测的那样,在你找到一个空闲的号码并且Xvfb启动之间,另一个X服务器可能会占用你认为是空闲的端口。因此,最好只是尝试启动Xvfb,处理如果端口被占用的失败,然后重试下一个端口直到成功或者没有可用端口为止。

#!/bin/bash
DISPLAY_NUM=0
unset TEST_HAS_RUN
until [ $TEST_HAS_RUN ] || (( $DISPLAY_NUM > 10 ))
do
 Xvfb :$DISPLAY_NUM &
 jobs
 sleep 2  # assumption here is that Xvfb will exit quickly if it can't launch
 if jobs | grep Xvfb
 then  
   echo launching test on :$DISPLAY_NUM
   xterm -display :$DISPLAY_NUM
   TEST_HAS_RUN=1
   kill %-
 else   
   let DISPLAY_NUM=$DISPLAY_NUM+1
 fi
done

如果 xterm 成功启动,它不会阻塞您的脚本,直到用户手动退出。否则,似乎 TEST_HAS_RUN 永远不会被设置,循环永远不会退出。实际上,如果 xterm 无法启动,您的测试看起来是 通过 的。我有什么遗漏吗? - user2975337
1
从技术上讲,如果Xvfb失败,然后在您的2秒睡眠期内另一个X服务器占用了端口,则您的测试将在错误的服务器上通过(特别是如果您的脚本在后台有另一个Xvfb以满足jobs | grep Xvfb)。最好使用唯一的fbdir(例如-fbdir /var/tmp/$0.$$.$RANDOM)启动Xvfb,然后检查该目录中的文件Xvfb_screen0 - user2975337
@jrodatus 这个答案假设 "xterm" 是测试,所以在那之后关闭显示器是正确的。而且另一个实例与此同时运行不会找到错误的服务器,因为 "jobs" 只报告此 shell 的子进程。不过,krlmlr 的答案现在更好了。 - James

1

也许有点跑题,但如果您使用xvfb-run来启动需要X服务器的命令,那么只需要运行

# xvfb-run -a your command

做到了。


0

基于@karunski的答案。

使用Xvfb来探测显示器,并使用lsof检查Xvfb进程中是否存在unix套接字,这更有效,注意sleep 0.5,可以根据机器而变化。

#!/bin/bash
DISPLAY=0

until [ $DISPLAY_NUM > 10 ]; do
        echo -n "Looking for display on $DISPLAY..."
        Xvfb :$DISPLAY > /dev/null 2>&1 &
        pid=$!
        sleep 0.5
        lsof -a -U -p $pid  > /dev/null 2>&1    

        notfound="$?"
        kill $pid > /dev/null 2>&1

        wait $pid

        [ "$notfound" == "0" ] && echo "found" && break

        echo "fail"
        let DISPLAY=DISPLAY+1
done

0

这对我有用(注意 - 使用bash)

exec {lock_fd}>/var/lock/xlockfile || exit 1
flock "$lock_fd" || { echo "ERROR: flock() failed." >&2; exit 1; }

DISPLAY_NUM=1
until [[ $xvfb ]]; do
  if [[ -e /tmp/.X$DISPLAY_NUM-lock ]]; then
    let DISPLAY_NUM=$DISPLAY_NUM+1
  else
    Xvfb :$DISPLAY_NUM -ac -screen 0 $XVFB_WHD -nolisten tcp &
    xvfb=$!
  fi
done

flock -u "$lock_fd"

请注意,仅当运行xvfb命令的所有脚本都使用外部锁时,外部锁才起作用,否则会再次出现竞争条件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接