2011-03-09 79 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位。

編輯:我搜索瞭如何讓Windows執行無緩衝IO,這是如何做到的! 呼叫

  1. CreateFile與FILE_FLAG_NO_BUFFERING dwFlagAndAttributes可以。然而,這有memory alignment issues要考慮(即文件訪問緩衝區應扇區對齊;應用程序通過調用GetDiskFreeSpace確定扇區大小)
  2. 使用WriteFile將數據寫入文件。這個寫入將被緩衝,而不是去緩存,它直接進入磁盤。
  3. 最後,請致電FlushFileBuffers刷新與文件關聯的元數據。

有人能請這些3個調用來幫助Perl的Win32 APIs。

+0

嘗試在您的打印中添加一個'\ n'並使用'$ | = 1'。 –

+0

'$ |'僅在當前選定的文件句柄上設置自動刷新(默認情況下爲標準輸出),並非所有文件句柄 – Cameron

+0

'\ n'永遠不會幫助未附加到終端的句柄。對於具有自動刷新('$ | = 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

你能做的最好的是sysopenO_SYNCfcntl標誌,或fsync()File::Sync;你給出的選項可以確保數據不會被緩存在你的程序中,但對內核是否正在緩衝寫入沒有任何幫助(這是因爲不斷將相同的塊刷新到磁盤會減慢所有其他I/O)。即使這樣你也可能會失敗,因爲一些硬盤會存在於操作系統中,並聲稱當數據實際上仍在驅動器內存緩衝區中時,數據已被提交給介質。

+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儘快,但操作系統可能需要一段時間才能將其提交到磁盤。不過,我相信你會發現這將滿足你的需求。

如果您在unix上,我會參考您的sync瞭解有關操作系統將數據提交到磁盤的更多信息。

+0

我也嘗試了autoflush,仍然是90%的時代,它並沒有將數據持久化到硬盤上。 – Santhosh