perl 中的无缓冲 IO

发布于 2024-10-20 17:56:10 字数 1423 浏览 1 评论 0原文

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

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

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

  1. sysopensyswrite
  2. $| = 1;

我已经尝试了这两个选项,但它不起作用。我在异常关闭之前几秒钟所做的任何写入都会丢失。

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

编辑:我搜索了如何让 Windows 执行无缓冲 IO,这就是它可以完成的方法! 调用

  1. 使用 dwFlagsAndAttributes 的 FILE_FLAG_NO_BUFFERING CreateFile范围。但是,这存在内存对齐问题考虑(即文件访问缓冲区应扇区对齐;应用程序通过调用 GetDiskFreeSpace)
  2. 使用 WriteFile 将数据写入文件。此写入将是无缓冲的,并且不会进入缓存,而是直接进入磁盘。
  3. 最后,调用 FlushFileBuffers 刷新与文件关联的元数据。

有人可以协助使用 Perl 的 Win32 API 来完成这 3 个调用吗?

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

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

However during an abrupt shutdown of the machine, the logs are not persisted to the file. So after searching at several places, two options were suggested for doing unbuffered IO (i.e writing the text to the disk instead of maintaining it in cache and then flushing it):

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

I have tried both these options and it just doesn't work. Any write that I do seconds before the abnormal shutdown gets lost.

Is there any way that I can almost deterministically accomplish unbuffered IO in Perl? I am running Windows 7 64-bit with Perl 5.8.3.

EDIT: I searched for how to have windows perform unbuffered IO and this is how it can be done!
Call

  1. CreateFile with FILE_FLAG_NO_BUFFERING for the dwFlagsAndAttributes parameter. However, this has memory alignment issues to consider (i.e File Access buffers should be sector aligned; An application determine the sector size by calling GetDiskFreeSpace)
  2. Use WriteFile to write data to the file. This write would be unbuffered and instead of going to the cache, it straightaway goes to the disk.
  3. Finally, call FlushFileBuffers to flush the metadata associated with the files.

Could someone please assist with the Win32 APIs from Perl for these 3 calls.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

荭秂 2024-10-27 17:56:10
use IO::Handle;
open(FH, "d:\\temp.txt");
FH->autoflush(1);
print FH "Some log";
close(FH);

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

如果您使用的是 unix,我会向您推荐 sync 了解更多信息让操作系统将数据提交到磁盘。

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

That will get it out to the OS ASAP, but the OS might take a while to commit it to disk. Still I'm sure you'll find this will suit your needs.

If you were on unix, I'd refer to you sync for more info on having the OS commit data to disk.

半寸时光 2024-10-27 17:56:10

这个怎么样?

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;
}

How about this?

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;
}
中性美 2024-10-27 17:56:10

您能做的最好的事情是使用 O_SYNC fcntl 标志的 sysopen ,或来自 File 的 fsync() ::同步;给你的选项确保数据不会在你的程序中缓冲,但对内核是否正在缓冲写入不做任何事情(它这样做是因为不断地将同一块刷新到磁盘会减慢所有其他I/ O)。即使那样,您也可能会失败,因为某些硬盘驱动器会对操作系统撒谎,并声称数据已提交到介质,而实际上数据仍在驱动器上的内存缓冲区中。

The best you can do is sysopen with the O_SYNC fcntl flag, or fsync() from File::Sync; the options you were given insure that data aren't buffered inside your program but do nothing about whether the kernel is buffering writes (which it does because constantly flushing the same block to disk slows down all other I/O). And even then you might lose, because some hard drives will lie to the OS and claim that data has been committed to media when it's actually still in an on-drive memory buffer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文