Schrift
[thread]6926[/thread]

Zeitbegrenzung für reguläre Ausdrücke (Seite 3)



<< |< 1 2 3 4 5 >| >> 41 Einträge, 5 Seiten
Taulmarill
 2005-04-26 16:51
#54181 #54181
User since
2004-02-19
1750 Artikel
BenutzerIn

user image
ich glaub ich hab was gutes gefunden :-)
der trick ist, in den regex möglicht performanten perlcode einzubetten, damit mehrere opcodes ausgeführt werden, dann greift alarm auch wieder. ich hab einfach mal die koknstante 1 benutzt, was trivialeres fiel mir auf die schnelle nicht ein.
der alarm greift hier sobald der eingebettete perlcode nach der alarmzeit ausgeführt wird, 'ne verzögerung von ein paar sekunden ist also drin. evtl. lässt sich das noch tunen indem man das im regex verschiebt bzw. an weiteren stellen platziert. performancemässig macht das <1% aus.
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;

use Time::HiRes qw(time);
use constant TIMEOUT => 1;

my $bigString = pack "C*", 33 .. 126;
$bigString = $bigString x 1_000_000;
my $time_to_die = time + TIMEOUT;

eval {
local $SIG{ALRM} = sub { die "regex timed out at ", time, "\n" };
alarm TIMEOUT;

print "time to die at $time_to_die\n";
print "begin regex at ", time, "\n";
my ( $foo, $bar, $baz ) = $bigString =~ /(?{1})(abc).*(def).*(ghi)/;
print "foo = $foo, bar = $bar, baz = $baz\n";
print "end regex at ", time, "\n";

alarm 0;
};

print "error while regex. $@\n" if $@;

use Benchmark qw(:all);

cmpthese ( 5, {
'mit ' => sub { $bigString =~ /(?{1})(abc).*(def).*(ghi)/ },
'ohne ' => sub { $bigString =~ /(abc).*(def).*(ghi)/ }
} );
$_=unpack"B*",~pack"H*",$_ and y&1|0& |#&&print"$_\n"for@.=qw BFA2F7C39139F45F78
0A28104594444504400 0A2F107D54447DE7800 0A2110453444450500 73CF1045138445F4800 0
F3EF2044E3D17DE 8A08A0451412411 F3CF207DF41C79E 820A20451412414 83E93C4513D17D2B
Strat
 2005-04-26 17:27
#54182 #54182
User since
2003-08-04
5246 Artikel
ModeratorIn
[Homepage] [default_avatar]
ist es moeglich, die RE auf mehrere aufzuteilen? haeufig sind mehrere kleinere schneller als eine grosse...
perl -le "s::*erlco'unaty.'.dk':e,y;*kn:ai;penmic;;print"
http://www.fabiani.net/
Crian
 2005-04-26 19:41
#54183 #54183
User since
2003-08-04
5873 Artikel
ModeratorIn
[Homepage]
user image
So, ich habe jetzt eine laufende gemischte Fork/Alarm-Lösung:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/perl
use strict;
use warnings;

use POSIX ":sys_wait_h";


pipe PREAD, CWRITE; # child -> parent

my $parent_pid = $$;


if (my $pid = fork()) {
parent($pid);
}
else {
child();
}


sub child {
close PREAD; # child - close parent end of pipe
print "child: id $$ dad $parent_pid\n";

print "child: start of proccess\n";
syswrite CWRITE, "0\n";
for (1..9) {print "child zZZZzzz\n";sleep 1;}
print "child is awaking\n";
syswrite CWRITE, "$_\n" for 1 .. 10;
print "child ending\n";
exit;
} # sub child


sub parent {
my ($pid) = @_;
close CWRITE; # parent - close child end of pipes
print "parent id $$ child $pid\n";

local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
my @erg;
eval {
alarm 15;
while (waitpid($pid, WNOHANG) == 0) {
print "parent: kind lebt noch\n";
sleep 1;
}
alarm 0;
};
if ($@) {
die "other error : $@"
unless $@ eq "alarm\n"; # propagate unexpected errors
warn "parent : child got time out!\n";
# ... Maßnahmen ergreifen um klarzumachen, dass die Verarbeitung
# unvollständig ist ...
}
else {
alarm 0; # Ist das wirklich notwendig?
chomp(@erg = <PREAD>);
print "parent got ", join(' - ', @erg), "\n";
print "parent : ", (eof(PREAD)?'ENDE':'keine Ende'), "\n";
}
kill TERM => $pid; # Kind abschießen (in beiden Fällen, einmal als Zombie,
# einmal läuft es noch und verbraucht Rechenzeit)
} # sub parent


