トップページ⇒php
981コメント337KB

【Perl】ファイルロック(排他処理)について語ろう

■ このスレッドは過去ログ倉庫に格納されています
0001nobodyさん02/06/23 10:18ID:eY2l+Gw1
どんな環境でも使えて、軽くて、頑丈なロックを考えようじゃありません
0167nobodyさん02/07/18 13:34ID:???
........吐くだけならロックしなくて良いじゃん
0168nobodyさん02/07/18 14:33ID:???
>>167
じゃあ、二つのプロセスが同時に同じファイルに対して吐きにいったら
どうなる?
0169nobodyさん02/07/18 14:56ID:???
open(OUT, "+< outfile.txt");
flock(OUT, 2);
seek(OUT, 0, 0);
print OUT "........";
truncate(OUT, tell(OUT));
close(OUT);

書き込みが終わってからtruncate()したほうが安全でちょっと速いみたい。

truncate(FILE, tell(FILE)) : 56 wallclock secs ( 8.21 usr + 39.26 sys = 47.47 CPU) @ 210.67/s (n=10000)
truncate(FILE, 0) 60 wallclock secs ( 8.43 usr + 42.72 sys = 51.15 CPU) @ 195.49/s (n=10000)
0170nobodyさん02/07/18 15:36ID:???
どうもならないよ、定数吐き出すだけでしょ?
正確にはsyswriteするか$|=1してバッファ使わない様にした方が良いけど
0171nobodyさん02/07/18 15:38ID:???
truncateしなくて良いじゃん変数じゃ無いのに
前提が変なのに何をどうしたいのやら
0172nobodyさん02/07/18 15:41ID:???
どこに定数って書いてるんだ?
0173fusianasan02/07/18 16:45ID:???
>>172
だれに言ってるんだ? わたしの記憶が確かなら>>170に定数って書いてるYO!
0174nobodyさん02/07/18 17:11ID:???
この棘々しい会話の流れ
( > v<)ノ  タマンネー♪
0175nobodyさん02/07/18 17:30ID:???
定数だろうが変数だろうが返り値だろうが関係ないわけですが
0176nobodyさん02/07/18 17:34ID:???
>>175
は? 何言いたいのおたく?
もうちょっと日本語の文章力付けなさいよ
0177nobodyさん02/07/18 17:42ID:???
みんなあまのじゃくさん♪
0178nobodyさん02/07/18 19:10ID:???
すんげー荒れてるw
0179nobodyさん02/07/18 23:34ID:???
この程度で荒れてるって言うのか。
0180nobodyさん02/07/18 23:43ID:???
荒れてるというよりみんなすさんでるな。
漢字で書いたら一緒だけど。

ま、もう当分この板はこんな調子でしょ・・・。
0181nobodyさん02/07/19 01:05ID:???
この板ってほんと殺伐系だね。馴れ合い一切なし。さっぱりしてて気持ちいいけどね。
0182nobodyさん02/07/19 01:29ID:???
>>165
いろいろなレスがあるけど、flockつかえるなら、それでいいんだよ。
初心者板でそう言い返してこい。
0183nobodyさん02/07/19 02:38ID:???
突然ですが、読むだけのオープンでもflockが必要なの?
他のプロセスとの関係とかあります?
0184nobodyさん02/07/19 02:45ID:???
>>165

>>1参照。
どんな環境でもつかえてっていうのが、このスレの主題。
flockは環境によってはつかえない。
flockつかえるなら、それでいい。
0185行番号でるかな02/07/19 03:00ID:???
>>183
01 open FH,"<./hoge";
02 my @hoge = <FH>;
03 close FH;
04 # @hogeを加工、比較的重い作業
05 open FH "+<./hoge";
06 flock (FH,2);
07 # 書き込み

