Perl、fork、信号量、进程

发布于 2024-11-10 05:00:45 字数 1803 浏览 2 评论 0原文

我需要创建一个程序,它可以从列表中以随机顺序同时运行 3 个进程,并用信号量一一锁定这些进程,以避免重复。 例如,您有一个包含 3 个程序的列表:

@array = ( 1,2,3);
  1. perl script.pl 首先运行 2;然后运行 ​​2。
  2. 通过随机尝试再次运行 2 并收到错误(因为 2 现在已被信号量锁定)。
  3. 运行 1。
  4. 运行 3。script.pl
  5. 等待所有 1、2、3 结束工作,然后自行退出。

到目前为止,这是我的代码:

#!/usr/bin/perl -w
use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT);
use IPC::Semaphore;
use Carp ();

print "Program started\n";

sub sem {
    #semaphore lock code here
  }

sub chooseProgram{
    #initialise;
    my $program1 = "./program1.pl";
    my $program2 = "./program2.pl";
    my $program3 = "./program3.pl";
    my $ls = "ls";
    my @programs = ( $ls, $program1, $program2, $program3 );    

    my $random = $programs[int rand($#programs+1)];
    print $random."\n";
    return $random;
}

#parent should fork child;
#child should run random processes;
#avoid process clones with semaphore;
sub main{                   
    my $pid = fork();
    if ($pid){
        #parent here
    }
    elsif (defined($pid)){
        #child here     
        print "$$ Child started:\n";
        #simple cycle to launch and lock programs
        for (my $i = 0; $i<10; $i++){
            # semLock(system(chooseProgram()); #run in new terminal window
            # so launched programs are locked and cannot be launched again
        }
    }
    else {
        die("Cannot fork: $!\n");
    } 
    waitpid($pid, 0);
    my $status = $?;
    #print $status."\n";
}
main();
exit 0;

问题:

  1. 需要锁定文件; (我不知道如何使用信号量。锁定文件的一些尝试失败,因此排除了该代码。)
  2. 子进程会等到第一个程序结束后再开始第二个程序。我怎样才能为一个孩子同时启动三个项目? (是否可能或应该为一个程序创建一个子项?)。
  3. 程序是非 GUI 的,应该在终端中运行。如何在新的终端窗口(选项卡)中运行程序?
  4. 无法正确检查 @programs 的所有程序是否已启动。 ——不太重要。

I need to create a program that would run 3 processes at the same time in random sequence from a list and lock those processes with semaphore one by one so to avoid duplicates.
For example, you have a list of 3 programs:

@array = ( 1,2,3);
  1. perl script.pl runs 2 at first;
  2. By random tries to run 2 again and receives an error (because 2 is now locked with semaphore).
  3. Runs 1.
  4. Runs 3.
  5. script.pl waits all of 1,2,3 to end work and then exit itself.

Here's my code so far:

#!/usr/bin/perl -w
use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT);
use IPC::Semaphore;
use Carp ();

print "Program started\n";

sub sem {
    #semaphore lock code here
  }

sub chooseProgram{
    #initialise;
    my $program1 = "./program1.pl";
    my $program2 = "./program2.pl";
    my $program3 = "./program3.pl";
    my $ls = "ls";
    my @programs = ( $ls, $program1, $program2, $program3 );    

    my $random = $programs[int rand($#programs+1)];
    print $random."\n";
    return $random;
}

#parent should fork child;
#child should run random processes;
#avoid process clones with semaphore;
sub main{                   
    my $pid = fork();
    if ($pid){
        #parent here
    }
    elsif (defined($pid)){
        #child here     
        print "$ Child started:\n";
        #simple cycle to launch and lock programs
        for (my $i = 0; $i<10; $i++){
            # semLock(system(chooseProgram()); #run in new terminal window
            # so launched programs are locked and cannot be launched again
        }
    }
    else {
        die("Cannot fork: $!\n");
    } 
    waitpid($pid, 0);
    my $status = $?;
    #print $status."\n";
}
main();
exit 0;

Problems:

  1. Need to lock file; (I don't know how to work with semaphore. Failed some attempts to lock files so excluded that code.)
  2. Child waits until first program ends before second start. How can I start three of programs at the same time with one child? (Is it possible or should I create one child for one program?).
  3. Programs are non-gui and should run in terminal. How to run a program in new terminal window(tab)?
  4. No correct check if all programs of @programs were launched yet. -- less important.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

z祗昰~ 2024-11-17 05:00:45

您的随机性要求非常奇怪,但如果我正确理解您的要求,那么您不需要任何类型的锁定来执行您想要的操作。 (所以你问题中的 1) 消失了)

首先对程序数组进行洗牌,然后启动该洗牌数组的每个命令(这涉及你的 4))。然后,只有waitpid您启动所有操作(涉及您的2)之后。

下面的代码就是这样做的,在新终端中启动各种 sleep 实例(我使用 urxvt,根据您想要生成的终端进行调整 - 这涉及您的 3 ))。

#! /usr/bin/perl -w
use strict;
use warnings;

my @progs = ("urxvt -e sleep 5", "urxvt -e sleep 2", "urxvt -e sleep 1");
my @sgrop;
my @pids;

# Shuffle the programs
while (my $cnt = scalar(@progs)) {
    push @sgrop, splice @progs, int(rand($cnt)), 1;
}

# Start the progs
foreach my $prog (@sgrop) {
    my $pid = fork();
    if (!$pid) {
        exec($prog);
        # exec does not return
    } else {
        print "Started '$prog' with pid $pid\n";
        push @pids, $pid;
    }
}

# Wait for them
map {
    waitpid($_, 0);
    print "$_ done!\n";
} (@pids);

不确定洗牌是否是最好的,但它确实有效。其背后的想法只是从初始(已排序)列表中随机选择一个元素,将其从那里删除并将其添加到已打乱的列表中。重复直到初始列表为空。

如果您试图在系统范围内锁定程序(即系统中没有其他进程应该能够启动它们),那么我很抱歉,但这是不可能的,除非程序保护自己免受并发执行。

如果您的问题是关于信号量的,那么很抱歉我错过了您的观点。 IPC 文档 有示例代码。我真的认为没有必要为了你想要做的事情而变得那么复杂。

以下是如何使用 IPC::Semaphore 方便的模块。

在 main 的开始处,创建一个包含所需数量的信号量的信号量集:

use IPC::SysV qw(S_IRUSR S_IWUSR IPC_CREAT IPC_NOWAIT);
use IPC::Semaphore;

my $numprocs = scalar(@progs);
my $sem = IPC::Semaphore->new(1234, # this random number is the semaphore key. Use something else
    $numprocs,                      # number of semaphores you want under that key
    S_IRUSR | S_IWUSR | IPC_CREAT);

检查错误,然后将所有信号量初始化为 1。

$sem->setall( (1) x $numprocs) || die "can't set sems $!";

在启动进程的代码中,在开始之前(尽管在 fork 之后),尝试获取信号量:

if ($sem->op($proc_number, -1, IPC_NOWAIT)) {
  # here, you got the semaphore - so nothing else is running this program
  # run the code
  # and once the code is done:
  $sem->op($proc_number, 1, 0); # release the semaphore
  exit(0);
} else {
  # someone else is running this program already
  exit(1); # or something
}

在上面,每个程序的 $proc_number 必须是唯一的(例如,它可能是程序数组中的索引)。 不要使用exec来启动程序。例如,使用system

请注意,在这种情况下,您将必须处理子进程的退出代码。如果退出代码为零,您可以将该程序标记为已运行。如果没有,您需要重试。 (这会变得混乱,您需要跟踪哪个程序已运行或未运行。我建议使用程序编号($proc_number)进行哈希,您可以在其中存储它是否已经运行完成与否,以及当前正在运行(或尝试运行)该代码的 pid,您可以使用该哈希来找出仍需要执行的程序。)

最后,在所有完成之后,您已经等待了所有子项,你应该自己清理:

$sem->remove;

此代码缺少正确的错误如果清理工作没有正确完成(即代码启动时信号量已经存在),检查将会奇怪地工作(即根本不好)。但它应该让你开始。

Your randomness requirement is very strange, but if I understood your requirements correctly, you don't need any sort of locking to do what you want. (So 1) in your question is gone)

Start by shuffling the program array, then start each command of that shuffled array (this deals with your 4)). Then only waitpid after you've started everything (which deals with your 2)).

