ReadFile Kernel32 Максимальный размер буфера

Я читаю напрямую с диска, используя С# и вызывая метод ядра 32 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());
            }

        }
    }
}

Заранее спасибо.


person trickdev    schedule 04.10.2011    source источник
comment
выложи сюда код, может проблема в пинвоке?   -  person Sergey Podobry    schedule 04.10.2011
comment
Я не думаю, что буфер ограничен. Я думаю, что если бы вы просто предоставили некоторые подробности (код, коды ошибок), мы могли бы завершить это.   -  person David Heffernan    schedule 04.10.2011
comment
Я обновил свой ответ, чтобы он соответствовал. Проблема должна быть в другом. 8Кб это ничто. Я только что сделал буфер размером 1 ГБ, без проблем! Пожалуйста, покажите, как вы вызываете ReadFile.   -  person David Heffernan    schedule 04.10.2011
comment
Какого черта ты пометил этот C++? В этом вопросе нет ничего С++.   -  person Puppy    schedule 04.10.2011
comment
@DeadMG В надежде, что некоторые опытные разработчики C ++ могут быть более привычными к вызову непосредственно в Kernel32 и распознают проблему.   -  person trickdev    schedule 04.10.2011
comment
Просто чтобы помочь любому, кто это видит - я думаю, что когда вы читаете с диска таким образом, вы должны читать целые секторы за раз ... поэтому буфер должен быть кратен размеру сектора, а ваше смещение должно быть в начале сектор. Размер сектора обычно составляет 512 байт (старые жесткие диски) или 4 КБ.   -  person J.H.    schedule 05.09.2018


Ответы (4)


Такого предела нет. Вы ошибаетесь.

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

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

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


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

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

В документации для CreateFile указано:

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

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

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

person David Heffernan    schedule 04.10.2011
comment
Я добавил некоторые детали к вопросу. Я понимаю, что я «явно» ограничен адресным пространством, но обнаруженные ошибки, похоже, указывали на то, что существует какой-то приемлемый диапазон, который был довольно мал. - person trickdev; 04.10.2011
comment
Я уверен, что проблема в чем-то другом. Как только мы увидим больше кода, все станет ясно. - person David Heffernan; 04.10.2011
comment
@trickdev Я использовал ваш код и с удовольствием прочитал 1 МБ. Я не сомневаюсь, что мог бы прочитать намного больше, чем это. Вы все еще не предоставили полный пример, и мне пришлось заполнить некоторые пробелы. Почему вы не можете предоставить полный код. Зачем усложнять мне жизнь? - person David Heffernan; 04.10.2011
comment
Я надеялся, что мне не нужно будет кормить все это с ложки, и это было то, что вы могли бы сразу заметить, но, очевидно, я ошибался. Все, что вам нужно сделать, это спросить ;) - person trickdev; 04.10.2011

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

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

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

Ресурсы

person osirisgothra    schedule 07.10.2011
comment
Добро пожаловать в SO, @osirisgothra. Я отредактировал ваше сообщение (редактирование еще не завершено). Пожалуйста, взгляните на это. - person Christoffer Lette; 07.10.2011

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

person Tony The Lion    schedule 04.10.2011
comment
Спасибо, Тони, вопрос хорошо поднят. Как я думаю, вы заметили, не совсем то, к чему я клонил, а полностью моя вина в формулировке вопроса. - person trickdev; 04.10.2011
comment
@trickdev Я знаю, что это не совсем то, что вы ищете, но я подумал, что все равно упомяну об этом. - person Tony The Lion; 04.10.2011

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

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

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

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

person Damon    schedule 04.10.2011
comment
Ничто не говорит о том, что ReadFile должен блокировать весь буфер за один раз. Поэтому, хотя то, что вы говорите, может быть правильным (я не знаю, так ли это), оно ортогонально реализации ReadFile. - person David Heffernan; 04.10.2011