【Perl】ファイルロック(排他処理)について語ろう
■ このスレッドは過去ログ倉庫に格納されています
0001nobodyさん
02/06/23 10:18ID:eY2l+Gw10470nobodyさん
03/08/09 22:15ID:???use IO::File::fock;
my $fh = new IO::File::fock('file','>>');
0471山崎 渉
03/08/15 22:42ID:???│ ^ ^ │<これからも僕を応援して下さいね(^^)。
⊂| |つ
(_)(_) 山崎パン
0472nobodyさん
03/08/24 11:20ID:???追記の場合って、どうするのが一番確実で負荷がかかんないんでしょうか?
僕は、データを編集する場合と同じように、以下のようにしちゃってますが。use Fcntl qw(:flock);
#万が一リネーム失敗したときのために、ユニークなファイル名にしておく
$tmpfile = "$datafile".".$$.". time() .".csv";
#ロックファイルを作成する(★注:ロックファイルは、各CSVごとにユニークに)
open (LOCKF, ">$datafile"."_lockf") or die("cannot open:$!");
flock (LOCKF, LOCK_EX); #ロックファイルをflockする
open(IN, "< $datafile") or die("cannot open:$!"); # 読みのみモードで開く
open(TMP,"> $tmpfile"); #テンポラリファイルを作成
while ($line = <IN>){
$line .= <IN> while ($line =~ tr/"// % 2 and !eof(IN));
$line =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
@values = map {/^"(.*)"$/s ? scalar($_ = $1, s/""/"/g, $_) : $_}
($line =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
#必要なものだけをEUCにして、出力時にSJISにする
foreach $value (@values){
&jcode::convert(\$value, "euc");
};
#CSV形式に変換
$newline = join ',', map {(s/"/""/g or /[\r\n,]/) ? qq("$_") : $_} @values;
print TMP "$newline\n" ;#テンポラリファイルに1レコード書き込み
}
print TMP "新しい行" . "\n";
close TMP;
close IN;
unlink $datafile;
rename ($tmpfile, $datafile) or die ("cannot rename : $!");
close LOCKF;
0473472
03/08/24 11:25ID:xfHM1yWk訂正。
↓これでどうでしょか?
use Fcntl qw(:flock);
#万が一リネーム失敗したときのために、ユニークなファイル名にしておく
$tmpfile = "$datafile".".$$.". time() .".csv";
#ロックファイルを作成する(★注:ロックファイルは、各CSVごとにユニークに)
open (LOCKF, ">$datafile"."_lockf") or die("cannot open:$!");
flock (LOCKF, LOCK_EX); #ロックファイルをflockする
open(IN, "< $datafile") or die("cannot open:$!"); # 読みのみモードで開く
open(TMP,"> $tmpfile"); #テンポラリファイルを作成
while ($line = <IN>){
print TMP $line ;
}
print TMP "新しい行" . "\n";
close TMP;
close IN;
unlink $datafile;
rename ($tmpfile, $datafile) or die ("cannot rename : $!");
close LOCKF;
0474472
03/08/24 11:57ID:xfHM1yWk単純に、
use Fcntl qw(:flock);
open(OUT, "+< $datafile");
flock (LOCKF, LOCK_EX);
truncate(OUT, 0); # ファイルサイズを0バイトにする
seek(OUT, 0, 0); # ファイルポインタを先頭にセット
print OUT "新しい行" . "\n";; # 書き込む
close(OUT); # closeすれば自動でロック解除
・・・・どっちがいいのでしょうか・・・?
0475nobodyさん
03/08/24 12:53ID:???追記なら
use Fcntl qw(:flock);
open(OUT, ">>$datafile");
flock (OUT, LOCK_EX);
seek(OUT, 0, 2); # ファイルポインタを最後尾にセット(念のため)
print OUT "新しい行" . "\n";; # 書き込む
close(OUT); # closeすれば自動でロック解除
--
# 私はこんな感じ(Win不可)
use FileHnadle;
my $fh = new FileHandle($datafile, O_WRONLY|O_CREAT|O_APPEND|O_EXLOCK) or die;
print $fh '新しい行', "\n";
close($fh);
IO::Fileでもいいんだけど、趣味でFileHandle使ってます。
0476nobodyさん
03/08/27 11:26ID:???open(LOG, ">$logfile") or &error("Open Error : $logfile");
flock(LOG, 2);
seek(LOG, 0, 2);
my(@lines) = <LOG>;
(if($agent =~ /MSIE 3/i) { $agent = 'MSIE 3'; }のような置換とか)
while($max-1 < @lines) { pop(@lines); }
unshift(@lines, "$agent<>$os<>$host<>$referer<>$hour<>$doc_uri<>\n");
unshift(@lines, "$addr\n");
print LOG @lines;
close(LOG);
こうすると書き込み時にログが一旦全部消えてしまい、
取得した最後のログ一行(先頭行に最終アクセスIPがあるので正確には二行)のみになってしまいます。
何処が悪いのでしょうか…
因みに元スクリプトではログを配列に読み込んだ時点で一旦閉じ、
処理後もう一度開いて書き込みという風になってたのですが、
これだと処理中に書き込まれたら駄目そうな気がしたので上のように書き換えたのです。
0477nobodyさん
03/08/27 19:09ID:???lockどうこうより、while がおかしいのでは?
$max-1 < @lines がどういう比較かわからんけど、自分で
pop(@lines);して削除しちゃってるじゃないの
常にwhileが真で全行削除になってその後unshiftで2行追加してるだけでは?
0478nobodyさん
03/08/27 21:08ID:???'>'でオープンすると?
・ファイルの中身を切り詰める。
・書き込み専用でオープンする(ファイルが無ければ作成)。
>seek(LOG, 0, 2);
この意味は?
・フィル最後尾にファイルハンドルを移動する。
さすがに読めません。
0479nobodyさん
03/08/27 21:09ID:???flock(LOG, 2);
my (@lines) = <LOG>;
(ロックスレなので、、略)
seek(LOG, 0, 0);
truncate(LOG, 0);
print LOG @lines;
close(LOG);
上の書き方では、seekとtruncateの間で終了させられると、
ファイルの中身が失う危険性があるから、
seek(LOG, 0, 0);
print LOG @lines;
truncate(LOG, ftell(LOG));
close(LOG);
と書くといいらしいです。(実際はどうなんでしょ?)
http://www.kt.rim.or.jp/%7ekbk/perl5.005/perlfaq5.html#How_come_when_I_open_the_file_re
http://www.kt.rim.or.jp/%7ekbk/perl5.005/perlfaq5.html#I_still_don_t_get_locking_I_jus
クックブック 7.11
ttp://homepage1.nifty.com/glass/tom_neko/web/web_04.html
0480479
03/08/27 22:53ID:???間違いでした。以下の通り。
truncate(LOG, tell(LOG));
0481nobodyさん
03/08/28 11:06ID:???だめなの?
0482nobodyさん
03/08/31 01:47ID:3rubdTEFuse DB_File;
use Fcntl;
open DAT,"lockfile";
flock(DAT,LOCK_EX);
tie(%hash,'DB_File',"dbfile",O_RDWR|O_CREAT,0666
---DBM代入処理
としてロックしてました。
lockfileというファイルへの排他ロックを取得しているのは
わかるんだけど、それがどうしてDBMのdbfileへのロックと
なるのかわからん。
0483nobodyさん
03/08/31 04:22ID:???「dbfileを使うプロセスは、かならずdbfileにアクセスする前にlockfileをロックする」
という紳士協定があればうまく行く。
dbfileにアクセスするのが>>482のスクリプトだけの場合も然り。
それでもなぜか分からないなら良く考えてみ。
0484nobodyさん
03/08/31 20:24ID:???ブロッキングを理解すると分かると思います。
flockでロックされると、LOCK_NBを指定していない限り、
排他される側はflock関数の場所で処理を止められます。
0485482
03/08/31 22:31ID:3rubdTEFflockをLOCK_SHでした場合は%hashへの代入はしない。
上を全てのdbfileを使う処理でする。
この順番を守ればflockの段階でブロックしてtieへ進まない。
やっと、すっきりした。
>>483.484さんありがとー
0486nobodyさん
03/08/31 23:56ID:???書き込みできる状態でファイルをオープンしないとロックしない
OSがあるそうです。ご注意あれ。
参考:
同じ(ような)処理をするモジュール。
http://search.cpan.org/author/DHARRIS/DB_File-Lock-0.05/Lock.pm
0487482
03/09/01 01:40ID:???open LOCK,'<lockfile';
ってするてことですね。気をつけます。
このモジュール結構便利ですね。
ありがとございます。
perlの面白みにはまっっていく・・土日外にでてねえ・・
0488nobodyさん
03/09/01 19:13ID:???SYNCがかかったらSHにもどしちゃダメ?
LOCK_EXの時間は短い方がいいとおもうんだけど
0489nobodyさん
03/09/01 22:19ID:???魅力的ですが、共有ロック(LOCK_SH)をしているところに
排他ロック(LOCK_EX)をしようとすると、
ブロックされて待たされることになります。
書き込む事がほとんどないのであれば、
検討する価値はあるかも。
ロックしたら、操作、すぐ開放が基本です。
0490488
03/09/02 11:52ID:???> 排他ロック(LOCK_EX)をしようとすると、
> ブロックされて待たされることになります。
同じプロセスならLOCK_SHからLOCK_EXへ移行できちゃったんですが...
0491nobodyさん
03/09/02 20:13ID:???processA processB
| |
flock(F, LOCK_SH); |
| |
処理 flock(F, LOCK_EX);
| :
| : (ブロックされる)
| :
close(F); 処理 (ここで処理開始)
| |
exit; |
close(F);
|
exit;
0492490
03/09/04 22:14ID:???って事でhttp://www.perldoc.jp/docs/modules/DB_File-1.805/DB_File.poをみると・・・
まともにロックしたかったらBerkeleyDBを使えっと(´Д`;)
0493nobodyさん
03/09/06 01:39ID:A9Khhwgn漏れなら小規模の場合は消えるの覚悟で単純にflockだけ(面倒)。
中規模以上はDB立ててDBのLOCKで対処。
これじゃいかんのか?
0494nobodyさん
03/09/06 02:09ID:???0495nobodyさん
03/09/06 03:10ID:???flockで消えちゃうのは、何かお前のやり方に問題があるんだと思うぞ。
システム自体がflockの堅牢性に頼っていると言うのに。
0496nobodyさん
03/09/07 02:25ID:???0497nobodyさん
03/09/07 10:06ID:???限界って?
Windowsやover networkでうまく働かない以外に何か問題あったっけ?
だいたいflock使えねーとか言ってるヤツのコードみると書き方間違ってる
だけだったりするんだよな。
ごちゃごちゃ言う前にモジュール使えってのはまぁ同意だけど。
0498nobodyさん
03/09/07 14:44ID:AN0xO5Efありますよ。ご自分でお調べください。
0499nobodyさん
03/09/07 15:01ID:???あとは昔のOSでバッファをフラッシュしないくらいしか思いつかないなあ。
UN*X系のOSで別に正しく使えばファイルが消えちゃうとかはないでしょ?
0500nobodyさん
03/09/07 15:03ID:???0502nobodyさん
03/09/07 15:14ID:???0503496
03/09/07 19:04ID:???flockでデータベースを丸ごとロックするより、
他の方法を使ったほうが限界性能が上がると言いたかったのですが…。
別の話として。
>Windowsやover networkでうまく働かない以外に何か問題あったっけ?
ActivePerlの場合、WIN32APIのLockFileEXが使えれば、Windowsでも使えます。
そして、それはうまく働きます。
私はflock(2)の信頼性を否定しません。
とはいえ、perlのflockについてはperldocに例外事項が書かれています。
(498さんの言いたかったのはこれかな)
perlのflock関数 = flock(2)であるとは限りません。
参考:
perldoc -f flcok
perlソース pp_sync.c
ActivePerlソース win32.c
0504nobodyさん
03/09/07 22:04ID:???おいおい、何はぐらかしてんだ?
>Windowsやover networkでうまく働かない以外に何か問題あったっけ?
>他の方法を使ったほうが限界性能が上がると言いたかったのですが…。
他の方法とは?
0505nobodyさん
03/09/07 22:26ID:K46D6zau起動させればflockの信頼性が判るよ。いくつかはロックかからずに
スルーされるからさ。
後はロックの残骸による次プロセス(同一ロック)の停止かな。
遠回りしないと解決出来ないのは面倒。
漏れが間違っているかもしれんが、同時に数千〜数万単位でロックを
必要とする環境に置いて上記の理由からflockは使いたくないです。
よってほとんどDBのロックに任せっきり。DBだと単純なa=a+1の場合だと
DB自体が保証してるんでロック不必要だし。
間違っていたら本当にスマソ。業務でロックを故意に壊しにいった結果
こうなったので。
0506nobodyさん
03/09/07 22:35ID:???ソースとdfの結果みせてみな。
そういうケースの殆どはファイルシステムがNFSだった、って
オチな訳だが。
もし本当にflock(2)に信頼性がないなら、sendmailがmailboxファイルを消しまくる
現象が世界中で起きてるはずだが。
> 後はロックの残骸による次プロセス(同一ロック)の停止かな。
・・・は?
> 間違っていたら本当にスマソ。
多分間違ってると思う。
0507nobodyさん
03/09/07 23:19ID:???>いくつかはロックかからずにスルーされるからさ。
どうやって判断してんだ。テストコードがバグってんだろ。よくあることだ。
>ロックの残骸
って何?
>flockは使いたくないです。
>よってほとんどDBのロックに任せっきり
・・・
0508nobodyさん
03/09/07 23:54ID:???店に怒鳴り込む馬鹿親がいると聞きますが・・・。
この業界にも、道具を使いこなす努力をせずに、自分の無能を全部道具のせいにする
DQNが増えてきたってことですかね。
0509nobodyさん
03/09/08 10:14ID:???ハゲドウ
しかし、不良だらけの道具を平気で売ってる大企業とかも在るのが実情ではある。
M$とか。
0510496
03/09/08 13:21ID:???>>Windowsやover networkでうまく働かない以外に何か問題あったっけ?
perldoc -f flockから引用
>Calls flock(2), or an emulation of it, on FILEHANDLE. Returns
>true for success, false on failure. Produces a fatal error if
>used on a machine that doesn't implement flock(2), fcntl(2)
>locking, or lockf(3).
>On systems that support a real flock(), locks are inherited
>across fork() calls, whereas those that must resort to the more
>capricious fcntl() function lose the locks, making it harder to
>write servers.
というように、いい加減なfnctl(2)によりエミュレーションされた場合は
サーバーの書き込みが激しい場合ロックが失われると書いてあります。
>>他の方法を使ったほうが限界性能が上がると言いたかったのですが…。
>他の方法とは?
データベース側に専用のロック機構があれば、そちらにお任せしますが?
全部flockしますか?
0512nobodyさん
03/09/09 02:09ID:???んじゃ、perlにflock(2)の呼び出しが実装されてれば問題がないということでよろしい
でしょうか?
俺は思わず「fcntlがダメダメ -> POSIX準拠と呼べない -> UNIX系とも呼べない」
と思ってしまったが。
0513496
03/09/09 20:09ID:???flock(2)が機能して、正しく使えていれば問題ないかと。
私の知る限りでは、fcntl(2)でも十分信頼性はあると思いますけどね。
0514nobodyさん
03/09/12 14:11ID:e1VX9Ox6flock(2)はover networkじゃなく、正しく使えば基本的に堅牢。
Perlのflockはコンパイルされた環境依存、ってとこか。
fcntlでもちゃんとPOSIXに準拠してれば問題ないはず。
その腐ったfcntlを実装してるOSが何なのか知りたい。
Larryの事だから、誰も使わないようなかなりマイナーな物を
想定しているような気がするが。
しかしfcntlは堅牢でないからDBを使う、といってる人がいたが、
じゃぁそのDBはどうやってファイルをロックしてるんだろう?
0515nobodyさん
03/09/12 19:43ID:???と言い切ってみるテスト…。
0516nobodyさん
03/09/13 00:45ID:???0518nobodyさん
03/09/14 21:13ID:???PostgreSQLだったらセマフォ。
>>517
http://qmail.jp/mta/spool.htmlでも触れられているが、
NFSでlockdが腐る現象がまれに発生する事を実際に経験している。
(俺は信者じゃない)
0519518
03/09/14 21:24ID:???これ、やっぱ嘘かも。
しかし、排他制御のプリミティブはfcntlやflockだけではない。
0520nobodyさん
03/09/15 06:39ID:???0521nobodyさん
03/09/15 06:42ID:???0522nobodyさん
03/09/15 07:54ID:???0523nobodyさん
03/09/15 10:38ID:???0524nobodyさん
03/09/15 17:19ID:???結構でてくるのですが、どれかお勧めはありますか?
それとも毎回自分で処理を書いたほうがいいのでしょうか?
0525nobodyさん
03/09/15 18:11ID:???「flock は腐ってる。 DB 使え」という答えしか返ってこない。
0527nobodyさん
03/09/15 23:44ID:???そんなこと書いてるのは約一名だろ。
>>524
perl -MConfig -e 'print $Config{d_flock}."\n"'
これ実行して「define」と出ればflock(2)が実装されてるからまず大丈夫。
ネットワーク越しでなければラクダ本に書いてあるflockのサンプルで充分。
0528nobodyさん
03/09/16 00:04ID:???何人か見た事あるので充分なのかどうかは微妙とか言ってみるテスツ。
0529nobodyさん
03/09/16 01:04ID:???0530nobodyさん
03/09/16 02:04ID:???別にseekやってもオーバーヘッド以外の害はないんだし、害がある場合もレアケースながら
存在するので、的確かどうかはともかく、少なくとも「充分」ではあるのでは?
0531nobodyさん
03/09/27 12:58ID:???mkdirで新規ディレクトリを作ったうえで、ロック成立条件を満たした場合にロック完了とする。
ディレクトリ名にタイムアウト時刻と共有/排他ロックの種別を埋め込む。
共有ロック時はディレクトリ作成後に、親ディレクトリのファイルリストを取得して、
自分の作ったディレクトリよりも前に排他ロックディレクトリが作成されていなければ、ロック完了。
排他ロック時も同様に、自分の作ったディレクトリよりも前にディレクトリが作成されていなければ、ロック完了。
もちろん、ファイルリスト取得時にタイムアウト時刻を過ぎたディレクトリは消去する。
環境依存せずに、read-write lock patternをperlで実装するとこうなるね。
0532nobodyさん
03/09/29 13:16ID:???flockサポートしてない環境ってそんなに一杯あるんですか?
windows 9xでサーバ立てる香具師はほっといたとして、、、
nkfのlock機構って精度低いんですか?
0533nobodyさん
03/09/30 04:58ID:???nkf の lock 機構ってのは聞いたことないんだが、
・・・っていうか、一体いつまでおんなじような議論を堂々巡りで続ける気なんですか?!?!
0535532
03/09/30 11:52ID:???0539531
03/10/02 09:02ID:???やっぱうちのサイトですか(w
で、今改良版を作ってるのですが、タイムアウト時刻をディレクトリ名にすると、同時にタイムアウト時刻が
違うロックを作ると誤動作するな。
だから、現在時刻をディレクトリ名にして、ディレクトリの作成時刻をタイムアウト時刻に変更すると言う
アクロバティックな操作が必要かもね。
改良版出来上がったら公開しますか?(y/n)
0540nobodyさん
03/10/02 16:33ID:???自作CGIを評価するスレ
http://pc2.2ch.net/test/read.cgi/php/1049514428/470
何これ。恥ずかしがりやさんなのかな。
0542nobodyさん
03/10/03 18:38ID:???いやいや何か勘違いしてない?
ファイルロックの話なんだからこのスレでいいだろう。
俺の言いたかったのは>>539の最後に(y/n)があって、
何故かhttp://pc2.2ch.net/test/read.cgi/php/1049514428/470 で
yと答えてたので報告したまでです。
0543nobodyさん
03/10/03 18:51ID:???これって安全そうでよさげな気はするんだけど、
ちょっと思ったんだけど、ものすごくアクセス多い場合に、
ディレクトリ作成〜ファイルリスト検査のタイムラグのせいで、
いつまでたってもだれもロックを取得できない、ってな状況になりそうな気がする。
0544531
03/10/05 10:57ID:???今度の奴は、ロックする側のプロセスがタイムアウト時間を個別に設定できます。
http://do.sakura.ne.jp/~junkroom/cgi-bin/megabbs/lounge/index.html
>>543
確かにファイルリスト検査に時間がかかるが、ならばどうしろと?
0545nobodyさん
03/10/06 12:54ID:???いや、どうすればいいか私にも思い付きません。すいません。
単にデメリットを挙げてみただけで、
だからといってその方法が使えねーとかいう気も毛頭ありません。
0546531
03/10/06 22:56ID:???さらに一念発起してrename方式に変更してロック処理にかかる負荷を低減してみました。
rename方式でテストしたらロックがぶっ壊れるなー、とか思ってたら
ロック検証用のルーチンの書き方に問題があった罠。
同じファイルを読み込みと書き込みで2回オープンして、しかもバッファ処理をカットしてなかったので、
スクリプト上では書き込んだつもりでもファイルシステム上では「まだ」書き込まれてない場合があることに
ようやく気がついた。
教訓。
ロック処理が正しくても、ファイル入出力の部分の書き方によっては思わぬファイル破損がありうる。
0547531
03/10/07 18:17ID:???やっぱりread-write lockってむづかしいでつね。
rename方式だと、共有ロックをかけているプロセス数が分からないといつロック解除していいかわからない。
ロックプロセス数を安全に記録するために、内部で排他ロックをかけなければいけない罠w
これでどのくらいロック処理で負荷がかかるか、だな。
0548nobodyさん
03/10/29 05:07ID:???例えば掲示板で最初のページだけ静的なファイルにしたいという場合、
ログとその最初のページの両方を同時にロックしないといけないので。
FLOCKのディレクトリ版とかあれば便利なのですが…。
0549nobodyさん
03/10/29 06:01ID:???ロック開始と解除の間で必要なファイルを扱えばいいだけ
ロックの仕組みを根本的に理解していないんじゃないか?
もうちょっと勉強せい
0550nobodyさん
03/10/29 08:13ID:???http://pc2.2ch.net/test/read.cgi/php/1024795138/
0551nobodyさん
03/11/03 16:44ID:???ttp://www98.sakura.ne.jp/~jun/perl/flock.html#02
0552nobodyさん
03/11/05 02:38ID:???flockってロックしたファイルが閉じるまでに開いた他のファイルまで
全部ロックしちゃうの?
0555nobodyさん
03/11/05 14:16ID:???読んだけど。
かいつまんで書くと
lockfileを開く
lockfileをロック
count.txtを開く
上書き
count.txtを閉じる
addr.txtを開く
追記
addr.txtを閉じる
lockfileをアンロック
だよね。
count.txtとaddr.txtには直接ロックかけてなくて
lockfileをロックすることによりこの二つのファイルもロックされてるらしいから
>551みたいに言った。
0556nobodyさん
03/11/05 14:16ID:???0557nobodyさん
03/11/05 14:28ID:???よく考えたらlockfileがロックされてるときはそこから先に進まないから大丈夫なのか。
0558nobodyさん
03/11/08 02:04ID:???どっちかのファイルにロックかけて最後まで開いてるままで十分だけどな。
0560nobodyさん
03/11/08 13:37ID:???0562nobodyさん
03/11/08 17:35ID:???0563nobodyさん
03/11/09 03:10ID:???レアケースだろうけど
似たような事して壊れたことあるよ
無茶苦茶って事では無いだろうが、
そのやり方だと同時オープンで
2人アクセスしてきたのに1しかカウントアップしないって
ケースがタイミング良ければ発生する程度かな
0567nobodyさん
03/11/09 18:12ID:???単に読み込みをブロックしてなかっただけってオチなんじゃ。
それか二つのファイルでファイルハンドルを同じにしちゃったとか…。
>564
open(COUNT, "+<$cfile");
flock(COUNT, 2);
(中略)
open(ADDR, ">>$afile");
(中略)
close(ADDR);
couse(COUNT);
0568nobodyさん
03/11/09 18:18ID:???複数のスクリプトが複数の同じファイルを読み書きする場合は
ロック用ファイルを一つ用意する方が管理が楽なのは当たり前。
でもカウンターのようにファイルが単一のスクリプトからしか読み書きされない場合は
どちらか先に読む方のファイルをロックしておけば十分。
■ このスレッドは過去ログ倉庫に格納されています