壊れないテキストカウンターを作るには??

[上に] [前に] [次に]
Chopstick [E-Mail] [HomePage] 2000/03/02(木) 03:07:36
ちょっと長いんですがすみません。。。
テキストで表示される簡単なカウンタを作ってるのですが、
なかなかうまくいきません。。
最初、本か何かの付録に付いていたスクリプトを書きました。

open(IN,"count.dat");
@lines=<IN>;
close(IN);
$count=$lines[0];
$count=$count+1;

$str=sprintf("%05d",$count); #5桁に設定
for($i=0;$i<5;$i++){
$n=substr($str,$i,1);
print "$n";
}

open(OUT,">count.dat");
flock(OUT,2);
print OUT "$count";
flock(OUT,8);
close(OUT);
(! /usr/local/bin/perl省略)
で、出力する場所がshtmlでなく、htmlだったのです。
で、一応カウンターが6000くらいまで上がったんですが、
そっからいきなり0に戻ってしまいました・・・(悲)

で、こりずに今度はSSIとかいう言語で書いてみました。
#!/usr/local/bin/perl

$counterfile="./counter1.dat";
open(FILE, "+<$counterfile");
#flock(FILE,2);

$count=<FILE>;
chop $count;
$count++;

seek(FILE,0,0);
print FILE"$count\n";
printf("%5d",$count);

#flock(FILE,8);
close(FILE);

