5

一般问题:如果调用它的脚本或 shell (bash) 命令将其调用到变量中,什么会导致脚本本身运行良好,挂起?

换句话说,怎么会有这样的脚本在这样调用时工作............/path/to/script arg arg当这样调用时失败并挂起...... VAR=$(/path/to/script arg arg);


(在注意到软件故障导致大量初始测试给出错误结果后进行重大编辑)


我的具体情况:我有一个运行良好的脚本(启动、停止或重新启动 java 应用程序 Apache Solr,改编自此处)。代码如下,它的命令是sbin/service solr [action],例如sbin/service solr start

当从脚本或直接从控制台(bash在我的情况下)调用时sbin/service solr start,它可以正常工作并快速完成。然而,如果它被调用到一个变量中,比如VAR=$(sbin/service solr start);,它可以工作,但会挂起一个 futext / clock_gettime 循环(下面的跟踪)。如果它不是被调用到变量中,它也会挂起,而是到strace.

奇怪的是,以相同方式调用的其他脚本使用相同的语法,例如sbin/service httpd start,在调用变量时工作得很好。因此,当输出存储为变量时,脚本很可能会挂起,但如果不是这种情况,则运行得很好。


以下是测试调用挂起和不挂起的结果:

挂起------------------------------------------------

  • VAR=$(/sbin/service solr start);
  • VAR=$(source /sbin/service solr start);
  • VAR=$(nohup /sbin/service solr start &);

(所以从哪个进程调用它并不重要)此外,编辑脚本文件以启动服务source会导致服务无法工作。

没有挂起-------------------------------------

  • VAR=$(/sbin/service solr start >> /dev/null);

输出到/dev/null允许我们请求输出而不会导致它挂起。但是,它没有多大用处,因为没有收到实际的输出。

  • /sbin/service solr start

与我最初的想法相反。这会输出一个简单的更新消息,理想情况下,我们会在变量和日志中捕获该消息 - 但尝试这样做会导致它挂起。

  • VAR=$(/sbin/service httpd restart);

挂起的语法在其他service脚本上工作得很好,脚本的输出毫无问题地传递给变量。


这是该脚本的完整代码:(已删除注释,自然 $SOLR_DIR 路径是真实脚本中的真实路径)

SOLR_DIR="[path/to/application]"
JAVA_OPTIONS="-Xms64m -Xmx64m -DSTOP.PORT=8079 -DSTOP.KEY=mustard -jar start.jar"
LOG_FILE="/var/log/solr.log"
JAVA="/usr/bin/java"

case $1 in
    start)
        echo "Starting Solr"
        cd $SOLR_DIR
        $JAVA $JAVA_OPTIONS 2> $LOG_FILE &
        ;;
    stop)
        echo "Stopping Solr"
        cd $SOLR_DIR
        $JAVA $JAVA_OPTIONS --stop
        ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}" >&2
        exit 1
        ;;
esac

var/log/solr.log(脚本中命名的日志文件)中没有错误或任何异常。Centos Linux 服务器(如果相关)。


在回答早期版本的问题时,@cdarke 建议我strace -f -o strace.out /path/to/script在调用此脚本的脚本上运行,并查看(大量!)输出文件strace.out。将近 3mbs,以下是一些观察结果:

  1. 从许多看起来像脚本按预期运行的活动开始。

  2. 然后,日志文件的最后 15% 左右是这样的,用不同的整数重复但看似相同的十六进制代码:

...

25687 futex(0x688d454, FUTEX_WAIT_PRIVATE, 1, {0, 49980000}) = -1 ETIMEDOUT (Connection timed out)
25687 futex(0x688d428, FUTEX_WAKE_PRIVATE, 1) = 0
25687 clock_gettime(CLOCK_MONOTONIC, {39074112, 932735888}) = 0
25687 clock_gettime(CLOCK_REALTIME, {1355007234, 333458000}) = 0

ps -p 即使我在脚本仍在运行、输出文件仍在变大且这些代码行仍在编写时这样做,这些 PID 也不会出现任何结果。我不太确定这怎么可能。

这是输出进入永无止境的 futex/clock_gettime 循环之前的最后一点,最后一部分显然是正确执行的脚本之后(solr/solr.xml是一个 Solr 配置文件,需要读取它以启动 Solr 进程):

