Perl命令行解释器无法退出。

5

我正在Windows上运行一个调用可执行文件的Perl脚本:

 $command = "$path_to_exe -i $dir -o $results";
 my $pid = fork();

    if ( !$pid ) {
        system($command);

        #do stuff

    } else {
        #do stuff
    }

 print "Exiting..."
 exit;

perl.exe没有退出,而是保持空闲状态。然后弹出一个窗口告诉我"Perl命令行解释器已停止工作"。

我对Windows中的进程管理不太了解,在此论坛上我以前读到过使用fork()exec()不是好习惯,但代码除了解释器不关闭部分之外运行良好。我尝试过从Unix中实现程序(这会产生相同的错误)到使用Win32::Process命令,但都无法解决问题。我希望有一个更简单的解决方案,可以让我保留已经编写的内容。

如果有人能解释一下在运行此代码时Windows发生了什么,那也会有所帮助!


fork-exec在win上不是一个好的实践,因为win没有针对它进行优化。Win是针对大规模线程和本地RPC进行优化的。但在你的情况下,这并不是一个真正的问题,因为对于你来说,你的包装脚本产生0.001秒或0.01秒的开销并不重要。只有当你例如有一个数据库服务器守护程序或必须每秒调用数千次的东西时,才会变得重要。另一件事是,是的,fork()-exec()很慢,但fork()-system()也同样慢...我还看到你根本没有回应我的第二个问题($pid而不是$$)。你应该更加合作。 - peterh
3个回答

4
我可以看到两个独立的问题。
1. system() 会创建一个子进程,因此如果你从一个 fork 的子进程中调用 system(),你将会有三个进程。但你只杀掉了第二个(fork 出来的子进程),而没有杀掉子进程的子进程($command)。尝试使用一些函数,比如 exec(),在 Unix 上它会在实际进程的位置(和 pid)上启动子进程。如果你很幸运,在 Windows 上的 Perl 也会这样做。
2. 在父线程中,你杀死的是当前进程 $$,而不是子进程的 pid $pid。可能你想要杀死的是子进程在父线程中的 pid。

我尝试使用exec(),发现它也无法退出。我会再试一次,看看会发生什么。在Windows中,$pid不是子进程的$pid,而是一个负数,我认为它对应于一个“伪进程”。不知何故,我使用taskkill的方式是有效的,别问我怎么做到的。谢谢。 - user0
我认为你生存的方式是停止盲目射击,开始用手和眼睛瞄准。 - peterh

1
我使用了以下代码(它会超时程序,最重要的是不会破坏Perl解释器!):
use Win32::Job;
my $job = Win32::Job->new;

   # Run $command for $max_time
   $job->spawn($Config{"path/cmd.exe"},  $command);
   $job->run($max_time);
   exit;

1

在我的情况下,我删除了一些"use"语句,问题得以解决。 这可能是因为Windows上的perl实现对于"fork"函数不完美,并且在导入某些"重型"对象(例如OLE库)时存在问题。

我删除的确切"use"语句:

#use Win32::OLE qw(in with);
#use Win32::OLE::Const 'Microsoft Excel';

解决方法:如果可能的话,在代码中没有分叉点之后动态导入库。

例如我的情况:

# code with fork
eval "use Win32::OLE::Const 'Microsoft Excel';";
eval "use Win32::OLE qw(in with);";
# code without fork

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接