Итак, я только что обнаружил, что libuv - довольно небольшая библиотека по сравнению с C библиотеками go (сравните с FFmpeg). Последние 6 часов я потратил на чтение исходного кода, чтобы прочувствовать событие l oop на более глубоком уровне. Но все еще не вижу, где реализована «неблокировка». Где в кодовой базе вызывается какой-либо сигнал прерывания события или еще много чего.
Я использую Node.js уже более 8 лет, поэтому я знаком с тем, как использовать асин c неблокирующее событие l oop, но на самом деле я никогда не смотрел на реализацию.
Мой вопрос состоит из двух частей:
- Где точно означает "цикл" "происходит в libuv?
- Какие ключевые шаги в каждой итерации l oop делают его неблокирующим и asyn c.
Итак, мы начнем с примера "Привет, мир!" Все, что требуется, это:
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
int main() {
uv_loop_t *loop = malloc(sizeof(uv_loop_t));
uv_loop_init(loop); // initialize datastructures.
uv_run(loop, UV_RUN_DEFAULT); // infinite loop as long as queue is full?
uv_loop_close(loop);
free(loop);
return 0;
}
Ключевой функцией, которую я изучал, является uv_run
. Функция uv_loop_init
, по сути, инициализирует структуры данных, поэтому я не думаю, что это слишком много. Но настоящее волшебство c, кажется, происходит с uv_run
, где-то . Набор высокоуровневых фрагментов кода из репозитория libuv - в этой сущности , показывающий, что вызывает функция uv_run
.
По сути, это сводится к следующему:
while (NOT_STOPPED) {
uv__update_time(loop)
uv__run_timers(loop)
uv__run_pending(loop)
uv__run_idle(loop)
uv__run_prepare(loop)
uv__io_poll(loop, timeout)
uv__run_check(loop)
uv__run_closing_handles(loop)
// ... cleanup
}
Эти функции находятся в сущности.
uv__run_timers
: запускает обратные вызовы таймера? циклы с for (;;) {
. uv__run_pending
: выполняет регулярные обратные вызовы? проходит по очереди с while (!QUEUE_EMPTY(&pq)) {
. uv__run_idle
: без исходного кода uv__run_prepare
: без исходного кода uv__io_poll
: есть ли опрос? (не могу сказать, что это значит, хотя) Имеет 2 цикла: while (!QUEUE_EMPTY(&loop->watcher_queue)) {
и for (;;) {
,
И тогда мы закончили. И программа существует, потому что «работы» не выполняются.
Так что я думаю, что после всей этой копки я ответил на первую часть моего вопроса, и цикл специально для этих 3 функций:
uv__run_timers
uv__run_pending
uv__io_poll
Но не реализовано ничего с kqueue
или многопоточностью и сравнительно мало разбираясь с файловыми дескрипторами, я не совсем слежу за кодом. Это, вероятно, поможет другим на пути к изучению этого.
Итак, вторая часть вопроса Каковы основные шаги в этих 3 функциях, которые реализуют неблокирование ? Предполагая, что это где все циклы существуют.
Не будучи экспертом C, for (;;) {
блокирует ли событие l oop? Или это может выполняться бесконечно, и каким-то образом переходить к другим частям кода из системных событий ОС или что-то в этом роде?
Так uv__io_poll
вызывает poll(...)
в этом бесконечном l oop. Я не думаю, что неблокирует, это правильно? Похоже, это все, что он делает в основном.
Рассматривая kqueue.c
, также существует uv__io_poll
, поэтому я предполагаю, что реализация poll
является резервной и используется kqueue
на Ma c что неблокирует?
Так это что? Это просто зацикливание в uv__io_poll
и каждой итерации, которую вы можете добавить в очередь, и до тех пор, пока в очереди есть какие-то вещи, которые она будет выполнять? Я до сих пор не понимаю, как это неблокирует и асинхронно c.
Может ли кто-нибудь описать, как это асин c и неблокирует, и какие части кода взять смотреть на? По сути, я хотел бы увидеть, где в libuv существует «свободное бездействие процессора». Где когда-либо свободен процессор при звонке на наш начальный uv_run
? Если он бесплатный, как он вызывается повторно, как обработчик событий? (Как обработчик событий браузера от мыши, прерывание). Я чувствую, что ищу прерывание, но не вижу его.
Я спрашиваю об этом, потому что хочу реализовать событие MVP l oop в C, но просто не понимаю, как на самом деле неблокирование реализовано. Там, где резина встречается с дорогой.