exit;
これもshtmlではなく、htmlに出力しました。
で、元々6000ヒットまでいってたので初期値を6000
としたんですが、600(つまり3桁)にしか出ません・・・。
これってなぜかわかりますか??(^_^;
CGI初心者なのでどこをどういじればいいのかわからないのです。
で、しかもCGIの時よりもSSIでやった時のほうがなんか
ページが重くなってる感じがします。。
SSIは比較的軽いって聞いたのですがおかしいです・・・(涙)
普通のテキストでいいのですが、どなたか極極初歩的で簡単
なテキストだけで表されるスクリプトを配布してるところ
知りませんか??
Web裏技さんや、KentWebさんも見たんですが、いろいろな
機能がついてて(まぁそれはそれでありがたいのですが・・)、
なかなか思うのがありません。。。
ホントお願いです。。。
どなたか先に述べた問題の解決法、もしくはスクリプト配布の
サイトを知ってる方がおられましたら教えてください
お願いしますm(__)m
最後まで読んでくれてありがとうございました。

tetti [E-Mail] 2000/03/02(木) 03:45:40
ボクはCGI書けないのでコードのことはアドバイスできませんが、下記サイトのCGI入門にあるテキストカウンタなんかシンプルでよいかも〜。

http://www.hyuki.com/

seltza 2000/03/02(木) 10:11:10
細かい指摘(つっこみ)は他の方に任せるとして。。
<「SSI」の使い方や結局のところファイルロックについて。

> で、元々6000ヒットまでいってたので初期値を6000
> としたんですが、600(つまり3桁)にしか出ません・・・。

元々のデータを設定されたとき、最後に改行は含まれていますか?
一っ番最初に最後の0が削られちゃってるのではないでしょうか?

> chop $count;

chomp $count;
に変更することでも対処可能かと。

ラウォッチ 2000/03/02(木) 10:50:02
>結局のところファイルロックについて。
以下が参考になります。
http://www.tohoho-web.com/wwwperl2.htm#flock

とにかく、カウンタ値の読み込みから書き込みまでロックしないと..。

EMI 2000/03/02(木) 12:25:23
じゃあつっこみ。

>で、こりずに今度はSSIとかいう言語で書いてみました。
SSIは、言語ではありません。
後、これの下に書いてあるスクリプトはどう見てもCGIですけど。

ところで、私も似たようなテキストカウンター使ってますけど、壊れるときは壊れます。
仕方ないので、ログを取りつつ壊れたらログを参考にその都度直してます。

#ただ、ロックのしかたがなんか間違ってる気がしてきた‥‥‥
#家帰ってから、チェックしてみよう。

真海 2000/03/02(木) 12:35:55
open(FILE, "+< file.txt");
flock(FILE, 2);
$cnt = <FILE>;
seek(FILE, 0,0);
print FILE ++$cnt;
close(FILE);

これで4年間、2000000Hit以上ですが、一度も壊れたことがありません。

EMI 2000/03/02(木) 12:48:55
なるほど、試してみます。

seltza 2000/03/02(木) 14:02:06
(笑) つっこみ合戦。
#けんか売ってるわけじゃありません〜

> 後、これの下に書いてあるスクリプトはどう見てもCGIですけど。
有効なHTTPヘッダを返していないので、CGIとしては利用できないですね。
言うとすれば「SSIとして利用するための(カウンタ)perlスクリプト」ってところ?

> 4年間、2000000Hit以上
平均、時間あたりで60Hitくらいかな? これくらいであれば flockによるロックでも問題ないということですね。

ロックにこだわるよりは、壊れたときにいかにして何事もなかったかのように元に戻せるかどうか、ということもまた別なポイントですね。(^-^

ではでは。
#けっきょく何の解決にも貢献していないこと、お詫びいたします。m(_ _)m

Chopstick [E-Mail] [HomePage] 2000/03/02(木) 15:03:35
>真海さん
それではその記述をそのままコピペすればいいんですか?
open(FILE, "+< file.txt");
flock(FILE, 2);
$cnt = <FILE>;
seek(FILE, 0,0);
print FILE ++$cnt;
close(FILE);

>Seltzaさん
平均、時間あたりで60Hitくらいかな? これくらいであれば flockによるロックでも問題ないということですね。

ということはカウンタの壊れる度合いはヒット数にも比例すると
考えてよいのでしょうか??
私のサイトは最近人気が出てきまして、1日2000ヒットくらい
なのですが、そうなるとどうなんでしょう??

seltza 2000/03/02(木) 19:44:00
こんにちは。

> 1日2000ヒットくらい
〜100カウント/時間 くらい?

やはりどういったロック機構を採用されるかを考えるとき、「どれくらいの頻度でファイルに対する書き込みが発生するか」というのは大きな要素でしょう。その上で、制作者の考え方次第ですね。

flock をロックとして使うときに、あえて問題がある(それくらい頻繁に衝突が発生する恐れがあるとき。)とすれば、

(・win32(95/98)で使用できない)
・仕様上、オープン後にロックを行う。

この後者を問題としているはずです。(たぶん。。)

open(FILE, "+<$counterfile");
flock(FILE,2);

つまりこの2行の処理を移動する間に、別のプロセスでまったく同じようにファイルオープンされるという、もの凄く
アンラッキーな状況を想定した場合、単なるflockによるロックでは不十分、と言えるでしょう。

よくできた代替ロックはこのあたりのことまで吟味されるべきですが、その分コードがかさむのは必然です。

詰まるところは用途&制作者の考えによる、と。。

B-Cus 2000/03/02(木) 20:03:51
> open(FILE, "+<$counterfile");
> flock(FILE,2);
> つまりこの2行の処理を移動する間に、別のプロセスでまったく
> 同じようにファイルオープンされるという、もの凄くアンラッキー
> な状況を想定した場合、単なるflockによるロックでは不十分
open 時にクリアされるわけじゃないから
これでいいと思うんですけど、ダメなんですか?

seltza 2000/03/02(木) 20:09:50
説明不足でした?

Aという人がcgiを動作させます。
openしました。その後flockの処理に移る前に・・

Bという人がcgiを動作させました(させていました)
openを呼びました。

壊れません?

B-Cus 2000/03/02(木) 20:15:54
僕は壊れないと思います。壊れるんですか?

コウノトリ 2000/03/02(木) 20:18:10
>壊れません?
”+>”で開いているので壊れないはずです。
真海さんのスクリプトの場合、
seek(FILE, 0,0);
print FILE ++$cnt;
の間でプロセスが死んでも1回カウントされないだけですよね?
考えられるもっとも強力な方法だと思いますが・・・

コウノトリ 2000/03/02(木) 20:19:08
"+<"でした。

seltza 2000/03/02(木) 20:20:02
少なくとも書き込み時に衝突...
あ。オープンは書き込み前提。失敬。
>open(FILE, "+<$counterfile");

ではそろそろ私は退散いたします〜。ふぅ・

コウノトリ 2000/03/02(木) 20:27:14
>少なくとも書き込み時に衝突...
>あ。オープンは書き込み前提。失敬。
2プロセスが競合しても、どちらかの書き込みが行なわれる前に必ず一回はflockが実行されます。だから安全です。

Chopstick [E-Mail] 2000/03/02(木) 21:04:45
こんばんわ。みなさんいろいろなご意見ありがとうございます。
では結局今までの
open(FILE, "+<$counterfile");
#flock(FILE,2);
というところを#を取って
open(FILE, "+<$counterfile");
flock(FILE,2);
にすればカウンタは壊れないということですか?
(ファイルロック用のファイルなんて作らなくてもいいんですね?)
でもって、桁数が初期値3桁でしか設定できなかった部分は
chop $count;

chomp $count;
に直すということでいいんですね??
どこまでも初心者でホントすみません・・・(^_^;

コウノトリ 2000/03/02(木) 21:17:42
最も確実な方法は、初期値を<<改行無しで>>1行だけ書いたファイルを転送することです。

>(ファイルロック用のファイルなんて作らなくてもいいんですね?)
いりません。

B-Cus 2000/03/02(木) 22:27:39
ここ以外でも flock や symlink が信用できないという声を
聞きますけど、以下のようなテストプログラムを試してみて下さい。

これでうまくいくなら flock,symlink 自体には問題ないん
でしょう。それでも「うちのカウンタは実際に壊れたんだ」と
いうなら、それはシグナル処理をしていないからだと思う。

うちの apache ではたまに SIGPIPE 送ってきますし
(どのパイプが閉じてしまったのかは調べてません)、
ここで配布している CGI が SIGPIPE,SIGINT,SIGHUP,
SIGQUIT,SIGTERM をブロックしてるのも、シグナルを
送る WWW サーバがある、ということでしょうから。
# もちろん perl や OS のバグという可能性もあるけど。

もし下のテストプログラムで排他処理がうまくいってないなら、
NFS 環境下で flock を使おうとした、perl の flock(F,8) と
close のバグ、くらいしか思いつきませんが、少なくとも
うちの Solaris2.6 では NFS でもうまく flock できている
ように見えます。

% ./check-lock [mode] [loop] [maxchild]
   mode=symlink or flock
   loop=ループする回数 (デフォルトは1000)
   maxchild=同時に起動する子プロセスの数 (デフォルトは10)

例:
  % ./check-lock symlink 100 5
    symlink でロックして、100回カウントアップ。
    最大5個しか子プロセスを作らない。
  % ./check-lock flock 1000
    flock でロックして、1000回カウントアップ。
    最大10個 (デフォルト値) しか子プロセスを作らない。

なお、プロセスが大爆発して、マシンが落ちても責任は取りません。
共用サーバを使っているなら、重い時間帯は避けましょう。
無謀な子プロセス数を指定するのはやめましょう。言ってる
意味がよくわからないなら使わないで下さい。

こちらで試した限りでは FreeBSD,Solaris とも、
  % ./check-lock flock 10000 30
  % ./check-lock symlink 10000 30
でうまくいきました。ただし Solaris+flock で、壊れることは
なかったものの、たまにプロセスが行方不明に。10000 ループで
最終的なカウンタ値が 9997。3個のプロセスがどこいったのかは謎。
fork の失敗かなぁ。


#--------------------------------------------------
#!/usr/local/bin/perl
$mode = shift;

if ( $mode eq 'flock' ){
} elsif ( $mode eq 'symlink' ){
    $lockfile = "sym-lock";
    unlink($lockfile);
} else {
    die "You must specify mode `flock' or `symlink'\n";
}

$counter_file = "$mode.txt";
$countup_func = "countup_$mode";
open(F,">$counter_file");
print F "0\n";
close(F);

$loop = shift || 1000;        # カウントアップする数
$children_num = shift || 10;  # 同時に生成する子供の数
$now_children = 0;            # 現在の子供の数

for ( $x=0 ; $x<$loop ; $x++ ){
    if ( $pid = fork() ){  # 親
$now_children++;
    } else {               # 子
&$countup_func($x);
exit;
    }
    if ( $now_children > $children_num ){  # 制限以上のプロセスを作らない
wait;
$now_children--;
    }
}

while ($now_children){  # 最後に残った子供たちを待つ
    wait;
    $now_children--;
}
open(F,$counter_file);   # 結果表示
printf "Result=%s",scalar(<F>);
close(F);

exit;

sub countup_flock {   # flock を使ったカウントアップ
    ($child_number) = @_;
    open(F, "+< $counter_file");
    flock(F,2);
    $num=<F>;
    seek(F, 0, 0);
    printf F "%d\n",$num+1;
    close(F);
    printf "$mode: I'm ${child_number}th child. counter=%d\n",$num+1;
}

sub countup_symlink {   # symlink を使ったカウントアップ
    ($child_number) = @_;
    $lock_flg = 0;
    while (1){
$lock_flg = symlink("$$",$lockfile);
last if ( $lock_flg );
select(undef,undef,undef,0.1);  # 0.1 秒 sleep
    }
    open(F,"$counter_file");
    $num=<F>;
    close(F);

    open(F,">$counter_file");
    printf F "%d\n",$num+1;
    close(F);

    printf "$mode: I'm ${child_number}th child. counter=%d\n",$num+1;
    unlink($lockfile);
}

Chopstick [E-Mail] 2000/03/02(木) 22:39:28
[[解決]]
ご返答いただいた皆様方、大変ありがとうございましたm(__)m
それではここに書いてくださった意見を参考に何とか自分で
やってみます。ありがとうございました

ひとこと物申す 2000/03/02(木) 23:33:29
≫Chopstickさんへ
ホームページアドレスに
>http://www.wakusei.ne.jp/twn/まだ実験中・・・・
と書くのはいただけませんねぇ。ご自分のサイトのURLならともかく(略)
>・半角文字で入力してください。
漢字が読めなかったんでしょうか? それとも・・・

≫EMIさんへ
>EMI 2000/03/02(木) 12:48:55
>なるほど、試してみます。
次のスレッドを熟読してください。
>../199912/99120169.htm
最近、やたらと書きまくってますが、使えるあなたの意見は一握り(爆)

Maruru 2000/03/03(金) 00:58:20
うーーん、なんつうか、、、
>ひとこと物申すさん
Chopstickさんが説明をよく読んでなかった点はChopstick
さんが悪いような気もしますが、ちゃんと教えてくれてる
EMIさんにあのような発言するのはよくないと思います。
「ひとこと物申す」って言ってますが、これじゃぁひとことじゃ
ないし・・・
しかも
>使えるあなたの意見は一握り(爆)
ってなんですか・・・
あと最近やたらと書いてるとディスクが増えるようですが、
あなたの発言自体がディスクを重くしていることも事実です。
まぁならばこの発言も同じか・・・(^_^;

[上に] [前に] [次に]