Sinn und Zweck war, das Kind nach Überschreitung einer gewissen Zeit (hier 15 Sekunden, siehe "alarm 15") am Weiterrechnen zu hindern, andererseits aber sofort, wenn das Kind vor Ablauf dieser Zeit fertig sein sollte, weitermachen zu können.

So sieht der Ablauf in der "Shell" aus:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
C:\Daten\perl\fork>f5.pl
parent id 1500 child -1520
parent: kind lebt noch
child: id -1520 dad 1500
child: start of proccess
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
child is awaking
child ending
parent: kind lebt noch
parent got 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10
parent : ENDE


Nach 9 Sekunden wacht das Kind auf, schreibt seine Daten in die Pipe und beendet sich. Der Vater nimmt die Daten entgegen und alles ist gut ;)


Und so sieht er aus, wenn man statt der 15 Sekunden nur 5 Sekunden wartet (mit "alarm 5"):

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
C:\Daten\perl\fork>f5.pl
parent id 1340 child -1520
parent: kind lebt noch
child: id -1520 dad 1340
child: start of proccess
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent: kind lebt noch
child zZZZzzz
parent : child got time out!
Terminating on signal SIGTERM(15)


Nach 5 Sekunden unterbricht der ungeduldige Vater das saumselige Kind und -ähm- beendet es.
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
Crian
 2005-04-26 19:49
#54184 #54184
User since
2003-08-04
5873 Artikel
ModeratorIn
[Homepage]
user image
[quote=Taulmarill,26.04.2005, 14:51]ich glaub ich hab was gutes gefunden :-)
der trick ist, in den regex möglicht performanten perlcode einzubetten, damit mehrere opcodes ausgeführt werden, dann greift alarm auch wieder. ich hab einfach mal die koknstante 1 benutzt, was trivialeres fiel mir auf die schnelle nicht ein.[/quote]
cool - leider hab ich das erst jetzt gesehen :-/

Allerdings sagt er mir "Eval-group not allowed at runtime, use re 'eval' in regex ...", wenn ich das bei mir einbaue?!

Egal, ich werd eh meine Lösung verwenden, denke ich mal, wozu hab ich die sonst ausgebrütet ;)
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
ptk
 2005-04-26 20:47
#54185 #54185
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
use re "eval" kannst du verwenden, wenn die eingegebenen Regexps aus einer sicheren Quelle stammen, ansonsten sind Code injections moeglich.
Crian
 2005-04-27 11:34
#54186 #54186
User since
2003-08-04
5873 Artikel
ModeratorIn
[Homepage]
user image
Ah danke, das werd ich ausprobieren. Die RE's sind von mir, und mir muss ich schon trauen, "ich" könnte ja auch gaaaanz schlimme Sachen in den Code einbauen ;)

Kann man das auch auf den Block begrenzt festsetzen, in dem diese Evals verwendet werden, oder gilt das dann für die ganze Datei (wegen dem use)?

Edit: Hmmm die anderen Dinge mit use strict "blablubb" gelten ja auch nur lokal...

Ich hab das jetzt mal eingebaut, leider bringt mir das anscheined aber auch keine Opcodes, selbst wenn ich den Timer auf eine Sekunde runter setze, bekomme ich keine Timer-Meldungen.


Edit2: Ich habe obiges Fork-Programm noch etwas verschönert zu

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/perl
use strict;
use warnings;

use POSIX ":sys_wait_h";


pipe PREAD, CWRITE; # child -> parent

my $parent_pid = $$;


if (my $pid = fork()) {
parent($pid);
}
else {
child();
}


sub child {
close PREAD; # child - close parent end of pipe
print "child: id $$ dad $parent_pid\n";

print "child: start of proccess\n";
syswrite CWRITE, "0\n";
for (1..9) {print "child zZZZzzz\n";sleep 1;}
print "child is awaking\n";
syswrite CWRITE, "$_\n" for 1 .. 10;
print "child ending\n";
exit;
} # sub child


sub parent {
my ($pid) = @_;
close CWRITE; # parent - close child end of pipes
print "parent id $$ child $pid\n";

my @erg;
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm 15;
while (waitpid($pid, WNOHANG) == 0) {
print "parent: kind lebt noch\n";
sleep 1;
}
alarm 0;
};
alarm 0; # Ist das wirklich notwendig?
if ($@) {
die "other error : $@"
unless $@ eq "alarm\n"; # propagate unexpected errors
warn "parent : child got time out!\n";
# ... Maßnahmen ergreifen um klarzumachen, dass die Verarbeitung
# unvollständig ist ...
}
else {
chomp(@erg = <PREAD>);
print "parent got ", join(' - ', @erg), "\n";
print "parent : ", (eof(PREAD)?'ENDE':'keine Ende'), "\n";
}
kill TERM => $pid; # Kind abschießen (in beiden Fällen, einmal als Zombie,
# einmal läuft es noch und verbraucht Rechenzeit)
} # sub parent