2行目で配列に全部読み込んでいる、hogeの内容全部がオンメモリな状態。
今10個のプロセスが一斉に読み込みを行ったとする。
つまり10個のプロセス全てが同じ内容の@hogeを得る。
全てのプロセスが完了した時のhogeの内容はどれか一つの
プロセスの内容しか反映されて居ないだろう。
018618302/07/19 03:17ID:???
なるほど、了解しました。
どこかのサイトに読み込みオープンでもflockすべきだと
書いてあったのが気になっていたんですが、
そういう場合のことだったんですね。
ありがとうございました。
0187nobodyさん02/07/19 14:05ID:???
ファイルロックなんでも相談室っぽい雰囲気になりそうだ
0188nobodyさん02/07/19 15:23ID:H65klWUw
http://www.din.or.jp/~ohzaki/perl.htm#File_Lock

これじゃだめか?
0189nobodyさん02/07/19 15:44ID:???
timeが全く同じだと、通常のrename式と同じ問題が生じる。
まあ、可能性ははるかに低くなると思うが。
0190nobodyさん02/07/19 15:48ID:???
>>189
???????????????????????????????????????????????????
0191nobodyさん02/07/19 19:14ID:g9uMB3Mk
>>189
同じtimeでも唯一のプロセスしかロックできないから大丈夫でない?
1秒内にロックを解除しないままプロセスが死んだ時ってこと?
0192nobodyさん02/07/20 21:32ID:???
2chはflockか?
0193nobodyさん02/07/25 16:40ID:Ong5dr5a
結局、実際の書き込みを、
1.ユニークなファイル名を作り書き込み。
2.指定のファイル名にrename
にしときなさいってこった。
0194nobodyさん02/07/25 17:54ID:???
>>193
rename時に衝突することもあるってこった。
0195nobodyさん02/07/26 01:04ID:???
ファイルロック完全にやって50点。
書き込み中のプロセス事故死に対応して100点。
0196nobodyさん02/07/26 02:18ID:???
>>195
それでは認識が甘いな。
0197nobodyさん02/07/26 03:55ID:???
&unlock if($lockkey);

sub unlock{
省略
}
この構文の意味がわからんから教えてくれ
unlock関数とif文が何で一緒になってるんだ?
0198nobodyさん02/07/26 05:36ID:???
教えてくれ、だってさ教えてくれ!!
ちょっと聞いた奥さん教えてくれ!
ぷぷぷ
0199 02/07/26 09:01ID:???
>>197はマルチポスト
0200nobodyさん02/07/26 11:19ID:???
ファイルロックって書き込み処理のときだけすればいいんですか?
読み込み処理のときにはしなくてもいいんですか?
0201nobodyさん02/07/26 11:27ID:???
>>192
2ちゃんはflockでした
0202nobodyさん02/07/26 13:35ID:NvB7dZaP
&unlock if($lockkey);
=
if($lockkey) {
  &unlock;
}
0203nobodyさん02/07/26 13:45ID:???
>>197 && >>202
ファイルロックとは関係ないぞ。
Perl初心者スレへ。
0204nobodyさん02/07/28 16:44ID:???
>>191
それでもタイムアウトまでは待つのでは?
あ、正常な処理中にタイムアウトを超えてしまった場合が問題なのかな?
完璧を追求するのなら。
0205nobodyさん02/07/29 14:41ID:???
あげてわるいか!
0206nobodyさん02/07/29 14:47ID:???
わるいっつったらどうするよ?
0207nobodyさん02/07/29 15:10ID:???
>>206
あげたもんはさがるまで待つしかしょーがねーだろ?って開き直るよ。
0208nobodyさん02/07/29 15:15ID:???
homepage1.nifty.com/glass/tom_neko/web/web_04.html
のページでロックの仕方が紹介されていたので使わせてもらおうと思いました。
で、「古いロックファイル(ディレクトリ)の削除」の項目にある、
$retry = 5; # リトライ回数セット
while (!mkdir($lockdir, 0755)) { # 作成。出来なければ待つ
  if (--$retry <= 0) { # 5回ダメなら
    if (mkdir($lockdir2, 0755)) { # ロックを消すための排他
      if ((-M $lockdir) * 86400 > 600) { # 作成時間が10分以上前なら
      # ロック入れ替え
      rename($lockdir2, $lockdir) or &error("LOCK ERROR");
      last; # 一連の処理へ
      }else{ rmdir($lockdir2); } # 部分ロック削除
    }
    &error("BUSY"); # あきらめる
  }
  sleep(1); # 1秒待つ
}
一連の処理
rmdir($lockdir); # 削除
とりあえず、これ使っておけば大丈夫なんでしょうか。
2ちゃんねるみたいな同時アクセスが凄い場所でない限り。
0209nobodyさん02/07/29 15:21ID:???
>>207
別にわるかねぇよ。
0210nobodyさん02/07/29 15:24ID:???
>>207
バグもあるけどキミんとこのアクセス数ならOK。
0211nobodyさん02/07/29 15:36ID:???
>>210
どういう意味なんだ?
0212nobodyさん02/07/29 15:55ID:yLhb3gGL
どこからダウンロードしたのか忘れたんだけど、
使ってるカウンター(Perl)見たら、

