![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
オープン中のファイルをflockによる排他をしたままrenameしたいのですがどうもうまくいきません。
sysopen(LOCK, "$file", O_WRONLY); もしくは open(LOCK, "> $file");
flock(LOCK, 2);
rename($tempfile,$file);
close;
close前にrenameするとロックが外れてしまいます。
close後にrenameをするとやはりロックが外れた状態でのrenameになります。
renameするファイル以外にflock専用ファイルを固定で一つ用意すれば簡単なのですが
openしたいファイル数は相当な数があり、各ファイルごとにロックをかけたいのです。
全ファイル分のflock専用ファイルを用意するか、排他方式をmkdirにする以外
何か良い手立てはございますでしょうか。
No.5ベストアンサー
- 回答日時:
しまった。
よく考えたら、$fh->seekや$fh->truncateのところで、プロセスが死ぬと、役立たずの印('+')を書き込まないから、次にロックを取得したプロセスが、データの一貫性を壊しちゃいますね。なのでやっぱり、別途ロックファイルを用意するのがベスト、という事に。
参考ソースにクラスまで作成して頂き恐縮致します。
>まあ言いたかったのは「renameでロックは外れない」という事で。
言い方が大雑把で申し訳ございませんでした。
今回の質問の主旨である「実ファイルごとにflockを掛ける」には
実ファイル自体flockを掛ける為にまずopen→flockしなければならず、
その後renameする事により「今開いている(実ファイルの為のファイルハンドル)が役立たず」になる結果から
主旨である「実ファイルの為のロック効果」が無効=ロックが外れると言う言い方になってしまいました。
テンポラリファイルの作業を実ファイルopen中に記述することを省略してしまったのも良くありませんでした。誠に申し訳ございません。
現状では別途ロックファイルを用意して対応して参りたいと思います。
大変ご丁寧な対応に感謝致します。
No.4
- 回答日時:
>具体的なソースを拝見できたら
ってことで以下に。どっか間違ってるかも。一応、Solaris10とMacOSX上のperlで試してます。
WindowsのSUAだと、truncateをコメントアウトしなきゃダメでした。
やはり、別途ロックファイルを用意するのが一番簡単かと。
まあ言いたかったのは「renameでロックは外れない」という事で。
※UNIX系なら。Windowsだと違うかも。
use warnings;
package CounterFile;
use Carp; use Fcntl qw( :flock ); use IO::File; use POSIX;
my $MARK_INVALID = '+';
sub new { my $class = shift; my $fname = shift;
my $self = bless {}, $class;
$self->{name} = $fname; $self->{cnt} = 0;
$self;
}
sub open { my $self = shift; my $fname = $self->{name};
my $fh = new IO::File( $fname, O_RDWR|O_CREAT );
confess '?![',$$,']IO::File>', $!, ' - ', $fname unless defined $fh;
flock( $fh, LOCK_EX ) or confess '?![',$$,']flock>', $!, ' - ', $fname;
# ロックを取得した時点で、今開いているファイルハンドルは、役立たずになっている可能性がある。その場合は再度openを試みなければいけない。
$fh;
}
sub inc { my $self = shift; my ($fh, $i);
do {
$fh = $self->open();
$i = <$fh> || 0;
} while ( $i eq $MARK_INVALID );
$self->{cnt} = ++$i;
my $fn_tmp = '.'.$self->{name}.$i.'-'.$$.'-'.time().'.tmp';
my $fh_tmp = new IO::File( $fn_tmp, O_WRONLY|O_CREAT );
confess '?![',$$,']IO::File>', $!, ' - ', $fn_tmp unless defined $fh_tmp;
$fh_tmp->print( $i ); undef $fh_tmp;
rename( $fn_tmp, $self->{name} ) or confess '?![',$$,']rename>', $!, ': ', $fn_tmp, '>', $self->{name};
# renameした時点で、今開いているファイル($fh)は役立たずになっており、ロック待ちしているファイルハンドルを持つ別プロセスに対し、役立たずである事を伝える必要がある。
$fh->seek( 0, 0 ) or confess '?![',$$,']seek>', $!, ' - ', $self->{name};
$fh->truncate( 0 ) or confess '?![',$$,']truncate>', $!, ' - ', $self->{name};
$fh->print( $MARK_INVALID ); undef $fh;
$self;
}
1;
package main;
$a = new CounterFile( 'a.dat' );
for ( my $k = 0; $k < 4; $k++ ){ print $$, ':', $a->inc()->{cnt}, "\n"; }
No.3
- 回答日時:
> close前にrenameするとロックが外れてしまいます。
ロックが外れるのではなく、ロックをかけてないだけでは。
renameにより、$tempfileが$fnameになったときに、別プロセス/スレッドがロックを取得しようとしている$fnameの実体は、$tempfileなので。
ただ、renameが完了した時点で、その$fnameは次の人がアクセスしても良い状態になっているはず(逆に$tempfileの処理が完了してないのにrenameしてはダメ。)なので、ロックがかかってないのは問題にならないと思われます。
この回答への補足
ご回答ありがとうございます。
> ただ、renameが完了した時点で、その$fnameは次の人がアクセスしても良い状態になっているはず(逆に$tempfileの処理が完了してないのにrenameしてはダメ。)なので、ロックがかかってないのは問題にならないと思われます。
renameする場所はflock内を想定していると思われますが、rename→ロックが外れる→closeとなりますと排他処理が不完全になってしまいます。具体的なソースを拝見できたらと思います。解釈が間違えておりましたらご指摘をお願い致します。
ちなみに簡単なカウンターを例にソースを書いてみました。rename後のcloseに問題があるようで排他は不完全でした。
sysopen(LOCK, $file, O_WRONLY|O_CREAT);
flock(LOCK, 2);
copy($file,$tempfile);
chmod(0666,$tempfile);
sysopen(TMPLOCK, $tempfile, O_RDWR|O_CREAT);
flock(TMPLOCK, 2);
my$cnt=<TMPLOCK>;
seek(TMPLOCK, 0, 0);
$cnt++;
print TMPLOCK $cnt;
truncate(TMPLOCK, tell(TMPLOCK));
close(TMPLOCK);
rename($tempfile,$file);
close(LOCK);
No.2
- 回答日時:
ちょっと用途が分かりませんがファイルをコピーしてコピーした方を
ロック付きで開きその後にコピー元を削除するという方法はどう
でしょうか。
結構面倒ですね。
何をしたいのかがもう少し詳細に分かればまた別の代替案なども
でてくるかも。
この回答への補足
ご回答ありがとうございます。
ファイル読み書き時のファイルの破損確立を極力減らす事を目標にしております。
具体的にやりたい事は
書き込み予定ファイル($file)のテンポファイル($tempfile)をコピー作成
→ rename($tempfile,$file); です。
そこで確実な排他処理を行いたいと言う事になります。
テンポファイルが必要な理由は以下の通りです。
・$tempfileへの正常に書き込みが完了した確認が取れた後に本ファイル$fileにrename
・ディスクスペース確認
> コピーした方をロック付きで開き
> その後にコピー元を削除
コピー元($file)が存在しなくなりますと、
コピーした方($tempfile)が正常に処理を完了して戻す処理→rename($tempfile,$file); を行うまでにプロセスが死にますと元ファイルの存在がなくなってしまいます。
$fileを保護する為に代理ファイルの$tempfileを用意したい意向ですので根本の$fileを削除してしまうのでは無意味になってしまいます。
案外、結構面倒です。。
本当の答えは実はシンプルなんだ!みたいな意見が出たら嬉しいです。
幻想でしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Perl perlのflock関数でロックをかけたままopen関数で何度もファイルを開きなおすことはできますか 3 2023/05/01 22:25
- Windows 10 renameコマンドでファイル名からアンダースコアを消す方法 5 2023/04/24 13:33
- CGI htmlからパラメータで、cgiに渡したい。 1 2023/02/06 16:15
- UNIX・Linux shellscript内のコマンドを、sudo(toor)として実行 2 2022/09/23 15:05
- その他(プログラミング・Web制作) Fortranでの出力ファイル 2 2023/03/21 21:25
- Excel(エクセル) Excelにて、フォルダ内のTextファイルをマクロで統合すると文字化けしてしまう時の解消コード 4 2023/01/01 07:32
- Visual Basic(VBA) Excelのマクロについて教えてください。 作業フォルダ内に2つのファイルがあります。 このファイル 2 2023/07/09 13:40
- PostgreSQL ポストグレにあるExcelファイルを開くには 1 2022/12/13 18:07
- その他(Microsoft Office) OneDrive Personalについて 1 2022/08/02 18:25
- その他(プログラミング・Web制作) セレクトボックスで選択された値をコントローラーで使用したい 2 2022/07/26 16:41
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
die関数のエラー出力先について
-
改行コードが勝手に
-
プログラムのヒントを下さい
-
window.open でのファイル指定方法
-
VBAでCSVファイルの特定行を書...
-
ReadLineでの読み出し行を指定する
-
awkスクリプトでダブルクォーテ...
-
VBAで巨大なファイルの途中から...
-
ExcelをCSV書き出す場合のシー...
-
VBAでCSVファイルを途中行まで...
-
htaccessで特定のディレクトリ...
-
fopenでディレクトリ内の全ファ...
-
ファイル出力の改行コードをLFに
-
エクセルVBAで素数だけを出力す...
-
fgets で値が取得できない
-
エクセルVBA コードが同じでも...
-
配列の中に重複文字列があるか...
-
DOSコマンドで、標準出力を出力...
-
MATLABのm-fileについて
-
openした後、closeしないでプロ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
close()で例外が投げられる理由
-
改行コードが勝手に
-
perlを用いた特定文字列間の抽...
-
Perl で syntax error
-
ハッシュにファイルハンドル
-
where can I buy snowbord in t...
-
open中のファイルをrename
-
GD.pmで作成した画像を保存する...
-
perlで複数のファイルの処理に...
-
while(<ハンドラ>) {} で行数を...
-
パスワード自動生成スクリプト...
-
die関数のエラー出力先について
-
Perlでファイル出力時にデッド...
-
perlのflock関数でロックをかけ...
-
PICでFatFsでオープンした内容...
-
クリックを何回もされて、重複...
-
cgiの投票回数制限設定について...
-
ファイルから読み込んだ文字を
-
テキストファイルの本文中に行...
-
データの並べ替え
おすすめ情報