25874 stat("solr/solr.xml", {st_mode=S_IFREG|0777, st_size=1320, ...}) = 0
25874 write(2, "Dec 8, 2012 5:12:05 PM org.apach"..., 106) = 106
25874 socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 89
25874 fcntl(89, F_GETFL)                = 0x2 (flags O_RDWR)
25874 fcntl(89, F_SETFL, O_RDWR|O_NONBLOCK) = 0
25874 setsockopt(89, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
25874 bind(89, {sa_family=AF_INET, sin_port=htons(8983), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
25874 listen(89, 50)                    = 0
25874 setsockopt(89, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
25874 lseek(12, 57747, SEEK_SET)        = 57747
25874 read(12, "PK\3\4\n\0\0\0\10\0\221Vi>F\347\254\364\325\4\0\0002\t\0\0002\0\0\0", 30) = 30
25874 lseek(12, 57827, SEEK_SET)        = 57827
25874 read(12, "\225V\377oSU\24\377\334\273\256\257_\36l\216m\254\262\351\224\241]\273\255\200\314/\5\246c\200"..., 1237) = 1237
25874 futex(0x2aaab0173054, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x2aaab0173050, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1} <unfinished ...>
25894 <... futex resumed> )             = 0
25894 futex(0x2aaab0173028, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
25874 <... futex resumed> )             = 1
25874 futex(0x2aaab0173028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
25894 <... futex resumed> )             = 0
25894 futex(0x2aaab0173028, FUTEX_WAKE_PRIVATE, 1) = 0
25894 clock_gettime(CLOCK_REALTIME, {1355008325, 376033000}) = 0
25894 futex(0x2aaab0173054, FUTEX_WAIT_PRIVATE, 3, {0, 983000} <unfinished ...>
25874 <... futex resumed> )             = 1
25874 futex(0x2aaab0173054, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x2aaab0173050, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1} <unfinished ...>
25894 <... futex resumed> )             = 0
25894 futex(0x2aaab0173028, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
25874 <... futex resumed> )             = 1
25874 futex(0x2aaab0173028, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
25894 <... futex resumed> )             = 0
25894 futex(0x2aaab0173028, FUTEX_WAKE_PRIVATE, 1) = 0
25894 poll([{fd=89, events=POLLIN|POLLERR}], 1, -1 <unfinished ...>
25874 <... futex resumed> )             = 1
25874 write(2, "2012-12-08 17:12:05.376:INFO::St"..., 66) = 66
25874 write(2, "\n", 1)                 = 1
25874 mmap(0x41348000, 12288, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x41348000
25874 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
25874 sched_getaffinity(25874, 32,  { ffff, 0, 0, 0 }) = 32
25874 sched_getaffinity(25874, 32,  { ffff, 0, 0, 0 }) = 32
25874 gettid()                          = 25874
25874 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
25874 rt_sigprocmask(SIG_UNBLOCK, [HUP ILL BUS FPE SEGV USR2 TERM], NULL, 8) = 0
25874 rt_sigprocmask(SIG_BLOCK, [QUIT], NULL, 8) = 0
25874 mmap(0x41348000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x41348000
25874 mprotect(0x41348000, 12288, PROT_NONE) = 0
25874 futex(0x10632d54, FUTEX_WAIT_PRIVATE, 1, NULL <unfinished ...>
25882 <... futex resumed> )             = -1 ETIMEDOUT (Connection timed out)
25882 futex(0x106cc428, FUTEX_WAKE_PRIVATE, 1) = 0
25882 clock_gettime(CLOCK_MONOTONIC, {39075204, 21489888}) = 0
25882 clock_gettime(CLOCK_REALTIME, {1355008325, 422198000}) = 0
25882 futex(0x106cc454, FUTEX_WAIT_PRIVATE, 1, {0, 49984000}) = -1 ETIMEDOUT (Connection timed out)
25882 futex(0x106cc428, FUTEX_WAKE_PRIVATE, 1) = 0
25882 clock_gettime(CLOCK_MONOTONIC, {39075204, 72479888}) = 0
25882 clock_gettime(CLOCK_REALTIME, {1355008325, 473185000}) = 0
25882 futex(0x106cc454, FUTEX_WAIT_PRIVATE, 1, {0, 49987000}) = -1 ETIMEDOUT (Connection timed out)
25882 futex(0x106cc428, FUTEX_WAKE_PRIVATE, 1) = 0

所以死亡螺旋前的最后一行是read()通道 12 上的。然后它只是循环 futex 和 clock_gettime 直到它被手动杀死。


最后一点可能无关紧要,但如果与此问题类似,我运行调用此脚本的脚本并使用nohup并将输出转移到/dev/null,我会在开始附近得到以下内容(输出文件中大约 100kb):这些:

25664 close(67) = -1 EBADF (Bad file descriptor)

他们从 67,每次增加 +1,到:

25664 close(1023) = -1 EBADF (Bad file descriptor)

然后他们紧随其后

25664 open("/dev/null", O_RDWR) = 3

同样,据我所知,PID 是空的。不确定这是否相关 - 我想这可能会导致使用 nohup 输出到 /dev/null 确实是解决此类问题的一般方法,但我以某种方式做错了,导致这些错误。

4

1 回答 1

2

我很确定问题在于 shell 正在捕获 /sbin/service 脚本它启动的 solr 服务的输出,因此在继续之前将等待服务退出(或至少关闭其标准输出)。这是一个简单的演示:

$ bg_service() { while true; do sleep 10; done; }
$ start_bg_service() { echo "starting"; bg_service& echo "running"; }
$ start_bg_service 
starting
[1] 8656
running
$ var=$(start_bg_service)
[It hangs at this point... until I open another shell and kill the background process]
于 2012-12-09T02:33:46.003 回答