Ограничения
Описать все внутренности node.js в одной статье невозможно, поэтому придётся сразу договориться о некоторых ограничениях.
Во-первых, да простят меня поклонники *nix, все, что здесь сказано, относится только к Windows.
Во-вторых, для простоты описания, я решил построить свою статью вокруг простой операции открытия файла, т.е. функции open из модуля fs.
require('fs').open("c:\\1.txt", 'r', function onOpen(err, result){ console.log("Result: ", result); });
Естественно, из-за этих ограничений ОЧЕНЬ многое останется за пределами статьи, но придется идти на компромисс между простотой восприятия и техническими подробностями.
IOCP
Для понимания работы Node.js под Windows необходимо понимать технологию IOCP. Input/output completion port – технология, предназначенная для выполнения асинхронных операции ввода/вывода, используемая в Windows. Основным объектом в данной технологии является IOCP порт, создаваемый при помощи функции CreateIoCompletionPort().Нас в первую очередь интересует, что IOCP порт инкапсулирует очередь событий, созданную в операционной системе. Функция PostQueuedCompletionStatus() помещает событие в очередь, а функция GetQueuedCompletionStatus() извлекает. Притом, если очередь пуста, то поток вызвавший GetQueuedCompletionStatus приостанавливается, до появления первого события.
Инициализация
Теперь, прежде чем приступить непосредственно к нашему примеру, рассмотрим некоторые моменты инициализации node.js. При запуске создается специальная структура описывающая цикл событий, назовем ее loop. В числе прочих полей, структура содержит ссылку на порт IOCP, и счетчик асинхронных запросов. Для простоты обозначим его как целочисленную переменную req_count. При инициализации цикла происходит создание порта IOCP:
iocp _handle= CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
Далее происходит запуск цикла событий, о котором мы поговорим позже.
Функция open.
Ну и наконец алгоритм работы самой функции open.
Во-первых, создается и инициализируется структура, описывающая асинхронный запрос, назовем ее fs_open_req. В ней сохраняется ссылка на callback onOpen, путь и модификаторы доступа к файлу и другая информация описывающая запрос. Кроме того структура содержит поле для хранения результата запроса или ссылки на него.
Во-вторых, увеличивается счетчик асинхронных запросов в структуре loop.
В-третьих, создается отдельный поток, в котором будет производиться открытие файла. При этом основной поток node.js, возвращается из функции open и затем уходит на следующую итерацию цикла событий. В порожденном потоке средствами операционной системы открывается файл 1.txt. Его дескриптор записывается в структуру fs_open_req.
В-четвертых, после открытия файла и завершения всех необходимых операций, порожденный поток вызывает PostQueuedCompletionStatus(), тем самым помещая в очередь IOCP событие об открытии файла. Притом через один из параметров PostQueuedCompletionStatus к сгенерированному событию прикрепляется ссылка на структуру fs_open_req.
Цикл событий.
На входе в цикл событий проверяется счетчик асинхронных запросов. Если зарегистрированных запросов нет, то программа завершается. Если есть, то вызывается функция GetQueuedCompletionStatus(), которая либо возвращает очередное событие, либо, если событий нет, приостанавливает работу потока до их появления.
На одной из итерации функция GetQueuedCompletionStatus вернет событие об открытии файла и вместе с ним ссылку на структуру fs_open_req. Далее node.js уменьшит счетчик асинхронных запросов и запустит callback onOpen, передав ему в качестве параметра результат открытия файла.
Заключение
Вот, собственно, и все. Хотел показать только основные принципы, так что очень многое осталось не описано. К примеру, сетевые операции ввода/вывода организованы несколько по-другому и полнее используют возможности IOCP. Но оставим это на следующий раз.
ссылка на оригинал статьи http://habrahabr.ru/post/159201/
Добавить комментарий