The code below does that, starting various sleep instances in new terminals (I use urxvt, adapt depending on what terminal you want to spawn - this deals with your 3)).

#! /usr/bin/perl -w
use strict;
use warnings;

my @progs = ("urxvt -e sleep 5", "urxvt -e sleep 2", "urxvt -e sleep 1");
my @sgrop;
my @pids;

# Shuffle the programs
while (my $cnt = scalar(@progs)) {
    push @sgrop, splice @progs, int(rand($cnt)), 1;
}

# Start the progs
foreach my $prog (@sgrop) {
    my $pid = fork();
    if (!$pid) {
        exec($prog);
        # exec does not return
    } else {
        print "Started '$prog' with pid $pid\n";
        push @pids, $pid;
    }
}

# Wait for them
map {
    waitpid($_, 0);
    print "$_ done!\n";
} (@pids);

Not sure the shuffling is the best out there, but it works. The idea behind it is just to pick one element at random from the initial (sorted) list, remove it from the there and add it to the shuffled one. Repeat until the initial list is empty.

If you're trying to lock the programs system wide (i.e. no other process in your system should be able to start them), then I'm sorry but that's not possible unless the programs protect themselves from concurrent execution.

If your question was about semaphores, then I'm sorry I missed your point. The IPC documentation has sample code for that. I don't really think it's necessary to go to that complexity for what you're trying to do though.

