Размер записи консоли через WINAPI WriteFile
и WriteConsoleW
задокументирован для неопределенного ограничения, как указано ниже:
nNumberOfCharsToWrite [in]
Количество символов для записи.Если общий размер указанного числа символов превышает доступную кучу, функция завершается ошибкой с ERROR_NOT_ENOUGH_MEMORY .
Не задокументировано, к какой «куче» это относится.Процесс может иметь несколько куч разных размеров (фиксированных или динамических).Реализация собственной кучи в библиотеке времени выполнения NT (например, RtlCreateHeap
) может создать кучу по указанному адресу, что обеспечивает удобный доступ к памяти, которая используется совместно с другими процессами.Использование общей кучи часто сочетается с портами Local Inter-Process Communication (LPC) - или с асинхронным LPC в NT 6.0+.Порты LPC используются для передачи сообщений между приложениями и системными службами, такими как диспетчер сеансов (smss.exe), диспетчер управления службами (services.exe), локальный центр безопасности (lsass.exe), сервер сеансов рабочего стола (csrss.exe).и экземпляры хоста консоли (conhost.exe).Сообщения, помещенные в очередь непосредственно на порт LPC, ограничены 256 байтами.Большие сообщения передаются путем помещения в очередь сообщений в порт, который ссылается на разделяемую память.
Оказывается, что старая реализация консоли (до NT 6.3) использует LPC в качестве канала ввода-вывода, и выше-компонентная куча составляет всего 64 КиБ .Это был своеобразный выбор дизайна.Я думаю, что кто-то слишком много выпил из подсистемы пользовательского режима - передачи сообщений Kool-Aid.Правильный ввод / вывод NT использует устройство с системными службами ввода / вывода, включая NtCreateFile
, NtReadFile
, NtWriteFile
и NtDeviceIoControlFile
.
Консольное приложение не знает, сколько из этогокуча доступна для записи.Python может начинаться с 64 КиБ и работать в обратном направлении, но его raw file I / O требует одного системного вызова на вызов.Вместо этого он записывает в 32 КиБ, что должно быть успешным.Это ограничение позволяет писать строки широких символов с 16K кодовыми точками UTF-16.Сложность заключается в том, что в стеке ввода-вывода консоли используется UTF-8 версии 3.6+, который необходимо декодировать с помощью MultiByteToWideChar
.В настоящее время он просто многократно делит буфер UTF-8 пополам, пока длина не станет меньше 16K.Таким образом, в примере вопроса написание 48 889 символов уменьшается вдвое до 24 444 символов и снова уменьшается вдвое до 12 222 символов.(IMO, было бы лучше попытаться записать до 16K кодовых точек; получить фактически записанное число и вызвать WideCharToMultiByte
на подстроке, чтобы определить количество записанных байтов UTF-8. В текущем проекте действительно есть ошибка, еслиUTF-8 2-4-байтовая последовательность перекрывает точку вырезания.)
В NT 6.3+ (Windows 8.1+) консольный ввод-вывод не имеет такого ограничения размера, поскольку он использует устройство ConDrv и I /O системные вызовы вместо LPC.Однако не стоит специально помещать код в код только для поддержки небуферизованного стека ввода-вывода текста, как настроено параметром командной строки -u
.Мы ожидаем, что интерактивный консольный ввод / вывод будет буферизован.Текстовый ввод / вывод без буферизации фактически запрещен обычным вызовом open
.Например:
>>> open('conout$', 'w', buffering=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: can't have unbuffered text I/O
Расширенная поддержка Windows 7 заканчивается 14 января 2020 года, поэтому Python 3.8 будет последней версией, которая его поддержит.Предел записи консоли должен быть удален в Python 3.9.