从zfs send流提取crypt_keydata:脚本在systemd中异常问题咨询
从ZFS Send数据流中提取crypt_keydata的问题与解决方案
我需要编写代码,从通过管道传入zstream dump -v的zfs send命令输出中提取crypt_keydata。crypt_keydata位于发送数据流开头的几行(非起始位置),需存入变量,用于对比远程与本地的crypt_keydata,确保拉取备份快照前远程密钥未变更。
初始实现代码
crypt_keydata_backup=$(stdbuf -oL zfs send -w -p "${backup_snapshot}" | stdbuf -oL zstreamdump -d | stdbuf -oL awk '/end crypt_keydata/{exit}1' | stdbuf -oL sed -n '/crypt_keydata/,$ {s/^[ ]*//; p}')
该代码在交互式bash/CLI中运行正常,但在systemd调用脚本时,zfs send命令始终无法终止,导致磁盘满载读取直至完成,耗时极长。
修改后的可行代码
crypt_keydata_backup="" while IFS= read -r line; do crypt_keydata_backup+="${line}"$'\n' if [[ "${line}" == *"end crypt_keydata"* ]]; then kill -SIGTERM "$(cat /tmp/sub_proc.pid)" &>/dev/null rm -f /tmp/sub_proc.pid break fi done< <(stdbuf -oL zfs send -w -p "${backup_snapshot}" | stdbuf -oL zstream dump -v & echo $! > /tmp/sub_proc.pid) # 提取目标crypt_keydata内容 crypt_keydata_backup=$(sed -n '/crypt_keydata/,$ {s/^[ ]*//; p}' <<< "${crypt_keydata_backup}")
该方案在CLI和systemd中均能正常运行,存在两个疑问:
- 这种实现目标的方式是否正确、高效且健壮?
- 为何该方案能在CLI和systemd中都生效,而初始代码仅在CLI中有效?
注:stdbuf -oL似乎能加快两种环境下的进程终止速度,若无必要可移除。
问题解答
1. 该实现方式的正确性、效率与健壮性分析
- 正确性:逻辑是成立的——通过逐行读取
zstream dump的输出,捕获到end crypt_keydata行后主动终止上游zfs send进程,再从已捕获的内容中提取目标数据,能准确拿到所需的crypt_keydata段。 - 效率:相比初始方案高效很多,因为它不会让
zfs send跑完整个快照数据流,拿到目标数据后立刻终止进程,避免了无意义的磁盘IO和时间消耗。stdbuf -oL设置行缓冲,让输出实时传递给后续进程,能更快触发终止逻辑,建议保留。 - 健壮性:存在可优化的点:
/tmp/sub_proc.pid存在竞态风险,多脚本同时运行时会互相覆盖PID文件,导致误杀进程,建议改用进程组终止,或用mktemp生成唯一临时文件。- 仅用
SIGTERM终止进程可能不够稳妥,可先发送SIGTERM,若几秒后进程未终止,再发送SIGKILL强制终止。 - 未处理
read失败的情况(比如上游进程意外终止),可在循环中增加错误判断,避免无限等待。
2. 初始方案在systemd中失效的原因
初始方案里,awk匹配到end crypt_keydata后执行exit退出,但上游的zfs send和zstreamdump不会自动终止:
- 在交互式CLI中,终端TTY会处理管道进程的SIGPIPE信号:
awk退出后管道关闭,zstreamdump向已关闭的管道写数据时收到SIGPIPE信号进而终止,随后zfs send也会因下游进程终止收到SIGPIPE并退出。 - 在systemd的无TTY非交互式环境中,部分进程对SIGPIPE信号的处理逻辑不同,或者
zfs send/zstreamdump未正确响应SIGPIPE,导致它们会持续运行直到整个快照数据流发送完成才终止。
而修改后的方案是主动终止上游的zfs send进程,不受环境对SIGPIPE信号处理的影响,因此在两种环境下都能正常工作。
内容的提问来源于stack exchange,提问作者Maanloper
相关产品推荐
相关产品推荐

