【问题标题】:What are the Perl techniques to detach just a portion of code to run independently?哪些 Perl 技术可以分离部分代码以独立运行?
【发布时间】:2013-02-19 16:55:15
【问题描述】:

我不参与接近操作系统的编程技术,但据我所知,当涉及到在 Perl 中并行执行某些事情时,首选的武器是fork,可能还有一些基于它的有用模块。 fork 的文档页面说:

Does a fork(2) system call to create a new process running the same program at the same point.

因此,拥有一个消耗大量内存的大型应用程序并为一个小任务调用 fork 意味着将有 2 个大的 perl 进程,而第二个将浪费资源来做一些简单的工作。

所以,问题是:要做什么(或如何使用fork,如果它是唯一的方法),以便让代码的分离部分独立运行并只消耗它需要的资源强>?

只是一个非常简单的例子:

    use strict;
    use warnings;

    my @big_array = ( 1 .. 2000000 );  # at least 80 MB memory
    sleep 10;  # to have time to inspect easely the memory usage

    fork();
    sleep 10;  # to have time to inspect easely the memory usage

子进程也消耗 80+ MB。

明确一点:与这个分离代码进行通信或以某种方式使用它的结果并不重要,只是为了可以说“嘿,为我运行这个简单的任务背景,让我同时继续我繁重的工作......不要浪费我的资源!”在运行繁重的 perl 应用程序时。

【问题讨论】:

  • 子进程将继承其父进程的所有属性。我认为没有真正的方法可以通过分叉来解决这个问题。
  • 使用fork 不是强制性的。任何技术都是可以接受的,甚至调用system

标签: perl process fork


【解决方案1】:

fork()exec() 是你的兔子。你 fork() 创建一个新进程(这是一个相当便宜的操作,见下文),然后 exec() 替换你运行的大的 perl 更小的东西。这看起来像这样:

use strict;
use warnings;
use 5.010;

my @ary = (1 .. 10_000_000);

if (my $pid = fork()) {
    # parent
    say "Forked $pid from $$; sleeping";
    sleep 1_000;
} else {
    # child
    exec('perl -e sleep 1_000');
}

@ary只是用来填满原进程的内存而已。)

我说fork()ing 相对便宜,尽管它确实复制了整个原始过程。这些陈述并不冲突;设计fork 的人注意到了同样的问题。复制是惰性的,即只复制实际改变的位。

如果您发现您希望进程相互通信,您将开始进入更复杂的 IPC 领域,已经写了很多书。

【讨论】:

  • 不会在调用exec()之前复制内存很短的时间,然后在子进程终止后释放内存吗?毕竟,如果一切都集中在调用 system/exec 上,那么甚至不需要使用 fork :) .. 好的,对于 exec 这是必需的,但 system('... &'); 似乎做得很好
  • @ArtM 如前所述,这是一个懒惰的副本。 system('... &') fork() 后面跟着exec(),中间有一个外壳,只是为了笑,而且启动时更脆弱。有不止一种方法可以做到这一点;我喜欢我的方式更好。
  • 在 Linux 上,每个 PID>1 的进程都是通过 fork(最初是 init)创建的。例如,如果您在终端中运行某些东西,bash(或其他)将分叉执行它。 Copy-on-write 语义确保这是高效的。
【解决方案2】:

您的分叉进程实际上并未使用 80MB 的常驻内存。该内存的很大一部分将被共享 - 从父进程“借用”,直到父进程或子进程写入它,此时 copy-on-write 语义将导致内存被实际复制。

如果您想完全放下包袱,请在您的 fork 中运行 exec。这将用不同的可执行文件替换子 Perl 进程,从而释放内存。如果您不需要向父母传达任何信息,这也是完美的。

【讨论】:

  • 不确定词法变量是否共享​​>。当两个进程都在运行时,我在ps 的 RES/SHR 列下得到这些值:48m/1776 和 46m/156(1M 数组元素)。
  • 对不起,上面的评论应该是top而不是ps
【解决方案3】:

没有办法只分叉你的进程足迹的一个子集,所以通常的解决方法归结为:

  1. fork 在父进程中运行内存密集型代码之前
  2. 使用systemopen HANDLE,'|-',... 启动一个单独的进程。当然,这个新进程不会从其父进程继承任何数据,因此您需要以某种方式将数据传递给该子进程。

【讨论】:

    【解决方案4】:

    在大多数操作系统上实现的fork() 非常有效。它通常使用一种称为写时复制的技术,这意味着页面最初是共享的,直到一个或其他进程写入它们。无论如何,您的很多进程内存都将是只读映射文件。

    仅仅因为一个进程在fork() 之前使用 80MB 并不意味着之后两个将使用 160。一开始它只会比 80MB 多一点点,直到每个进程开始污染更多页面。

    【讨论】:

    • 我认为它应该是直到至少一个进程开始弄脏......或者逻辑上等同于此。好吧,这个my @big = (1 .. 1000000) 只是一个简单的通用示例代码,它可能会更复杂并扩展到整个代码空间。感谢您的干预。
    猜你喜欢
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    相关资源
    最近更新 更多