1. Как происходит связь между кодом js, c++. На самом гитхабе Nodejs можно открыть папки с функциями, которые я импортирую в коде. А также их реализации в файлах .cc расширения. Но не могли бы подробнее описать механизм, которые позволяет 2 разных языка использовать.
JS движок (например V8) предоставляет платформе (Node.js) некоторый C++ api, через который платформа может движком управлять. Например через это api можно дать движку строчку с JS кодом и попросить его выполнить, а в ответ получить указатель на результат выполнения (V8 например возвращает результат последнего выражения в коде). Так же, если у нас есть указатель на JS функцию, можно попросить движок ее выполнить с определенными аргументами и this, а в ответ получить результат этого выполнения (функция что-то вернула или бросила исключение). Ну и наконец, мы можем дать этому api указатель на C/C++ функцию, а движок сделает из нее JS функцию и вернет указатель на нее, когда JS функция вызывается движок вызовет нашу C/C++ функцию и даст ей на вход контекст выполнения, из которого можно извлечь this и аргументы функции, так же через контекст можно установить результат выполнения JS функции, указав что JS функция должна вернуть некоторое JS значение или бросить некоторое исключение.
2. V8 - движок для исполнения JS кода, то есть превращения его в машинный код для исполнения комманд. Libuv написана на с++. Они же не работают в паре? Я так понимаю, что часть кода исполняется на движке v8, а различные асинхронные вызовы, которые через Nodejs api делегируются библиотеке Libuv - уже от неё превращаются в низкоуровневый машинный код, который исполняется компьютеров?Можете чуть пролить свет на исполнения уже между этими компонентами Libuv, V8.
V8 ничего не знает о libuv, как и libuv ничего не знает о V8.
V8 занимается компиляцией/интерпретацией/выполнением JS кода, управлением JS памятью и некоторыми другими связанными с JS вещами. В V8 нет никаких event loop и чего-то подобного, он запускает JS код только когда ему об этом скажет платформа (например Node.js).
Libuv занимается асинхронными операциями. Что-то переадресует операционной системе, например сетевые сокеты можно собрать в пачку и отдать в epoll в Linux или kqueue в MacOS, заблокировав лишь один поток сразу на тысячи сокетов. А что-то просто блокируется на фоновых потоках, например обращения к файловой системе. Event loop нет и здесь, но зато есть очередь задач, которые потенциально готовы.
Ну и наконец сама платформа (Node.js) реализует event loop (по сути просто бесконечный цикл), в котором получает потенциально готовые задачи от libuv и раскладывает по своим очередям готовые, а неготовые планирует в libuv, спрашивает у V8 не хочет ли он сделать сборку мусора, ну и наконец говорит V8 вызвать некоторые JS функции.
3. После того, как Nodejs передает асинхронный вызов Libuv на исполнение. То callback функция помещается позже в стэк для исполнения, а исполнятся начинается после того, как система просигнализирует о завершении процесса, например считывания файла. Или вначале считывается файл, а потом система об этом сигнализирует и помещается callback функция в стэк Event Loop'a для исполнения?
Выше уже расписал как Node.js взаимодействует с libuv в теле event loop, так же расписал как JS код может вызвать C++ функцию. Рассмотрим на сильно упрощенном примере, что происходит когда в JS коде на Node.js мы вызываем
fs.readFile(filepath, callback)
:
1. fs.readFile - это обычная JS функция, но внутри она вызывает некоторую C++ функцию из Node.js, которая получает указатель на строку filepath и указатель на JS функцию callback, но эта C++ функция будет запущена на том же потоке где и event loop (по сути где-то внутри цикла), а блокировать нам нельзя. Поэтому она просто планирует вызов другой C++ функции на фоновых потоках и завершается, возвращая тем самым управление JS коду.
2. В какой-то момент до запланированной C++ функции дойдет очередь на выполнение на одном из фоновых потоков, она прочитает файл в память и добавит в очередь готовых задач некоторый объект, который является результатом операции чтения из файла, этот объект будет хранить в себе указатель на прочитанные данные и указатель на JS функцию callback.
3. Еще через некоторое время поток с event loop извлечет из очереди этот объект результат и скажет V8 вызвать функцию callback с данными из файла.
4. Можете более подробней рассказать про исполнение кода на инстансах приложения в зависимости от количества ядер процессора и управления потоками:
4.1 Есть к примеру приложений на Nodejs. На компьютере 4 ядра. Я на каждом ядре могу запустить инстанс этого приложения. И у каждого инстанса в pool thread'e будет по 4 потока дополнительных, которые создаются по умолчанию?
Каждый инстанс Node.js - это отдельный процесс операционной системы. У каждого процесса своя изолированная память. И да, каждый может запускать несколько потоков (у Node.js кстати 4 потока - это минимум, при большой нагрузке она может добавить в пул до 128 потоков), но в случае с Node.js большая часть потоков почти всегда что-то ждут (пока тот же файл прочитается например) и почти не тратят ресурсы CPU, а активно работает лишь один - тот в котором event loop.
4.2 Если отойти от ноды и запустить приложение на php фреймворке на Apache и этот сервер выделяет под каждый запрос свой поток.В данном случае - это такой же условно поток, который может выделяться и библиотекой Libuv или это абсолютно разные понятия? И сколько инстансов php приложения запущено, когда Apache под каждый создает свой поток? Как соотносится поток с инстансом приложения на php.
С точки зрения операционной системы - да, это такие же потоки. Разница заключается в том, что в этих потоках происходит. В php только 1 поток, который то что-то полезное делает, то ждет ответа от БД/файловой системы/еще чего-то. А в Node.js это разделено, 1 поток занят только полезной работой (выполнением JS кода) и несколько только ждут ответа.
Насчет какие там лимиты на количество инстансов php у Apache я не скажу, но они точно не бесконечные, а значит если лимит будет исчерпан, то клиенты (браузер) будут ждать пока какой-то из инстансов php не завершит свою работу полностью. А в node.js мы на одном инстансе можем обслуживать сразу множество клиентов, выполняя задачи для каждого по чуть-чуть, пока для одного клиента ждем ответ из БД можем заняться обработкой запроса от другого клиента.
4.3 Может в дополнение расскажите про потоки в системе, их ограничения и т.д
Тут целую лекцию можно читать. Думаю это стоит вынести в отдельный вопрос.