You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows端fork子进程SSH到Linux执行脚本偶发卡住问题求助

故障背景
  • 代码运行逻辑:fork生成子进程后,子进程从Windows侧SSH登录Linux机器qwelnx45,运行script1_bkt.csh脚本,日志输出到Windows路径$AngleLogFilePath\Angle_${date}.log,对应实际路径为V:\ricky_angle_testing_63.1\depot\vfr\63.10\main\logs\angleLogsrickyPersonal\Angle_${date}.log。
  • 前台父进程trialSet.pl、trialSetDepWint.pl运行状态正常。
  • V盘为映射/dv/lmv/mentor/路径的CIFS网络存储。
问题现象
  • fork生成的子进程从Windows SSH到Linux运行script1_bkt.csh时偶发卡住,非必现。
  • 关键特征:Linux端script1_bkt.csh的PID运行一段时间后消失,说明脚本执行完成,但Windows侧触发脚本的ssh.exe PID仍存在,对应命令$GoToUnix cd $ClientAltRoot/lkg ; source script1_bkt.csh未正常结束,陷入卡住状态。脚本正常执行耗时约3小时,偶发会无限卡住。
  • 关联现象:Windows端子进程卡住时,即便Linux端脚本已执行完毕,对应的日志文件内容不完整,缺少脚本输出的部分数据,疑似进程卡住后停止写入日志。
相关代码
use File::Path qw( make_path );
use POSIX qw(strftime);

my $ClientAltRoot           = "/dv/lmv/mentor/ricky_angle_testing_63.1/depot/vfr/63.10/main/";
my $GoToUnix              = "C:\\cygwin\\bin\\ssh.exe qwelnx45";
my $AngleLogFilePath    = "V:\\ricky_angle_testing_63.1\\depot\\vfr\\63.10\\main\\logs\\angleLogsrickyPersonal";
my $date                    = strftime("%Y%m%d_%H%M%S", localtime(time));

make_path("$AngleLogFilePath") or warn  "Failed to create dir $AngleLogFilePath";

my $aqpid;
# fork angle process
if ($aqpid = fork()) {
    $SIG{CHLD} = 'DEFAULT';
} elsif (defined ($aqpid)) { 
    sleep 10;

    print "Angle child started\n";
    $angleReturnStatus = system ("$GoToUnix cd $ClientAltRoot/lkg ; source script1_bkt.csh > $AngleLogFilePath\\Angle_${date}.log ");
    $angleFailed += 1 if ($angleReturnStatus > 0);
    exit 0; 
}

print "##### Starting the foreground script ###### \n";
system "$GoToUnix \"cd /home/ricky/; echo abc ;  /home/ricky/trialSet.pl > setTrial_ricky/set_${date}.log\"  ";
print "Ended SetDep\n";     

print "Waiting as child process has not ended";
1 while (wait() != -1);

system ( "perl C:\\ricky\\Testing\\trialSetDepWint.pl ");
print "Demo script ended\n";
根因分析
  1. SSH流缓冲区阻塞:当前命令仅重定向了标准输出到CIFS路径的日志文件,未处理标准错误流。当脚本产生大量stderr输出时,SSH缓冲区被占满会导致进程挂起,即便远程脚本执行完毕,SSH也不会退出,因为缓冲区还有未写入的数据。再加上CIFS网络存储本身存在写入抖动,日志写入出现短暂超时时,会进一步加剧缓冲区无法清空的问题,最终导致进程卡住。
  2. Cygwin SSH终端适配问题:Cygwin版本的SSH默认远程执行命令时不会分配伪终端,若脚本在特定分支逻辑下尝试读取终端输入(比如隐性调用read、弹出交互确认、密码提示),会因无终端可用而挂起,这也是问题偶发的核心诱因之一。
  3. SSH连接超时僵死:脚本执行时长长达3小时,若中间网络出现短暂波动、防火墙会话超时,会导致SSH连接被静默中断,但TCP层未检测到断连,SSH进程就会陷入僵死状态无法退出。
  4. Windows Perl fork模拟缺陷:Windows无原生fork机制,Perl的fork是用线程模拟实现的,当前设置的$SIG{CHLD} = 'DEFAULT'信号处理逻辑在模拟场景下可能存在异常,导致子进程退出后状态未被正确回收,或子进程内部的system调用信号传递失败。
解决方案

快速验证修复

首先修改子进程内的system调用,补充SSH参数和流重定向逻辑:

$angleReturnStatus = system ("$GoToUnix -n -o BatchMode=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=10 \"cd $ClientAltRoot/lkg ; source script1_bkt.csh 2>&1\" > $AngleLogFilePath\\Angle_${date}.log ");

参数说明:

  • -n:将SSH标准输入重定向到/dev/null,避免脚本尝试读取输入导致挂起
  • -o BatchMode=yes:禁用所有交互提示,遇到需要密码/确认的场景直接报错退出,不会卡住
  • -o ServerAliveInterval=30:每30秒向服务端发送保活包
  • -o ServerAliveCountMax=10:连续10次保活失败自动断开SSH连接,避免僵死
  • 2>&1:将标准错误合并到标准输出,避免stderr缓冲区满导致的阻塞

如果修改后仍偶发卡住,可以临时把日志输出路径改到Windows本地磁盘而非CIFS存储,验证是否是CIFS写入抖动导致的问题。如果本地路径无故障,说明CIFS是核心诱因,可以调整为先写本地磁盘,脚本执行完成后再异步同步到CIFS路径。

进阶优化

  1. 用IPC::Run替代system调用执行SSH命令,主动设置超时阈值,超过3.5小时就强制杀掉SSH进程,避免无限卡住:
use IPC::Run qw( run timeout );
my $cmd = [split(/ /, $GoToUnix), '-n', '-o', 'BatchMode=yes', '-o', 'ServerAliveInterval=30', '-o', 'ServerAliveCountMax=10', "cd $ClientAltRoot/lkg ; source script1_bkt.csh 2>&1"];
run $cmd, '>', "$AngleLogFilePath\\Angle_${date}.log", timeout(3.5*3600) or do {
    $angleFailed +=1;
};
  1. 在script1_bkt.csh末尾添加exit 0,强制脚本执行完后主动关闭会话。
  2. 父进程增加子进程运行状态监控,定时检查子进程运行时长,超过阈值就强制杀掉子进程和对应的SSH进程,避免阻塞后续任务执行。

内容的提问来源于stack exchange,提问作者AskQuestionsP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 08:39:03