ReadFile Kernel32 Максимальный размер буфера - PullRequest
1 голос
/ 04 октября 2011

Я читаю прямо с диска, используя C # и определяю метод kernelF ReadFile.Я заметил, что при больших чтениях (в настоящее время чтение только в единичных блоках) размер буфера находится вне диапазона.

Кто-нибудь знает максимальный размер буфера чтения здесь?

Если да, то какова цель ограничения размера буфера, когда у меня есть лишняя память, которую я хочу прочитать в ?Я понимаю концепции буферизации и сохранения небольшого объема памяти, но почему маленький размер навязывается нам?Возможно, это просто артефакт старого Win32 API?

РЕДАКТИРОВАТЬ: ошибка, полученная от Marshal.GetLastWin32Error(): «Значение не попадает в ожидаемый диапазон».

Верхний предел, прежде чем я получуошибка составляет 8192 байта (8 КБ - отсюда мое замешательство).

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 ]

1 голос
/ 07 октября 2011

Я не уверен, поможет ли это вам, но это может помочь другим читателям этого поста:

  1. Когда дело доходит до ReadFile (), его поведение и успех во многом зависят от того, как файл был открывается с помощью CreateFile (). Просто чтобы дать вам точку в правильном направлении, подумайте об этом; когда вы вызываете CreateFile () для чтения в буфер, вы, как правило, не захотите FILE_FLAG_NO_BUFFERING, если вы не готовы к выравниванию данных и хорошему большому обходному пути в части информации, специфичной для аппаратного обеспечения, хотя, вероятно, это будет просто проще нет.

  2. Ваш код, кажется, не делает этого, но он является взаимодействием, так что может быть получено искаженное значение, хотя где-то, и .NET печально известен за использование (?) Блоков памяти , так что вы можете попытаться выделить память через взаимодействие, чтобы гарантировать, что блок памяти никуда не переместится. Кроме того, я бы постарался свести к минимуму взаимодействие, насколько это возможно, это кажется несколько ненадежным, когда это нужно больше всего. Вместо этого вы можете подумать о том, чтобы написать DLL, которая будет воспроизводить ваш код лучше и взаимодействовать с ним вместо этого ... Я делал это во многих случаях, и он работает довольно хорошо и добавляет бонус к тому, что нет необходимости писать то же самое зашифруйте код, просто используйте его в любом количестве программ ... Что касается основной причины вашей проблемы, я сам не смог ее дублировать, самый большой файл, который я пробовал, составляет около 800 МБ, и он открылся, прочитал, закрыл, хорошо ... можете ли вы сделать доступным исходный код, чтобы его могли протестировать другие (или очень подробный исполняемый файл), чтобы выяснить, есть ли у других проблемы с ним? В любом случае надеюсь, что это кому-то помогло.

Ресурсы

1 голос
/ 04 октября 2011

Нет такого ограничения. Вы ошибаетесь.

Очевидно, что вы ограничены адресным пространством и требованием, чтобы буфер представлял собой непрерывный блок виртуальной памяти. В 32-битной системе каждый процесс может адресовать только 2 ГБ виртуальной памяти. Более того, вы не сможете выделить 2 ГБ непрерывного блока памяти.

Но это общие ограничения. ReadFile API с радостью прочитает настолько большой буфер, насколько вы можете выделить.

Вы утверждаете, что достигли лимита в 8 КБ, но я только что успешно написал и прочитал файл объемом 1 ГБ, используя WriteFile и ReadFile. Очевидно, у вас есть проблемы, но это не то, что вы думаете. Если бы вы могли показать остальную часть кода, особенно ту, которая вызывает ваш p ​​/ invoke, тогда я уверен, что это станет очевидным.


И теперь, когда вы опубликовали свой полный код, мы можем видеть, в чем проблема. Вы не читаете файл, а вместо этого выполняете чтение физического диска. Теперь я понимаю, что вы имели в виду под «чтением непосредственно с диска», но я думаю, что вы могли бы быть более конкретным!

В любом случае, я не знаю подробностей того, что здесь происходит, но проблема явно не в ReadFile как таковая, а в том, что ваш дескриптор относится к физическому диску, а не к файлу.

Документация для CreateFile гласит:

Том содержит одну или несколько смонтированных файловых систем. Дескрипторы тома можно открывать как некэшированные по усмотрению конкретной файловой системы, даже если в CreateFile не указана опция без кэширования. Следует предположить, что все файловые системы Microsoft открывают дескрипторы томов как некэшированные. Ограничения на некэшированный ввод / вывод для файлов также распространяются на тома.

Файловая система может требовать или не требовать выравнивания буфера, даже если данные не кэшированы. Однако если при открытии тома указана опция без кэширования, выравнивание буфера применяется независимо от файловой системы на томе. Во всех файловых системах рекомендуется открывать дескрипторы томов как некэшированные и следовать ограничениям ввода-вывода без кэширования.

Я думаю, вам следует задать новый вопрос о том, как читать с физических дисков.

0 голосов
/ 04 октября 2011

Пока работает ReadFile, ядро ​​должно заблокировать буфер. Это должно произойти, иначе могут произойти злые вещи (страница не может присутствовать при попытке скопировать данные или, что еще хуже ... DMA в нее).

Каждое приложение имеет ограничения на свой рабочий набор, который вы также можете настроить программно . Если вы (или ядро ​​от вашего имени) попытаетесь заблокировать больше памяти, чем разрешено ограничениями вашего рабочего набора, это не удастся. Вы не можете заблокировать больше, чем ваш минимальный размер рабочего набора, который по умолчанию имеет довольно небольшое значение (IIRC что-то вроде 16 МБ).

Обратите внимание, что «максимальный размер рабочего набора» - это не количество памяти, которое может использовать ваше приложение (это было бы трагично). Это скорее показатель того, когда страница, принадлежащая вашему процессу, считается «может быть выгружена, если кому-то еще понадобится память».

Цель всего этого состоит в том, чтобы система продолжала работать со многими программами, работающими одновременно, используя неизвестные объемы памяти.

0 голосов
/ 04 октября 2011

Не уверен насчет максимального размера буфера для чтения, однако причина для размера буфера вообще заключается в том, что внутри функции можно выполнить проверку, чтобы никакие данные не записывались после конца буфер. Если функция не имеет понятия о размере входного буфера, она может записывать после конца буфера, вызывая переполнение буфера . Это было бы серьезным недостатком безопасности.

...