1

C# を使用してディスクから直接読み取り、kernel32 ReadFile メソッドをピンボークしています。より大きな読み取り (現在は単一のチャンクでのみ読み取り) では、バッファー サイズが範囲外であることに気付きました。

ここで読み取りバッファの最大サイズを知っている人はいますか?

もしそうなら、読み込みたい余剰メモリがあるときにバッファサイズを制限する目的は何ですか? バッファリングと小さなメモリ フットプリントの維持の概念は理解していますが、なぜ小さいサイズが強制されるのですか? おそらく、古い Win32 API の単なる人工物でしょうか?

編集: から受け取ったエラーは、Marshal.GetLastWin32Error()「値が期待される範囲内にありません」です。

このエラーが表示されるまでの上限は 8192 バイト (8KB であるため、混乱しています)。

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace DiskRead
{
    class Program
    {
        public const uint GenericRead = 0x80000000;
        public const uint FileShareRead = 1;
        public const uint FileShareWrite = 2;
        public const uint OpenExisting = 3;

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
          uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
          uint dwFlagsAndAttributes, IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer,
           uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

        static void Main(string[] args)
        {
            string path = @"\\.\PhysicalDrive0";

            IntPtr ptr = CreateFile(path, GenericRead, FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);

            SafeFileHandle handleValue = new SafeFileHandle(ptr, true);
            FileStream fileStream = new FileStream(handleValue, FileAccess.Read);

            const uint numberOfBytesToRead = 8193;

            uint bytesRead;
            byte[] buffer = new byte[numberOfBytesToRead];


            if (!ReadFile(handleValue.DangerousGetHandle(), buffer, numberOfBytesToRead, out bytesRead, IntPtr.Zero))
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

        }
    }
}

前もって感謝します。

4

4 に答える 4

1

そのような制限はありません。あなたは間違っています。

明らかに、アドレススペースと、バッファが仮想メモリの連続ブロックであるという要件によって制限されます。32ビットシステムでは、各プロセスは2GBの仮想メモリしかアドレス指定できません。さらに、2GBの連続したメモリブロックを割り当てることはできません。

ただし、これらは一般的な制限です。APIは、ReadFile割り当て可能な限り大きなバッファーを喜んで読み込みます。

あなたは8KBの制限に達したと主張していますが、私はとを使用して1GBのファイルを正常に書き込みおよび読み取りましWriteFileReadFile。明らかにあなたはいくつかの問題を抱えていますが、それはあなたが思っていることではありません。残りのコード、特にp / invokeを呼び出すコードを表示できれば、それが明らかになると確信しています。


そして、完全なコードを投稿したので、問題が何であるかを確認できます。ファイルを読み取っていませんが、代わりに物理ディスクの読み取りを実行しています。これが「ディスクから直接読み取る」という意味だと思いますが、もう少し具体的にできたと思います。

とにかく、ここで何が起こっているのか詳細はわかりませんが、問題ReadFile自体は明らかにそうではありませんが、ハンドルがファイルではなく物理ディスクにあるという事実です。

CreateFile州の文書:

ボリュームには、1つ以上のマウントされたファイルシステムが含まれます。CreateFileで非キャッシュオプションが指定されていない場合でも、ボリュームハンドルは、特定のファイルシステムの裁量で非キャッシュとして開くことができます。すべてのMicrosoftファイルシステムがボリュームハンドルを非キャッシュとして開いていると想定する必要があります。ファイルの非キャッシュI/Oの制限は、ボリュームにも適用されます。

データがキャッシュされていない場合でも、ファイルシステムはバッファアライメントを必要とする場合と必要としない場合があります。ただし、ボリュームを開くときにnoncachedオプションが指定されている場合、ボリューム上のファイルシステムに関係なく、バッファーの整列が強制されます。すべてのファイルシステムで、ボリュームハンドルを非キャッシュとして開き、非キャッシュI/Oの制限に従うことをお勧めします。

物理ディスクから読み取る方法について、新しい質問をすることを検討する必要があると思います。

于 2011-10-04T13:29:57.217 に答える
1

これが役立つかどうかはわかりませんが、この投稿の他の読者に役立つ可能性があります。

  1. ReadFile() に関して言えば、その動作と成功は、ファイルが CreateFile() でどのように開かれたかによって大きく異なります。正しい方向へのポイントを与えるために、これについて考えてみてください。バッファーへの読み取りのために CreateFile() を呼び出す場合、データのアライメントとハードウェア固有の情報のいくつかへの大きな回り道を処理する準備ができていない限り、通常は FILE_FLAG_NO_BUFFERING を必要としません。しない方が簡単です。

  2. あなたのコードはこれを行っていないようですが、それ相互運用であるため、どこかで文字化けした値が取得される可能性があり、.NET はメモリ ブロックの痴漢 (?) で悪名高いため、相互運用を介してメモリを割り当ててみることをお勧めします。メモリブロックがどこにも移動しないことを保証します。また、相互運用性を可能な限り最小限に抑えようとしますが、最も必要なときにやや信頼性がないようです。代わりに、より適切に動作する DLL を作成することを検討してください。代わりにあなたのコードと相互運用してください..私は多くの場合これを行いましたが、それはかなりうまく機能し、同じコードを書き直す必要がないというボーナスを追加します。好きなだけ多くのプログラムで再利用するだけです...あなたの問題の根本的な原因、私はそれを自分で複製することができませんでした、私が試した最大のファイルは約800MBで、開いて、読んで、閉じてOKです...ソースを利用できるようにして、他の人がテストできるようにしてください(または非常に冗長にビルドされた実行可能ファイル) 他の人が同じ問題を抱えているかどうかを確認するには? とにかく、これが誰かを助けたことを願っています。

資力

于 2011-10-07T01:07:08.517 に答える
0

読み込むバッファの最大サイズについてはわかりませんが、バッファのサイズの理由は、関数内で内部的にチェックを行うことができるため、バッファの末尾を超えてデータが書き込まれないためです。関数に入力バッファーのサイズの概念がない場合、バッファーの末尾を超えて書き込み、バッファー オーバーフローが発生する可能性があります。これは重大なセキュリティ上の欠陥になります。

于 2011-10-04T11:08:32.153 に答える
0

の実行中ReadFile、カーネルはバッファをロックする必要があります。そうしないと、悪いことが起こる可能性があります (データをコピーしようとしたときにページが存在しないか、さらに悪いことに DMA がページにコピーされます)。

すべてのアプリケーションにはワーキング セットに制限があり、プログラムで設定することもできます。あなた (またはあなたに代わってカーネル) が、ワーキング セットの制限で許可されているよりも多くのメモリをロックしようとすると、これは失敗します。最小ワーキング セット サイズを超えてロックすることはできません。これはデフォルトでかなり小さい値 (IIRC では 16MB のようなもの) に設定されています。

「最大ワーキング セット サイズ」は、アプリケーションが使用できるメモリ量ではないことに注意してください (これは悲劇的です)。むしろ、プロセスに属するページが「他の誰かがメモリを必要とする場合にページアウトされる可能性がある」と見なされる場合の図です。

これらすべての背後にある意図は、未知の量のメモリを使用して、同時に実行されている多くのプログラムでシステムが動作し続けることを保証することです。

于 2011-10-04T13:11:04.433 に答える