centos Bash:进程块b/c读取循环有时无法从文件描述符读取更多数据,但手动cat /proc/< pid>/fd/< fd>可以

k4aesqcs  于 2022-11-07  发布在  其他
关注(0)|答案(1)|浏览(184)

设定:

我有一个复杂的脚本,它启动postgres示例并将输出重定向到文件描述符3

exec 3< <(su -l postgres -c "/usr/local/pgsql/bin/postmaster -p '$port' -d 3 -D '$dataDir' 2>&1 & echo \$!")
            #Explanation:
            #
            # exec = execute the command
            # 3< send the output of the followint to the file descriptor 3 which later is read by the "read" command
            # <(...) execute the command in () and send the output to the 3< filedescriptor
            # su -l postgres -c "...": Execute the command in "..." as user postgres
            # /usr/.../postmaster  ... -D '$backupDir': Execute postgres
            # 2>&1 redirect sterr to stdout, so both are returned normally
            # & command chaining. Execute one more command if the first succeeded (starting postgres)
            # echo \$!: Echo the PID of this process

然后,我读取postgres写入该文件描述符的数据

while [ true ]
do
  read -u 3 line

  //Do something or break

done

问题:

大多数情况下,这只是工作。但有时一切都卡住了,直到我在命令行上手动执行cat /proc/<pid>/fd/3,这解决了问题,并使脚本继续。

我目前的分析:

我对所发生的事情的假设是,由于***无论什么原因***,read停止清空fd(3)的缓冲区,而是总是将相同的内容放入$line,然后postgres停止,因为它在尝试写入fd(3)时被阻塞,而fd(3)似乎“已满”,导致死锁。
这总是发生在postgres关闭时,它会一次记录大量调试信息。
当我执行cat /proc/<pid>/fd/3时,缓冲区似乎被清空了,postgres继续并关闭,我的脚本也继续阅读日志并检查postgres是否退出。
我尝试增加文件描述符缓冲区,但不确定如何增加。
sysctl -w net.unix.max_dgram_qlen=20480
没有解决这个问题。

问题:

1:为什么手动猫工作时读取失败?
2:如何增加文件描述符的缓冲区?

其他信息:

分操作系统7:3.10.0-1160.15.2.el7.x86_64(现在也发生在最新的Manjaro)
如果多个这样的脚本并行运行(针对不同的postgres数据目录),这种情况似乎会更频繁地发生
这是在服务器进行较大更新后开始发生的,但也可能是巧合,因为当时我们还增加了并行脚本的数量

更新:我做了另一个深入的调查,这一次似乎行为改变了,发生了什么更有意义,至少在Manjaro。

当我在postgres示例上执行一条SQL语句时,它会发生,而不是读取、挂在循环中并总是返回相同的内容。
所以发生的事情是这样的:read没有清空缓冲区,postgres在执行sql语句之前向其写入内容时卡住,sql语句也会卡住,因此无法恢复阅读=〉死锁
所以如果我能以某种方式增加我打开的这个文件描述符的缓冲区大小,这就解决了所有问题。有人想得到赏金吗?

解决方案:

在我的例子中,在向postgres发出SQL语句之前,确保fd(3)被完全清空就足够了。

echo "Empty postgres output buffer before executing sql command"
    lastLine=""
    line=""
    while [ true ]
    do
            read -u 3 -t 1 line 
            if [ $? -ne 0 ]
            then
                    echo "Nothing in buffer";
                    break
            fi
            if [ "$lastLine" == "$line" ]
            then
                    echo "Nothing new in buffer: $line"
                    break
            fi
            lastLine=$line
            echo "Postgres: $line"
    done

然后,有足够的缓冲区剩余,以避免在执行sql命令时尝试写入fd(3)时阻塞postgres。

u7up0aaq

u7up0aaq1#

为了进行故障排除,您是否可以运行此程序?


# export variables port and dataDir

exec 3< <(su -l postgres -c "echo /usr/local/pgsql/bin/postmaster -p '$port' -d 3 -D '$dataDir' 2>&1")

while read -u 3 line
do
    echo "$line"
done

看看你还有没有问题。
请注意,我遗漏了& echo \$!
你能考虑一下这种脚本结构吗?


# !/usr/bin/env bash

# export variables port and dataDir

log_file=/tmp/postmaster.log
su -l postgres -c "echo /usr/local/pgsql/bin/postmaster -p '$port' -d 3 -D '$dataDir'" &>$log_file & echo $!

exec 3< <(tail -f $log_file)

while read -u 3 line
do
    echo "$line"
done

以便将两个进程分离,而不依赖于缓冲区大小。

相关问题