1

I'm trying to get a perl script to loop very quickly (in Solaris).

I have something like this:

#! /bin/perl

while ('true')
{
 use strict;
 use warnings;
 use Time::HiRes;

 system("sh", "shell script.sh");
 Time::HiRes::usleep(10);
}

I want the perl script to execute a shell script every 10 microseconds. The script doesn't fail but no matter how much I change the precision of usleep within the script, the script is still only being executed approx 10 times per second. I need it to loop much faster than that.

Am I missing something fundamental here? I've never used perl before but I can't get the sleep speed I want in Solaris so I've opted for perl.

TIA

Huskie.

EDIT:

Revised script idea thanks to user comments - I'm now trying to do it all within perl and failing miserably! Basically I'm trying to run the PS command to capture processes - if the process exists I want to capture the line and output to a text file.

#! /bin/perl

while ('true')
{
 use strict;
 use warnings;
 use Time::HiRes;

 open(PS,"ps -ef | grep <program> |egrep -v 'shl|grep' >> grep_out.txt");
 Time::HiRes::usleep(10);
}

This returns the following error:

Name "main::PS" used only once: possible typo at ./ps_test_loop.pl line 9.
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Huskie69
  • 795
  • 3
  • 11
  • 31
  • 1
    How long is shell script.sh? – Toto Oct 03 '14 at 09:49
  • 3
    You aren't going to be able to create 100,000 processes a second on many computers... you will need to change `script.sh` so it runs in a loop within itself without exiting and then does a `sleep` at the end of each loop. That way you create one process that runs a long time instead of 100,000 per second. – Mark Setchell Oct 03 '14 at 09:52
  • 2
    Shell scripts are not particularly fast because they tend to launch external processes for performing even the most trivial tasks. You may need to rewrite the script in Perl or even C to reach the 100000iterations/second. – salva Oct 03 '14 at 10:02
  • Thanks Mark, I hadn't thought about it that way! – Huskie69 Oct 03 '14 at 10:04
  • Thanks Salva - I've edited my original - trying to do it all within perl now – Huskie69 Oct 03 '14 at 10:05
  • 1
    https://metacpan.org/pod/Proc::ProcessTable instead of `ps -ef` – mpapec Oct 03 '14 at 10:14
  • @mpapec - I don't have that perl module installed - and it would take about 6 months for a sysadmin to give me the go ahead to install it! – Huskie69 Oct 03 '14 at 10:55
  • 1
    Little comment: the `use strict; use warnings;` lines should go directly under the `#!/bin/perl` so those pragmas get turned on straight away. You can also do `open(my $ps, "ps -ef | grep |egrep -v 'shl|grep' >> grep_out.txt");` to get rid of the warning. – i alarmed alien Oct 03 '14 at 11:23
  • You can get the information you want without running any external program just looking at the links `/proc/*/exe`. Perl provides the built-in `readlink` for reading symbolic links. – salva Oct 03 '14 at 11:23
  • To make the error go away, add close PS; to your program. Of course, in your case, the open doesn't make sense, because you aren't doing anythin with the pipe. It makes more sense to use system() instead of open(). – user1934428 Oct 06 '14 at 14:05

2 Answers2

1

The bottleneck is the creation of processes, pipes, and opening the output file. You should be doing that at most once, instead of doing it in each iteration. That's why you need to do everything in Perl if you want to make this faster. Which means: don't call the ps command, or any other command. Instead, read from /proc or use Proc::ProcessTable, as the comments suggest.

Incidentally: the use statement is executed only once (it is essentially a shorthand for a require statement wrapped in a BEGIN { } clause), so you might as well put that at the top of the file for clarity.

reinierpost
  • 8,425
  • 1
  • 38
  • 70
1

This is a pure perl program (not launching any external process) that looks for processes running some particular executable:

#!/usr/bin/perl

use strict;
use warnings;

my $cmd = 'lxc-start';

my $cmd_re = qr|/\Q$cmd\E$|;

$| = 1;

while (1) {
    opendir PROC, "/proc" or die $!;
    while (defined(my $pid = readdir PROC)) {
        next unless $pid =~ /^\d+$/;
        if (defined(my $exe = readlink "/proc/$pid/exe")) {
            if ($exe =~ $cmd_re) {
                print "pid: $pid\n";
            }
        }
    }
    closedir PROC;
    # sleep 1;
}

On my computer this runs at 250 times/second.

salva
  • 9,943
  • 4
  • 29
  • 57
  • This would be ideal but I'm also trying to see the values of the variables that are passed to the process. I have tested the example - am I right in thinking I should be replacing my $cmd with the process name I'm hoping to pickup? If so, I've tried replacing it with just'grep' and ran a grep from another session but the script isn't picking it up. – Huskie69 Oct 03 '14 at 13:20