Die Frage ist, ob dieses zweite alarm 0 wirklich notwendig ist. Denn wird der eval-Block verlassen, so wurde entweder der Alarm schon ausgelöst oder wieder deaktiviert.


Edit 3: Mein Hauptproblem konnte ich überraschend durch eine winzige Änderung lösen, das Problem war durch aus Versehen hereingerutschte mehrfache Leerzeichen verursacht worden.

Trotzdem bin ich an einer generellen Zeitüberwachung interessiert, und falls sich da noch etwas mit den opcodes oder wie auch immer erreichen lässt, fände ich das sehr erfreulich.\n\n

<!--EDIT|Crian|1114588058-->
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
ptk
 2005-04-27 12:13
#54187 #54187
User since
2003-11-28
3645 Artikel
ModeratorIn
[default_avatar]
[quote=Crian,27.04.2005, 09:34]Kann man das auch auf den Block begrenzt festsetzen, in dem diese Evals verwendet werden, oder gilt das dann für die ganze Datei (wegen dem use)?

Edit: Hmmm die anderen Dinge mit use strict "blablubb" gelten ja auch nur lokal...[/quote]
Theoretisch sollten alle Pragmas nur im jeweiligen Scope gelten. Die SYNOPSIS der re-Manpage sieht auch so aus, als ob das auch fuer "use re" gilt.

EDIT: In perlmodlib, Abschnitt Pragmatic Modules steht mehr zu Pragamas und Scopes.\n\n

<!--EDIT|ptk|1114590812-->
Crian
 2005-04-27 16:16
#54188 #54188
User since
2003-08-04
5873 Artikel
ModeratorIn
[Homepage]
user image
Ich hab jetzt eine Client-Server-Lösung gebastelt: Der Server führt die Tests der regulären Ausdrücke durch und forkt sich dabei, das Hauptprogramm schickt seine Anfragen an diesen Server. Der Server überwacht sein Kind, das den RE bearbeitet und bricht es ggf. ab.

Das klappt auch super, nur leider bekomme ich eine Verlangsamung um ca. Faktor 10 durch die Kommunikation. Schade eigentlich...
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
Crian
 2005-04-27 16:38
#54189 #54189
User since
2003-08-04
5873 Artikel
ModeratorIn
[Homepage]
user image
Ich poste mal das Programm, auch wenn ich es wohl nicht anwenden werde. Aber vielleicht stecken ja noch dumme Fehler drin.

Server:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/perl
use strict;
use warnings;

use IO::Socket;
use File::Basename;
use POSIX ":sys_wait_h";

use Communication;

my $Debug = 0;

my $server_port = 4714;

my $server = IO::Socket::INET->new(
LocalPort => $server_port,
Type => SOCK_STREAM,
Reuse => 1,
Listen => 10,
) or die "Kann keinen TCP-Server an Port $server_port sein: $@\n";
print "Server ", basename($0), " is starting on port $server_port ...\n";

main();
close($server);
exit;


sub main {
CLIENT: while (my $client = $server->accept()) {
pipe PREAD, CWRITE; # child -> parent
chomp(my $input = <$client>);
print "Client sagt '$input'\n" if $Debug;

unless ($input eq Communication::RE_REQUEST) {
print $client Communication::DECLINE, "\n";
}
else {
print $client Communication::ACCEPT, "\n";
my $timeout = <$client>;
unless (defined $timeout) {
warn "Client schickte falsche Daten, Abbruch!\n";
next CLIENT;
}
my $txt = <$client>;
unless (defined $txt) {
warn "Client schickte falsche Daten, Abbruch!\n";
next CLIENT;
}
my $re = <$client>;
unless (defined $re) {
warn "Client schickte falsche Daten, Abbruch!\n";
next CLIENT;
}
chomp ($timeout, $txt, $re);
unless ($timeout =~ /^\d+$/) {
warn "Timeoutwert '$timeout' ist keine natürliche Zahl\n";
next CLIENT;
}
print "Client sagt timeout = '$timeout'\n",
"Client sagt txt = '$txt'\n",
"Client sagt re = '$re'\n" if $Debug;
print '.';
if (my $pid = fork()) {
parent($pid, $client, $timeout);
}
else {
child($txt, $re);
}

}
}
} # sub main


