3

open および print 呼び出しを使用してログをファイルに書き込む Perl アプリケーションがあります。

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

ただし、マシンの突然のシャットダウン中は、ログはファイルに保持されません。したがって、いくつかの場所を検索した後、バッファリングされていない IO を実行するための 2 つのオプションが提案されました (つまり、テキストをキャッシュに保持してからフラッシュする代わりに、ディスクに書き込みます)。

  1. sysopensyswrite
  2. $| = 1;

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

Perl でバッファなし IO をほぼ決定論的に達成できる方法はありますか? Perl 5.8.3 で Windows 7 64 ビットを実行しています。

編集:ウィンドウにバッファリングされていないIOを実行させる方法を検索しましたが、これがその方法です! 電話

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

これらの 3 つの呼び出しについて、Perl の Win32 API について誰か助けてください。

4

3 に答える 3

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

これにより、OS にできるだけ早く送信されますが、OS がディスクにコミットするのに時間がかかる場合があります。それでも、これがあなたのニーズに合っていると確信しています。

Unix を使用している場合は、OS がデータをディスクにコミットする方法の詳細については、syncを参照してください。

于 2011-03-09T07:34:15.253 に答える
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;
}
于 2011-03-11T08:13:01.853 に答える
0

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

于 2011-03-09T07:27:56.183 に答える