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

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

■ このスレッドは過去ログ倉庫に格納されています
0001nobodyさん02/06/23 10:18ID:eY2l+Gw1
どんな環境でも使えて、軽くて、頑丈なロックを考えようじゃありません
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効いてるのか調べてと言ってるのに
空振りしてるんだよ、それ
他のファイル編集部分は時間的にロック無しでも動いてるだけだよ多分
0268nobodyさん02/08/11 21:39ID:???
「多分」って便利な言葉
俺は断言してないよ、忙しかったしよって感じ
0269nobodyさん02/08/14 01:12ID:???
http://www.kent-web.com/support/wforum.cgi?mode=allread&no=3986&page=0

排他処理とはちょっとズレるかもしれないが、これは立派にシステム上に矛盾が起きてるんだから、"バグ"ではないにしろ、立派な不具合だよな…
この不具合って、そんなに難しいことなのかな…
あたりまえのように、こういうのは想定しないかな?
Kentのスクリプトって、こういうところが馬鹿だよね。
0270nobodyさん02/08/14 01:45ID:7AoVgCGe
http://homepage1.nifty.com/glass/tom_neko/web/web_04.html#lockall
このまとめてロックっていいんですか?
0271nobodyさん02/08/14 02:02ID:???
>>270
      ,一-、
     / ̄ l |   / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
    ■■-っ < んな こたーない
    ´∀`/    \__________
   __/|Y/\.
 Ё|__ | /  |
     | У..  |
0272nobodyさん02/08/14 02:05ID:7AoVgCGe
やっぱだめなんですか
0273nobodyさん02/08/14 02:14ID:7AoVgCGe
他にいい方法はありませんか?
0274nobodyさん02/08/14 04:31ID:???
ヅラを強力ロック
0275nobodyさん02/08/14 20:00ID:DbQN4DsP
監獄ロック
0276nobodyさん02/09/14 03:01ID:???
慎吾ママの
0277nobodyさん02/10/01 08:47ID:OVvB7u9F
http://kagino110.hp.infoseek.co.jp/sub18.htm
強力なロックだ
0278nobodyさん02/10/01 14:48ID:???
>>277
ううむ、強力すぎてアクセスさえもできないよ!
0279名無しさん@お腹いっぱい。02/10/01 16:34ID:???
>>278
漏れは開きすぎちゃったよ。windows再起動した。
皆気をつけろよ。中のどれか開いたりしたら、国際電話だぞ。

0280名無しさん@お腹いっぱい。02/10/04 08:00ID:???
ム板の「Perlについての質問箱」がruby基地外によって潰されました。
心ある人は、rubyスレにお礼参りしてください。
0281nobodyさん02/10/08 18:16ID:???
>>277
ちょっとワラタ
0282nobodyさん02/10/11 23:24ID:0pBUR/gh
read.cgiのファイルロック。これ最強
0283nobodyさん02/10/12 00:02ID:???
>>282
どういうロックやってるの? 知らないから教えてくれ。
0284nobodyさん02/10/15 14:15ID:???
ロックなんかしてないよ
2chの負荷でロックなんかしたら使い物にならん
壊れてたら読み直せばすむことだし
0285nobodyさん02/10/15 18:19ID:jOp8uFKz
bbs.cgiはどうやってるの?
0286nobodyさん02/10/16 11:23ID:???
日時とスレッドキーなんかを含めたファイル名で
一時ファイルを作って書き込み終わったら
subject.txtにリネーム
renameのアトミック性に頼ってるわけだな
datはふつうにflockしてるだけ
0287nobodyさん02/10/22 13:32ID:???
わたし 思う。
ロック機能作る。
理論的 このスクリプト完璧。
でも どうテストする。
アルバイト100人雇う。
100人いっせいにアクセスする。
そんな 手間かけられない。 どうする。
2ちゃんねるに晒す。 それ怖い。 どうする。
0288 02/10/22 16:25ID:???

・ 実験しないで本番に使う。
・ 覚悟して晒す。
・ 一年生になって友達100人つくる。
0289nobodyさん02/10/22 16:39ID:???
>>287
不覚藁
0290nobodyさん02/10/22 17:09ID:???
>>287
100 スレッド立ててアクセスしまくるにきまっとろーが。
0291nobodyさん02/10/27 19:23ID:zFNcoqUs
>>185
別人ですが、読み込んだものを加工してまた書き込む場合には
読み込むときにもロックが必要なんですね?
単に読み込んで、例えば表示したいだけで
加工もしない場合は必要ないんですよね。
0292nobodyさん02/10/27 21:19ID:???
ログ更新中に読み出したらおかしな内容になってしまう。ロックは必要。
0293nobodyさん02/10/27 22:41ID:???
テストスクリプトはlock握って数十秒眠るようにするわけだ。勿論タイムアウト値の調整は必要だ。
そうすりゃ一人でもテストできるだろ。
0294nobodyさん02/10/28 01:48ID:???
>>292
表示がおかしかったら読み直してもらうという
アプローチもありうる(read.cgiとか)。
0295nobodyさん02/10/29 01:24ID:3RRgraL5
flockについて、説明として例えば

flock(XX, 2)
書込中だから書込読込ロック、あとから来たら待つ。

とあるのですが、待つってどのくらい待つことが出来るのでしょう…?
上限や目安みたいなものはあるのでしょうか?
0296nobodyさん02/10/29 01:40ID:???
>>295
めっちゃ、待つでぇ
0297 ◆hMJAPH9PWA 02/10/29 09:26ID:???
>>295
実行者が我慢できなくなってkillするまで、ずっと待つよ。
で、それじゃこまるからってんでブロックしないモードがある。

use Fcntl qw(:flock);

flock FH, LOCK_EX | LOCK_NB or die "ロック失敗:$!";
0298nobodyさん02/10/29 09:56ID:???
もっともブロックしないで無限ループするくらいなら
ブロックして待ったほうがましだが
0299nobodyさん02/11/03 16:56ID:???
大事件発生!

rename を使った完璧なファイルロックが完成してしまいました。
0300nobodyさん02/11/03 18:41ID:???
外出です
0301nobodyさん02/11/05 18:35ID:5Ci8Aq1y
んむ・・・。
0302perlお勉強ちゅー02/11/29 01:12ID:vd7dmowT
>>299
こんな感じかな、

my($process)=$$;
open (IN,"data.txt");
flock (IN,2);
open (OUT,">$process.tmp");
flock (OUT,2);

#hogehoge処理

close (IN);
close (OUT);
rename ("$process.tmp","data.txt");

不安なんですけど、これで良いのでしょうか?皆様。
0303perlお勉強ちゅー02/11/29 01:54ID:???
↑ open の戻り値チェックしてなかった。スマソ
0304私も勉強中02/11/29 17:19ID:???
close (OUT);
から
rename ("$process.tmp","data.txt");
までの間に、他のプロセスが open (OUT,">$process.tmp") を
実行してしまい、$process.tmpが空になる可能性がある。

※open (OUT,">$process.tmp")した瞬間
$process.tmpのファイルサイズが0になるので、
次の flock (OUT,2) で止めても手後れ。

そんな気がするが、どうだろうか。
030530402/11/29 17:25ID:???
ごめん、間違えた。
tmpファイルの名前を毎回変えれば大丈夫か。
0306nobodyさん02/11/29 19:16ID:???
>>302
普通に、だめだね。
たとえば、Aプロセスがclose(OUT)まで行く、その直後にBプロセスが
open (IN,"data.txt");、でそのあとAプロセスがrename、Bプロセスは
普通に終了するけど、BのINは、Aが変更を加える前のdata.txtなので
Bがrenameした時点で、Aで加えた変更は破棄される。
0307perlお勉強ちゅー02/11/30 00:18ID:???
>>304 さん >>306 さん
ご解説して下さってありがとうございます。
close しなければ rename できないと勝手に
思い込んでいました。(やってみたらできました)
close したときに flock が解除されるのなら
rename 後に close しとけば別プロセスに割り
込まれることが減り、少しはましになるかな。
0308nobodyさん02/11/30 15:54ID:???
>>307
> rename 後に close
だめ。 AプロセスがINをopen、直後にBがINをopen、Aがflockして処理を
おえる、Bがflockを獲得して処理を終える。これでも、BのINはAが変更を
加える前のdata.txtなので、同様にAの変更が破棄される。

なおかつ、Win環境だとopen中のファイルのrenameはできなかったような?
0309私も勉強中02/11/30 16:23ID:???
open (LOCK,"lock");
flock(LOCK,2);
open (IN,"data.txt");
open (OUT,"date.tmp");

#hogehoge処理

close (IN);
close (OUT);
rename ("date.tmp","data.txt");
close (LOCK);

INの前にflockするという方向で考えると、こうなるのか?
renameを使ったファイルロックと言っていいのかどうか分からないが。
■ このスレッドは過去ログ倉庫に格納されています