sub child {
my ($txt, $re) = @_;

$server->shutdown(0); # nur im Kind schließen

close PREAD; # child - close parent end of pipe
#print "child: id $$ dad $parent_pid\n";

print "child: start of proccess\n" if $Debug;
use re 'eval';
if (my @all = $txt =~ /$re/) {
print CWRITE "$_\n" for $`, $&, $', @all;
}
else {
print CWRITE "-1\n";
}
print "child: end of proccess\n" if $Debug;
exit;
} # sub child


sub parent {
my ($pid, $client, $timeout) = @_;
close CWRITE; # parent - close child end of pipes
print "parent id $$ child $pid\n" if $Debug;

my @erg;
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
while (waitpid($pid, WNOHANG) == 0) {
#print "parent: kind lebt noch\n";
#sleep 1;
}
alarm 0;
};
alarm 0; # Ist das wirklich notwendig?
if ($@) {
die "other error : $@"
unless $@ eq "alarm\n"; # propagate unexpected errors
warn "parent : child got time out!\n" if $Debug;
print $client Communication::TIME_OUT, "\n";
}
else {
chomp(@erg = <PREAD>);
print "parent got ", join(' - ', @erg), "\n" if $Debug;
if (@erg == 1 and $erg[0] eq -1) {
print $client Communication::NO_MATCH, "\n";
}
else {
print $client "$_\n" for @erg;
}
}
#kill TERM => $pid; # Kind abschießen (in beiden Fällen, einmal als Zombie,
# einmal läuft es noch und verbraucht Rechenzeit)
} # sub parent


Client:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/perl
use strict;
use warnings;

use IO::Socket;
use File::Basename;

use Communication;


do_the_re(15, 'a b c d', '\s*(b)\s*(c)\s*');
do_the_re( 5, 'a b c d', '\s*(b)\s*(c)\s*');
do_the_re( 5, 'a b c d', '\s*(e)\s*');
do_the_re( 2, 'a b c d', '(?{sleep 3})');


sub do_the_re {
my ($timeout, $txt, $re) = @_;

my $remote_host = 'localhost';
my $remote_port = 4714;

my $socket = IO::Socket::INET->new(
PeerAddr => $remote_host,
PeerPort => $remote_port,
Proto => 'tcp',
Type => SOCK_STREAM,
) or die "Konnte Verbindung zu $remote_host:$remote_port nicht herstellen: $@\n";

print "Client ", basename($0), " is starting, connected to $remote_host:$remote_port ...\n";

print $socket Communication::RE_REQUEST, "\n";

chomp(my $antwort = <$socket>);
die "no server-accept\n" unless $antwort eq Communication::ACCEPT;

print $socket "$timeout\n$txt\n$re\n";

chomp(my @erg = <$socket>);
if (@erg == 1 and $erg[0] eq Communication::TIME_OUT) {
print "ABBRUCH WEGEN ZEITUEBERSCHREITUNG\n";
}
elsif (@erg == 1 and $erg[0] eq Communication::NO_MATCH) {
print "KEIN MATCH\n";
}
else {
print "Ergbenis: [", join('], [', @erg), "]\n";
}
}


und Communication.pm:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;

package Communication;

#------------------------------------------------------------------------------
# Von Exporter erben:
#------------------------------------------------------------------------------
use base qw/Exporter/;
our @EXPORT = qw//;
our @EXPORT_OK = qw/RE_REQUEST ACCEPT DECLINE TIME_OUT NO_MATCH/;
our %EXPORT_TAGS = (all => [qw/RE_REQUEST ACCEPT DECLINE TIME_OUT NO_MATCH/]);

#------------------------------------------------------------------------------
# Konstanten:
#------------------------------------------------------------------------------
use constant RE_REQUEST => 33001;
use constant ACCEPT => 33002;
use constant DECLINE => 33003;
use constant TIME_OUT => 33004;
use constant NO_MATCH => 33005;


1;


Testlauf:

Code: (dl )
1
2
3
4
5
6
7
8
9
C:\Daten\perl\socket>c4.pl
Client c4.pl is starting, connected to localhost:4714 ...
Ergbenis: [a], [ b c ], [d], [b], [c]
Client c4.pl is starting, connected to localhost:4714 ...
Ergbenis: [a], [ b c ], [d], [b], [c]
Client c4.pl is starting, connected to localhost:4714 ...
KEIN MATCH
Client c4.pl is starting, connected to localhost:4714 ...
ABBRUCH WEGEN ZEITUEBERSCHREITUNG
s--Pevna-;s.([a-z]).chr((ord($1)-84)%26+97).gee; s^([A-Z])^chr((ord($1)-52)%26+65)^gee;print;

use strict; use warnings; Link zu meiner Perlseite
sri
 2005-04-27 17:03
#54190 #54190
User since
2004-01-29
828 Artikel
BenutzerIn
[Homepage] [default_avatar]
Mit POE waer das einfacher... ;)
<< |< 1 2 3 4 5 >| >> 41 Einträge, 5 Seiten



View all threads created 2005-04-25 13:24.