ロックファイル作成
↓
ファイル読み込み
↓
ロックファイル消す
↓
カウントUP
↓
ロックファイル作成
↓
ファイルに書き出し
↓
ロックファイル消す

といった流れになってたんだけど、初めのファイル読み込みの時にもロックは必要なの?
0213nobodyさん02/07/29 16:25ID:???
>>219
とんでもねぇロックの仕方だな
0214nobodyさん02/07/29 16:27ID:???
>>219
はとんでもないロックを出さなければいけない。
0215nobodyさん02/07/29 16:32ID:???
219に期待sage
0216nobodyさん02/07/29 17:26ID:???
>>219 はロッカー♪
0217nobodyさん02/07/29 17:33ID:???
>>219
凄い。そんなアルゴリズムがあったのか。
0218nobodyさん02/07/29 17:35ID:???
>>219ある意味で、とんでもないな。
0219 ◆JAPH9PWA 02/07/29 21:21ID:???
unless(`ps` =~ /\Q$0\E/)
{
# ロック権を取得したので処理をする
}
0220nobodyさん02/07/29 22:11ID:???
>>219
\Qと\Eの意味知らんけど、確かにとんでもないな
0221nobodyさん02/07/29 22:13ID:???
Windowsでうごかねぇよ。
0222nobodyさん02/07/29 22:15ID:???
他のプロセスを全てkillする排他制御はどうか
0223nobodyさん02/07/29 22:17ID:???
>>222
最強だな、うん
0224nobodyさん02/07/30 03:13ID:???
>>223
なんで最強なんだ? suEXEC ならどうする?
0225nobodyさん02/07/30 09:03ID:???
httpdから何から、「全て」killするからでしょ。root限定で
0226nobodyさん02/07/30 09:52ID:???
そんな真面目に考えなくても
0227nobodyさん02/07/30 11:37ID:???
木村ロック
0228nobodyさん02/07/31 23:34ID:EOzgSn6U
書き込み中にタイムアウトになるとログが壊れます?
0229nobodyさん02/08/01 02:00ID:DTOpJumx
>>228に便乗
�@open直後
�Aprint直後
�Bclose
�A、�A〜�Bでしょうか?
でもopenでクリティカルな場合に吹っ飛ぶこと考えると…�@?
0230nobodyさん02/08/01 03:58ID:???
>>228
サーバの正常なタイムアウトなら、書き込みのシステムコールの
途中で止まったりしないでしょう。数回に分けて書き込んでたら、
止まるけど。サーバの容量制限に引っ掛かって止まる事もあるし。
>229
書き込むときに上書き(>)で開くと内容が消えるので、その直後に
止まったらアウト。

どうしてもログを守るんだったら、かならずtempファイルに書き
出して、書き込み成功を確認した後にrenameすることが必要。
ファイルサイズを計ってサイズが異様に小さいときは失敗だし、
内容をチェックすれば完璧。