Here's how you could go about it using the IPC::Semaphore module for convenience.

At the start of your main, create a semaphore set with as many semaphores as required:

use IPC::SysV qw(S_IRUSR S_IWUSR IPC_CREAT IPC_NOWAIT);
use IPC::Semaphore;

my $numprocs = scalar(@progs);
my $sem = IPC::Semaphore->new(1234, # this random number is the semaphore key. Use something else
    $numprocs,                      # number of semaphores you want under that key
    S_IRUSR | S_IWUSR | IPC_CREAT);

Check for errors, then initialize all the semaphores to 1.

$sem->setall( (1) x $numprocs) || die "can't set sems $!";

In the code that starts your processes, before you start (after the fork though), try to grab the semaphore:

if ($sem->op($proc_number, -1, IPC_NOWAIT)) {
  # here, you got the semaphore - so nothing else is running this program
  # run the code
  # and once the code is done:
  $sem->op($proc_number, 1, 0); # release the semaphore
  exit(0);
} else {
  # someone else is running this program already
  exit(1); # or something
}

In the above, $proc_number must be unique for each program (could be it's index in your programs array for instance). Don't use exec to start the program. Use system instead for example.

Note that you will have to deal with the exit code of the child process in this case. If the exit code is zero, you can mark that program as having run. If not, you need to retry. (This is going to get messy, you'll need to track which program was run or not. I'd suggest a hash with the program number ($proc_number) where you'd store whether it already completed or not, and the current pid running (or trying to run) that code. You can use that hash to figure out what program still needs to be executed.)

Finally after all is done and you've waited for all the children, you should clean up after yourself:

$sem->remove;

This code lacks proper error checking, will work strangely (i.e. not well at all) if the cleanup was not done correctly (i.e. semaphores are already laying around when the code starts). But it should get you started.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文