STDINを読みたいのですが、5秒を超えてはいけません。それから、これまでに読んだデータを処理したいと思います。
select
まさにこれのために設計されているようです。入力があるかタイムアウトするまで待ちます。
入力がある場合にのみ非ブロックコンテンツを読み取る必要があります。
だから私はこれがうまくいくと思います:
#!/usr/bin/perl -w
use strict;
use Fcntl;
open(my $in, "<&", "STDIN") or die;
my $buf = "";
my $readsize = 10;
# Make $in non-blocking
fcntl($in, &F_SETFL, fcntl($in, &F_GETFL, 0) | &O_NONBLOCK);
while(not eof($in)) {
my $starttime = time();
my $rin = my $win = my $ein = '';
vec($rin, fileno($in), 1) = 1;
while(time() < $starttime + 5 and length $buf < $readsize) {
# Wait up to 5 seconds for input
select($rin, $win, $ein, $starttime + 5 - time());
# Read everything that is available in the inputbuffer
while(read($in,$buf,$readsize,length $buf)) {}
}
print "B:",$buf;
$buf = "";
}
次のように実行すると:
(echo block1;sleep 1; echo block1; sleep 6;echo block2)|
perl nonblockstdin.pl
これらのブロックは互いにマージされます。ブロック2は6秒後に開始されるため、2つのブロックでなければなりません。
私は何が間違っていましたか?
答え1
いくつかの問題:
- 設定は
O_NONBLOCK
ファイル記述子だけでなく、開いたファイルの説明にも影響します。たとえば、 を実行すると stdin が非ブロックになることがthat-script; cat
わかります。cat: -: Resource temporarily unavailable
cat
- 非ブロックI / Oを使用している場合、
read()
この時点で入力がなく、eofが設定されている場合、システムコールはEAGAINエラーを返します。呼び出しは、バッファリングされたI / Oの実行を意味しperl
ません。実際、非ブロックI/Oには使用できません。あなたの例では、最初のものはread、waited、2番目のものはreadで、両方のsを返します。eof()
read()
block1
eof()
select()
sleep 1
block1
read()
block1
たとえば、ここではI / Oブロックとタイムアウトを使用することをお勧めしますalarm()
。非ブロックI / Oを使用してselect()
代わりにeof()
使用する場合、終了時にフラグが事前に設定されている場合はそれをクリアする必要があります(stdinで設定することは、stdinが他の共有sysread()
プロセスと対話する可能性があるため、まだ悪い考えです)。read()
O_NONBLOCK
O_NONBLOCK
答え2
Stephenの提案を元にこれを思い出し、これまでは効果があるようです。
#!/usr/bin/perl -w
use strict;
my $timeout = 2;
my $buf = "";
my $blocksize = 30;
my $readsize;
my $nread;
my $buflen;
my $alarm;
my $eof;
open(my $in, "<&", "STDIN") or die;
do {
eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
# Add upto the next full block
$readsize = $blocksize - (length $buf) % $blocksize;
do {
$nread = sysread $in, $buf, $readsize, length $buf;
$readsize -= $nread;
} while($nread and $readsize);
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n"; # propagate unexpected errors
$alarm = 1;
} else {
$alarm = 0;
}
print "B:$buf<\n";
$buf = "";
$eof = not ($nread or $alarm);
} while(not $eof);