これならrenameの瞬間に電源が切れるか、HDDがクラッシュする
くらいの事がなければ、壊れないはず。(バグは除く)
どのみち定期的なバックアップは必要かもね。
023122902/08/01 13:35ID:???
>>230
thnx
参考になりますた
023219302/08/01 19:06ID:W9oFKOXJ
>>194
だから〜。
処理ロックのファイルロックは当然かけておいて、
書き込み時はユニークファイル名に書き出してrename
ってこったよ。
0233nobodyさん02/08/02 01:01ID:???
>>232
アマちゃんですね。
0234nobodyさん02/08/03 18:57ID:???
http://www.google.co.jp/search?hl=ja&inlang=ja&ie=Shift_JIS&as_qdr=all&q=site%3A2ch.net+perl&lr=
>>1-233
この調子でがんばれ糞ども
023522802/08/03 20:27ID:npWjw1lc
230さま
たまにログファイルが途中でぶち切れちゃうんです
ファイルアップありだから
でかいファイルをアップロードした時に
途中でタイムアウトするのかと思ったんですが
そうでもないんですね・・・

open(DB,"+<$logdir$log_d") || &error('ファイルエラー error_24');
flock(DB, 2);
my @lines=<DB>;
unshift (@lines,$thred);
seek(DB, 0, 0);
print DB @lines;
truncate(DB, tell(DB));
close (DB);

これ問題ないですよね?
なんでだろ・・・
flock使えない鯖なんでしょうか?
他に原因は考えられます?
0236nobodyさん02/08/03 23:45ID:???
>>235
でかいファイルを全部配列に入れるのは大問題だ
023722802/08/04 07:59ID:xc+iopog
>236
50件ずつにログを区切ってるんで
読み込んでるのは
書き込み50件分のデータです
スレッドのタイトル一覧も表示しないといけないし・・・
023822802/08/04 08:03ID:xc+iopog
やっぱ
flockが使えないんでしょうか?
それかtell(DB)の値がおかしくなることがあるのかな?
0239nobodyさん02/08/04 12:11ID:???
>>235
> seek(DB, 0, 0);
> print DB @lines;
> truncate(DB, tell(DB));

先頭にseekした後に、truncate(FH, tell(FH)) したら当然ファイルサイズ0に
なるわけだが。
024023002/08/04 12:57ID:???
データが大きくないとしても、my @lines=<DB>が失敗してるのかも。
my @lines=<DB> or &error('読み込みエラー');
としてみては?
読み込んだ大きなファイルの方にメモリを食われてるのかもしれず。
システムコール(ここではファイル操作)は常に失敗の可能性を考えて
おいたほうがいいかと。flockやprintもね。
0241nobodyさん02/08/04 13:14ID:???
>>235
もうちょっと問題切り分けた方が良いよ
マルチパートだと思うんだけど、変なモジュールを使って
データ千切れちゃってるとか。
Niftyはflock空振りサーバの代名詞だけど大丈夫?
適当にsleepしてウェイトかけながらテストするとか
シグナル関係全部無視するとか
基本的に共用ならサーバからkillされる様な使い方は
間違ってると思うよ
もし自分のなら、さっさとコネクション切ってじっくりと作業すれば
良いだけなんだけど
サーバのBBSとかFAQは読んでる?

>>239
それは print 〜 が有るから問題無い
と言う事で、一応。
024223902/08/04 14:38ID:???
>>241 正直、スマンカッタ。
0243nobodyさん02/08/04 15:21ID:???
で、なにが最強なの?
024422802/08/04 21:59ID:etatASlj
レスありがとうございます
最初から書いとくべきだったんですが
説明を付け足すと
2chタイプの掲示板なんです
でログファイルへの書き込みはこの部分だけです
カウンターなんかも同じ方法で書き込みしてるんですが
それは飛んだことないんです
で、ログファイルへの書き込みが処理の最後の方なので
遅い回線でファイルアップした時にデータを送るのに
時間がかかり途中でCGIが止まる事があるのかな?
と考えてたのです(最後の方の処理のログファイルが影響受けやすいと)

