如何在Bash守护进程-客户端IPC中使用单个命名管道实现多进程通信?
问题:命名管道实现守护进程与客户端通信仅能成功一次
我尝试编写脚本启动一个基于qalc的守护进程,通过两个命名管道$FIFO.out和$FIFO.in分别处理输出与输入,期望客户端能随时和守护进程通信。
现有代码
守护进程脚本
#!/bin/sh # Daemon script FIFO="/tmp/qcalc" mkfifo "$FIFO.out" "$FIFO.in" exec qalc -t -u8 <"$FIFO.in" >"$FIFO.out"
客户端脚本
#!/bin/sh # Client script FIFO="/tmp/qcalc" talk() { echo "$1" >"$FIFO.in" # 跳过前两行输出 read -r _ <"$FIFO.out" read -r _ <"$FIFO.out" read -r result <"$FIFO.out" echo "$result" } talk 1 talk 2
问题现象
当前实现只能完成一次通信,之后客户端会在读取操作时阻塞冻结,运行日志如下:
$ ./daemon & [2] 57848 $ ./client 1 [2] + done ./daemon ^C./client: 13: cannot open /tmp/qcalc.out: Interrupted system call
原因分析
问题出在:qalc在处理完第一次输入后,因为$FIFO.in的写入端被客户端关闭了(客户端第一次执行echo后,管道的写入端就关闭了),qalc会认为输入已经结束,直接退出进程——这就是日志里显示守护进程done的原因。后续客户端再尝试读取$FIFO.out时,已经没有进程往这个管道写入数据,因此会一直阻塞。
解决方案
修改守护进程脚本:保持管道写入端持续打开
我们需要让$FIFO.in的写入端始终处于打开状态,避免qalc因为收到EOF而退出。修改后的脚本如下:
#!/bin/sh # Daemon script FIFO="/tmp/qcalc" mkfifo "$FIFO.out" "$FIFO.in" # 打开文件描述符3指向FIFO.in的写入端,保持管道不被关闭 exec 3>"$FIFO.in" # 启动qalc进程,关联管道 qalc -t -u8 <"$FIFO.in" >"$FIFO.out" # 退出时清理管道和文件描述符 trap 'rm -f "$FIFO.out" "$FIFO.in"; exec 3>&-' EXIT
修改客户端脚本:稳定写入与读取
客户端每次写入后不要直接关闭管道写入端,同时用循环读取的方式确保拿到正确结果,避免因输出行数变化导致问题:
#!/bin/sh # Client script FIFO="/tmp/qcalc" talk() { # 打开文件描述符4指向FIFO.in的写入端,保持写入通道稳定 exec 4>"$FIFO.in" echo "$1" >&4 # 循环读取输出,跳过前两行,获取第三行结果 line_count=0 result="" while read -r line <"$FIFO.out"; do line_count=$((line_count + 1)) if [ "$line_count" -eq 3 ]; then result="$line" break fi done echo "$result" # 关闭文件描述符 exec 4>&- } talk 1 talk 2
说明
- 守护进程通过打开额外的文件描述符保持
$FIFO.in写入端打开,即使客户端每次写入后关闭自己的写入通道,管道依然不会触发EOF,qalc会持续运行等待下一次输入。 - 客户端用文件描述符写入,避免每次
echo后关闭管道,同时循环读取确保准确获取结果,提升通信稳定性。
内容的提问来源于stack exchange,提问作者Utkarsh Verma
相关产品推荐
相关产品推荐

