NODE.JS + Windows: заглянем внутрь

в 23:12, , рубрики: javascript, node.js, метки: ,

Статья для node.js программистов, понимающих принципы асинхронно-событийного программирования, но не знающих как это устроено изнутри. Если для вас уже недостаточно стандартной картинки с «зацикленными» кружочками и хочется хотя бы взглянуть, что же у цикла событий под капотом, то вам под кат.
NODE.JS + 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 + Windows: заглянем внутрь

Инициализация

Теперь, прежде чем приступить непосредственно к нашему примеру, рассмотрим некоторые моменты инициализации node.js. При запуске создается специальная структура описывающая цикл событий, назовем ее loop. В числе прочих полей, структура содержит ссылку на порт IOCP, и счетчик асинхронных запросов. Для простоты обозначим его как целочисленную переменную req_count. При инициализации цикла происходит создание порта IOCP:

iocp _handle= CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);

NODE.JS + Windows: заглянем внутрь

Далее происходит запуск цикла событий, о котором мы поговорим позже.

Функция open.

Ну и наконец алгоритм работы самой функции open.
Во-первых, создается и инициализируется структура, описывающая асинхронный запрос, назовем ее fs_open_req. В ней сохраняется ссылка на callback onOpen, путь и модификаторы доступа к файлу и другая информация описывающая запрос. Кроме того структура содержит поле для хранения результата запроса или ссылки на него.
Во-вторых, увеличивается счетчик асинхронных запросов в структуре loop.
В-третьих, создается отдельный поток, в котором будет производиться открытие файла. При этом основной поток node.js, возвращается из функции open и затем уходит на следующую итерацию цикла событий. В порожденном потоке средствами операционной системы открывается файл 1.txt. Его дескриптор записывается в структуру fs_open_req.
В-четвертых, после открытия файла и завершения всех необходимых операций, порожденный поток вызывает PostQueuedCompletionStatus(), тем самым помещая в очередь IOCP событие об открытии файла. Притом через один из параметров PostQueuedCompletionStatus к сгенерированному событию прикрепляется ссылка на структуру fs_open_req.

NODE.JS + Windows: заглянем внутрь

Цикл событий.

На входе в цикл событий проверяется счетчик асинхронных запросов. Если зарегистрированных запросов нет, то программа завершается. Если есть, то вызывается функция GetQueuedCompletionStatus(), которая либо возвращает очередное событие, либо, если событий нет, приостанавливает работу потока до их появления.
На одной из итерации функция GetQueuedCompletionStatus вернет событие об открытии файла и вместе с ним ссылку на структуру fs_open_req. Далее node.js уменьшит счетчик асинхронных запросов и запустит callback onOpen, передав ему в качестве параметра результат открытия файла.

Заключение

Вот, собственно, и все. Хотел показать только основные принципы, так что очень многое осталось не описано. К примеру, сетевые операции ввода/вывода организованы несколько по-другому и полнее используют возможности IOCP. Но оставим это на следующий раз.

Автор: bogushevich

Источник

Поделиться