でも、どういう条件でログが消えるのかは特定できてません
スレッド50件記録のうちの途中(全部じゃないです。例えば36件目とか)で
消えてしまいます(メッセージが途中までしかなかったり)・・・
(レスは別ファイルなのですが、そっちは消えません)

>読み込んだ大きなファイルの方にメモリを食われてるのかもしれず。
そういう制限もあるんですね試してみます
>もうちょっと問題切り分けた方が良いよ
でも、書き込みしてるのこの部分だけなんですよ
やっぱflockが使えなくて
他のプロセスの書き込み中のデータを読み込んでるとログが途中で消えますよね?
でも、カウンターは消えないんですよ・・・
(それともそのうち消えるんでしょうか?ファイルサイズが小さいから確率が低いだけ?)
>サーバのBBSとかFAQは読んでる?
すいません、その辺、勉強してきます
0245nobodyさん02/08/04 22:48ID:???
>>244
一度flock以外のロックで試してみれば?
こういうのが問題を切り分けるって事じゃないのか。
024623002/08/05 01:30ID:???
データのどこで切れるかが重要。

1.flockが利かない場合は、まるっと新規発言が記録されないか、書
き込みの後のtruncateで半端な位置で切られるわけで、それぞれの
発言内容の量にさほど違いが無いなら、48〜50発言目くらいの後ろ
の方で切れるはず。

2.新規発言の処理に問題あるなら、unshiftで入れた1発言目(あとで
気が付いたなら数発言目)で切れるが、その後ろの発言まで消える事
は無い。

3.書き込みが全面的に失敗し、書き込まれて無いのに切り詰めれば
ログ丸ごと消える。(ずっとあとになって気が付けば36発言目になっ
てたりするが)

というわけで、36発言目で切れるなら問題は
1.読み込み(my @lines=<DB>)
2.書き込み(print DB @lines)
3.切り詰め(truncate(DB, tell(DB)))
の3個のどれかが失敗してると考えられるよね。それぞれ失敗時には
エラーにしないと。で、これらが失敗する原因は、メモリ使用量や
ディスク容量の制限、あるいはサーバの混雑にありそう。

途中まで書き込んでprintが失敗してるんなら、tempファイル式にす
るしか無いけど、まずは上記の対策してから。
024722802/08/06 00:52ID:eY1Nkha6
my @lines=<DB> || &error('読み込みエラー');
にしたら
最初の一行しか読まないんですが・・
すいません・・・
0248nobodyさん02/08/06 01:13ID:???
評価順位って知ってる?
(@lines=<DB>)
その他力本願ぶりじゃ先は真っ暗っぽいね
024922802/08/06 01:14ID:HdiBMgeW
or にしたら
ちゃんと動きました
or と || とは違うんですか?
はぁ・・
025022802/08/06 01:29ID:AoKqt/pr
おお、わかりました
ありがとうございます
0251nobodyさん02/08/06 06:51ID:???
>>248
ハァーーーーーーーー


眠たい
0252 02/08/06 09:00ID:???
>>251
何が眠たいんだ? 
my (@lines=<DB>) || &error('読み込みエラー');
で動いてるんだが、向学の為に教えて欲しい
0253 ◆JAPH9PWA 02/08/06 09:12ID:???
>>249
orは||より優先度がずっと低いんだよ。
だから
@lines = <DB> or error;
は
(@lines = <DB>) or error;
と評価され、
@lines = <DB> || error;
は
@lines = (<DB> || error);
と評価される。
||演算子のオペランドはスカラコンテキストで評価されるから、
結果として一行しか読み込まれないことになる、というわけね。

>>252
< my(@lines = <DB>)
> (my(@lines) = <DB>)
0254 02/08/06 15:22ID:???
>>253
優先度を云々するのなら、= と || および = と or について言わないと意味ナイだろ。
0255 ◆JAPH9PWA 02/08/07 10:19ID:???
>>254
そういえばそうだ。肝心な所が抜けててごめん。

