Небуферизованный IO в Perl - PullRequest
3 голосов
/ 09 марта 2011

У меня есть приложение Perl, которое записывает журналы в файл, используя вызовы открытия и печати. ​​

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

Однако во время внезапного выключения машины журналы не сохраняются в файле.Поэтому после поиска в нескольких местах были предложены два варианта выполнения небуферизованного ввода-вывода (т.е. записи текста на диск вместо сохранения его в кеше и последующей его очистки):

  1. sysopen , syswrite
  2. $ |= 1;

Я испробовал оба эти варианта, но они просто не работают.Любая запись, которую я делаю за секунды до того, как ненормальное отключение будет потеряно.

Есть ли способ, которым я могу почти детерминистически выполнить небуферизованный ввод-вывод в Perl?Я использую 64-битную Windows 7 с Perl 5.8.3.

РЕДАКТИРОВАТЬ: Я искал, как заставить Windows выполнять небуферизованный ввод-вывод, и вот как это можно сделать!Вызовите

  1. CreateFile с FILE_FLAG_NO_BUFFERING для параметра dwFlagsAndAttributes.Однако при этом необходимо учитывать проблем с выравниванием памяти (т.е. буферы доступа к файлам должны быть выровнены по секторам; приложение определяет размер сектора, вызывая GetDiskFreeSpace )
  2. Use WriteFile для записи данных в файл.Эта запись не будет буферизироваться, и вместо того, чтобы идти в кеш, она сразу переходит на диск.
  3. Наконец, вызовите FlushFileBuffers , чтобы сбросить метаданные, связанные с файлами.

Может кто-нибудь помочь с Win32 API от Perl для этих 3 вызовов.

Ответы [ 3 ]

6 голосов
/ 09 марта 2011
use IO::Handle;
open(FH, "d:\\temp.txt");
FH->autoflush(1);
print FH "Some log";
close(FH);

Это доставит его в ОС как можно скорее, но ОС может занять некоторое время, чтобы зафиксировать его на диске.Тем не менее, я уверен, что вы найдете, что это будет соответствовать вашим потребностям.

Если бы вы работали в Unix, я бы обратился к вам sync для получения дополнительной информации о том, чтобы данные фиксации ОС былидиск.

2 голосов
/ 11 марта 2011

Как насчет этого?

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 голосов
/ 09 марта 2011

Лучшее, что вы можете сделать, это sysopen с флагом O_SYNC fcntl или fsync() из File::Sync; опции, которые вам дали, гарантируют, что данные не буферизуются внутри вашей программы, но ничего не делают с тем, что ядро ​​ буферизует записи (что происходит потому, что постоянная запись одного и того же блока на диск замедляет все другие операции ввода-вывода ). И даже в этом случае вы можете проиграть, потому что некоторые жесткие диски будут лгать ОС и утверждать, что данные были записаны на носитель, когда они все еще находятся в буфере оперативной памяти.

...