Unbuffered IO in perl

I have a Perl application that writes logs to a file using open and print calls.

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

However, with a sharp shutdown of the machine, the logs are not saved in the file. Therefore, after searching in several places, two options were proposed for performing unbuffered I / O (for example, for writing text to disk, and not for saving it in the cache, and then for cleaning it):

I tried both of these options and it just doesn't work. Any entry I make seconds before abnormal shutdown is lost.

Is there a way that I can almost deterministically perform unbuffered I / O in Perl? I am using the 64-bit version of Windows 7 with Perl 5.8.3.

EDIT: I was looking for how to make windows without I / O buffering, and here's how to do it! Call

  • CreateFile with FILE_FLAG_NO_BUFFERING for the dwFlagsAndAttributes parameter. However, these are memory alignment issues to consider (for example, file access buffers must be sector-aligned; the application determines the sector size by calling GetDiskFreeSpace )
  • Use WriteFile to write data to a file. This record will be unbuffered and instead of going to the cache, it immediately goes to disk.
  • Finally, call FlushFileBuffers to clear the metadata associated with the files.

Maybe someone can help using the Win32 API with Perl for these 3 calls.

+3
3

?

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

, , . , , , .

unix, sync , OS .

+6

The best you can do is sysopenwith a flag O_SYNC fcntlor fsync()from File::Sync; the parameters that you provide ensure that the data is not buffered inside your program, but do nothing about whether the kernel is buffering (this is because the constant deletion of the same block on the disk slows down all other input operations- output). And even then you can lose, because some hard drives will lie in the OS and claim that the data was transferred to the media when they are actually still in the memory buffer on the disk.

0
source

All Articles