>>253 追記
とりあえず、代入演算子は比較的優先順位が低くなってるんだよ。
例えば、算術演算子や比較演算子などは全て代入演算子より優先度が高い。
だからこそ
$foo = 100 + 200; という式が
($foo = 100) + 200; ではなく、$foo = (100 + 200);と評価される。
で、||や&&といった演算子は代入演算子よりも優先順位が高いため、
加算演算子と同様の評価順序になるってわけ。
しかしながら、orやandといった演算子の優先順位は代入演算子のそれより低いので、
このケースの場合は期待通りに動くわけだね。

詳細についてはついてはperldoc perlopを参照のこと。
0256 ◆JAPH9PWA 02/08/07 10:21ID:???
ていうかここはファイルロックスレじゃないか!
スレ違いもいいとこだね。とほほ…
025722802/08/10 00:33ID:???
おかげであれからログが消えてません
open(LOG,"+<$log") || &error('ファイルエラー');
flock(LOG, 2) or &error('ロックエラー');
my @lines = <LOG> or &error('読み込みエラー');
〜〜〜更新処理〜〜〜
seek(LOG, 0, 0);
print LOG @lines;
truncate(LOG, tell(LOG));
close (LOG);
こんな感じです
やっぱり同時刻に書き込みが集中した時に消えるようなので
KENT式mkdirロックを少し改造したものをあわせて使ってます
そのまま使うとエラーでロックを外した瞬間に書き込みが重なってしまうようなので・・・
ありがとうございました
025823002/08/10 06:54ID:???
>>257
>そのまま使うとエラーでロックを外した瞬間に書き込みが重なってしまう

ロックを外したら、もはやそのプロセスは書き込まないはず。
まさか&errorでexitせずに、実行続けてるとか?

mkdir式が有効なのは、flockがダメだったからかもしれないが、
処理が遅くなるのとタイムアウトがある事で、読み書きが集中
しないようになった効果かもしれない。

質問者は問題が解決しさえすればいいんだが、
flockとmkdir式の併用なんて気持ちわるい結末だなぁ。
025922802/08/10 18:58ID:???
>ロックを外したら、もはやそのプロセスは書き込まないはず。
どんなエラーでもロックを外してしまうので
書き込み中の人がいて、それ以外の人がエラー出してロックを外してしまう
というのが問題でした
上で書いてるのはちょっと違いました。。

いろいろ教えて頂いたのに、はっきり原因を解明せずに
気持ち悪い結末にしてしまって申し訳ないですが
上のflockのみではprintで失敗したらどうしようもないのなら
mkdirを併用する方法はいいんじゃないかと思うんですが・・
026023002/08/10 22:16ID:???
>>259
書き込み中の人がいるとしたら、その人しかロックが成功していないので、
それ以外の人がロックを外す事はありえないのが排他制御。
mkdir式の場合、ロック成功したプロセスがエラーで外すなら問題ないわけ。
通常、ロックを成功してないプロセスがロックを外すようなのはバグ。
flockの場合はロックかけたプロセス以外は外せないよ。

> printで失敗したらどうしようもない
mkdirを併用しても同じ状況だけど。
print LOG @lines or &error('書き込みエラー');
でOK。truncateする前にやめればデータは壊れない。

とにかく、mkdir式にするならflockは必要ないので消そうよ。
0261nobodyさん02/08/11 12:39ID:???
結局KENTかよ!!!
0262nobodyさん02/08/11 12:53ID:???
flock使えるならflockにしとけよ!!!
0263nobodyさん02/08/11 13:45ID:???
flockよりKENTが優れていることが証明されました!!!
0264nobodyさん02/08/11 13:46ID:???
どこで?
0265KENT02/08/11 15:40ID:???
flockってカエルだよね?
0266 02/08/11 15:50ID:???
おはロックが有効な鯖ってあるの?
0267nobodyさん02/08/11 19:41ID:???
>>228
だから初めにflock効いてるのか調べてと言ってるのに
空振りしてるんだよ、それ
他のファイル編集部分は時間的にロック無しでも動いてるだけだよ多分
■ このスレッドは過去ログ倉庫に格納されています