perl中的无缓冲IO

时间:2011-03-09 07:14:49

标签: windows perl io unbuffered

我有一个Perl应用程序,它使用打开和打印调用将日志写入文件。

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

但是,在机器突然关闭期间,日志不会持久保存到文件中。因此,在几个地方搜索之后,建议使用两个选项来执行无缓冲的IO(即将文本写入磁盘而不是将其保留在缓存中,然后将其刷新):

  1. sysopensyswrite
  2. $| = 1;
  3. 我已经尝试了这两个选项但它不起作用。在异常关机之前我做的任何写操作都会丢失。

    有什么方法可以几乎确定性地在Perl中完成无缓冲的IO?我使用Perl 5.8.3运行Windows 7 64位。

    编辑:我搜索了如何让Windows执行无缓冲的IO,这就是它的完成方式! 呼叫

      对于dwFlagsAndAttributes参数,使用FILE_FLAG_NO_BUFFERING的
    1. CreateFile。但是,这需要考虑memory alignment issues(即文件访问缓冲区应该是扇区对齐的;应用程序通过调用GetDiskFreeSpace来确定扇区大小)
    2. 使用WriteFile将数据写入文件。这个写入将是无缓冲的,而不是直接进入缓存,它直接进入磁盘。
    3. 最后,调用FlushFileBuffers来刷新与文件关联的元数据。
    4. 有人可以协助Perl的Win32 API进行这3次调用。

3 个答案:

答案 0 :(得分:6)

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

这将尽快将其发布到操作系统,但操作系统可能需要一段时间才能将其提交到磁盘。我仍然相信你会发现这会满足你的需求。

如果您使用的是unix,我会引用您sync以获取有关让操作系统将数据提交到磁盘的更多信息。

答案 1 :(得分: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;
}

答案 2 :(得分:0)

您可以做的最好的事情是sysopen O_SYNC fcntl标记或fsync()来自File::Sync;您给出的选项可以确保数据不会缓冲在程序中,但不会对内核是否正在缓冲写入做任何事情(这样做是因为不断将相同的块刷新到磁盘会减慢所有其他I / O)。即便如此,您可能会失败,因为一些硬盘驱动器会对操作系统产生影响并声称数据已经提交给媒体,因为它实际上仍处于驱动器内存缓冲区中。