2011-03-09 6 views
3

私は、オープンコールとプリントコールを使ってファイルにログを書き込むPerlアプリケーションを持っています。perlでのバッファリングされていないIO

open (FH, "d:\\temp.txt"); 
print FH "Some log"; 
close (FH); 

ただし、マシンを突然シャットダウンしている間は、ログはファイルに保存されません。だから、いくつかの場所で検索した後、二つのオプションは、(すなわち、ディスクへのテキストを書く代わりに、キャッシュにそれを維持し、それをフラッシュ)バッファなしIOを行うために提案された:

  1. sysopensyswrite
  2. $| = 1;

私はこれらのオプションの両方を試しましたが、うまくいきません。異常なシャットダウンが失われる前に私が何度も行うすべての書き込み。

可能なことはありますかほとんどは、PerlでバッファリングされていないIOを確定的に達成していますか?私はPerl 5.8.3でWindows 7 64ビットを実行しています。

EDIT:WindowsでバッファリングされていないIOを実行する方法を検索しました。 コールは、dwFlagsAndAttributesパラメータのFILE_FLAG_NO_BUFFERINGと

  1. CreateFile。ただし、これにはmemory alignment issuesが考慮されます(つまり、ファイルアクセスバッファはセクタアライメントされている必要があります。アプリケーションはGetDiskFreeSpaceを呼び出してセクタサイズを決定します)
  2. WriteFileを使用してファイルにデータを書き込みます。この書き込みはバッファリングされず、キャッシュに行くのではなく、すぐにディスクに書き込まれます。
  3. 最後に、FlushFileBuffersを呼び出して、ファイルに関連付けられたメタデータをフラッシュします。

誰かがこの3回の呼び出しでPerlのWin32 APIを手伝ってもらえますか?

+0

プリントに「\ n」を追加し、 '$ | = 1 'となる。 –

+0

'$ |'は、現在選択されているファイルハンドル(デフォルトではSTDOUT)にautoflushを設定するだけで、すべてのファイルハンドルが – Cameron

+0

'\ n'で端末に接続されていないハンドルには役に立ちません。 autoflush( '$ | = 1')を持つハンドルには' \ n'は必要ありません。 – ikegami

答えて

2

これはいかがですか?

use strict; 
use warnings; 

use IO::Handle  qw(); # For autoflush. 
use Symbol   qw(gensym); 
use Win32API::File qw(CloseHandle CreateFile GetOsFHandle OsFHandleOpen GENERIC_WRITE OPEN_ALWAYS FILE_FLAG_WRITE_THROUGH); 
use Win32::API  qw(); 

use constant WIN32API_FILE_NULL => []; 

sub open_log_handle { 
    my ($qfn) = @_; 

    my $handle; 
    if (!($handle = CreateFile(
     $qfn, 
     GENERIC_WRITE, 
     0,      # Exclusive lock. 
     WIN32API_FILE_NULL,  # No security descriptor. 
     OPEN_ALWAYS,    # Create if doesn't exist. 
     FILE_FLAG_WRITE_THROUGH, # Flush writes immediately. 
     WIN32API_FILE_NULL,  # No prototype. 
    ))) { 
     return undef; 
    } 

    my $fh = gensym(); 
    if (!OsFHandleOpen($fh, $handle, 'wa')) { 
     my $e = $^E; 
     CloseHandle($handle); 
     $^E = $e; 
     return undef; 
    } 

    $fh->autoflush(1); 

    return $fh; 
} 

sub close_log_handle { 
    my ($fh) = @_; 

    my $handle = GetOsFHandle($fh) 
     or return undef; 

    if (!FlushFileBuffers($handle)) { 
     my $e = $^E; 
     close($fh); 
     $^E = $e; 
     return undef; 
    } 

    return close($fh); 
} 

my $FlushFileBuffers = Win32::API->new('kernel32.dll', 'FlushFileBuffers', 'N', 'N') 
    or die $^E; 

sub FlushFileBuffers { 
    my ($handle) = @_; 
    return $FlushFileBuffers->Call($handle); 
} 

{ 
    my $fh = open_log_handle('log.txt') 
     or die $^E; 

    print($fh "log!\n") 
     or die $^E; 

    close_log_handle($fh) 
     or die $^E; 
} 
0

あなたができる最善のO_SYNCfcntlフラグをsysopen、またはFile::Syncからfsync()です。あなたが与えたオプションは、データがあなたのプログラムの中にバッファリングされないことを保証しますが、カーネルが書き込みをバッファリングしているかどうかは何もしません(常に同じブロックをディスクにフラッシングするので、他のすべてのI /また、一部のハードドライブがOSに存在し、実際にオンドライブメモリバッファに入っているときにデータがメディアにコミットされていると主張するため、失われる可能性もあります。

+0

WindowsでFile :: Syncを使用することはできないようです(cygwinを除く) – ikegami

+0

Windowsのプログラミングには慣れていません。うまくいけば、他の誰かが飛び込むことができます。 – geekosaur

+0

O_SYNCはWindowsでは利用できません! – Santhosh

6
use IO::Handle; 
open(FH, "d:\\temp.txt"); 
FH->autoflush(1); 
print FH "Some log"; 
close(FH); 

これは、OS ASAPにそれを取得しますが、OSはディスクにコミットするまでに時間がかかることがあります。それでもあなたのニーズに合ったものを見つけることができます。

UNIXの場合は、OSにディスクへのコミットに関する詳細については、syncを参照してください。

+0

私はまた、90%の時間のautoflushを試みました、それはハードディスクにデータを保持しません。 – Santhosh

関連する問題